mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #18988 from bfredl/uipack
refactor(ui): encode "redraw" events without intermediate allocations
This commit is contained in:
commit
e3bfc1293e
@ -6,9 +6,10 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/types.h"
|
||||
|
||||
#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL }
|
||||
#define ARRAY_DICT_INIT KV_INITIAL_VALUE
|
||||
#define STRING_INIT { .data = NULL, .size = 0 }
|
||||
#define OBJECT_INIT { .type = kObjectTypeNil }
|
||||
#define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL }
|
||||
@ -84,18 +85,10 @@ REMOTE_TYPE(Window);
|
||||
REMOTE_TYPE(Tabpage);
|
||||
|
||||
typedef struct object Object;
|
||||
|
||||
typedef struct {
|
||||
Object *items;
|
||||
size_t size, capacity;
|
||||
} Array;
|
||||
typedef kvec_t(Object) Array;
|
||||
|
||||
typedef struct key_value_pair KeyValuePair;
|
||||
|
||||
typedef struct {
|
||||
KeyValuePair *items;
|
||||
size_t size, capacity;
|
||||
} Dictionary;
|
||||
typedef kvec_t(KeyValuePair) Dictionary;
|
||||
|
||||
typedef enum {
|
||||
kObjectTypeNil = 0,
|
||||
|
@ -669,6 +669,32 @@ void api_free_string(String value)
|
||||
xfree(value.data);
|
||||
}
|
||||
|
||||
Array arena_array(Arena *arena, size_t max_size)
|
||||
{
|
||||
Array arr = ARRAY_DICT_INIT;
|
||||
kv_fixsize_arena(arena, arr, max_size);
|
||||
return arr;
|
||||
}
|
||||
|
||||
Dictionary arena_dict(Arena *arena, size_t max_size)
|
||||
{
|
||||
Dictionary dict = ARRAY_DICT_INIT;
|
||||
kv_fixsize_arena(arena, dict, max_size);
|
||||
return dict;
|
||||
}
|
||||
|
||||
String arena_string(Arena *arena, String str)
|
||||
{
|
||||
if (str.size) {
|
||||
char *mem = arena_alloc(arena, str.size + 1, false);
|
||||
memcpy(mem, str.data, str.size);
|
||||
mem[str.size] = NUL;
|
||||
return cbuf_as_string(mem, str.size);
|
||||
} else {
|
||||
return (String)STRING_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
void api_free_object(Object value)
|
||||
{
|
||||
switch (value.type) {
|
||||
|
@ -63,17 +63,31 @@
|
||||
#define PUT(dict, k, v) \
|
||||
kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))
|
||||
|
||||
#define PUT_C(dict, k, v) \
|
||||
kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v }))
|
||||
|
||||
#define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition));
|
||||
|
||||
#define ADD(array, item) \
|
||||
kv_push(array, item)
|
||||
|
||||
#define ADD_C(array, item) \
|
||||
kv_push_c(array, item)
|
||||
|
||||
#define FIXED_TEMP_ARRAY(name, fixsize) \
|
||||
Array name = ARRAY_DICT_INIT; \
|
||||
Object name##__items[fixsize]; \
|
||||
name.size = fixsize; \
|
||||
name.items = name##__items; \
|
||||
|
||||
#define MAXSIZE_TEMP_ARRAY(name, maxsize) \
|
||||
Array name = ARRAY_DICT_INIT; \
|
||||
Object name##__items[maxsize]; \
|
||||
name.capacity = maxsize; \
|
||||
name.items = name##__items; \
|
||||
|
||||
#define cbuf_as_string(d, s) ((String) { .data = d, .size = s })
|
||||
|
||||
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 })
|
||||
|
||||
/// Create a new String instance, putting data in allocated memory
|
||||
|
@ -9,11 +9,13 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/channel.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/msgpack_rpc/channel.h"
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/screen.h"
|
||||
@ -21,14 +23,32 @@
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/window.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/ui.c.generated.h"
|
||||
# include "ui_events_remote.generated.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint64_t channel_id;
|
||||
Array buffer;
|
||||
|
||||
#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data.
|
||||
/// guranteed size available for each new event (so packing of simple events
|
||||
/// and the header of grid_line will never fail)
|
||||
#define EVENT_BUF_SIZE 256
|
||||
char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data
|
||||
char *buf_wptr; ///< write head of buffer
|
||||
const char *cur_event; ///< name of current event (might get multiple arglists)
|
||||
Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!)
|
||||
|
||||
// state for write_cb, while packing a single arglist to msgpack. This
|
||||
// might fail due to buffer overflow.
|
||||
size_t pack_totlen;
|
||||
bool buf_overflow;
|
||||
char *temp_buf;
|
||||
|
||||
// We start packing the two outermost msgpack arrays before knowing the total
|
||||
// number of elements. Thus track the location where array size will need
|
||||
// to be written in the msgpack buffer, once the specifc array is finished.
|
||||
char *nevents_pos;
|
||||
char *ncalls_pos;
|
||||
uint32_t nevents; ///< number of distinct events (top-level args to "redraw"
|
||||
uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!)
|
||||
bool flushed_events; ///< events where sent to client without "flush" event
|
||||
|
||||
int hl_id; // Current highlight for legacy put event.
|
||||
Integer cursor_row, cursor_col; // Intended visible cursor position.
|
||||
@ -38,8 +58,76 @@ typedef struct {
|
||||
bool wildmenu_active;
|
||||
} UIData;
|
||||
|
||||
#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf))
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/ui.c.generated.h"
|
||||
# include "ui_events_remote.generated.h"
|
||||
#endif
|
||||
|
||||
static PMap(uint64_t) connected_uis = MAP_INIT;
|
||||
|
||||
#define mpack_w(b, byte) *(*b)++ = (char)(byte);
|
||||
static void mpack_w2(char **b, uint32_t v)
|
||||
{
|
||||
*(*b)++ = (char)((v >> 8) & 0xff);
|
||||
*(*b)++ = (char)(v & 0xff);
|
||||
}
|
||||
|
||||
static void mpack_w4(char **b, uint32_t v)
|
||||
{
|
||||
*(*b)++ = (char)((v >> 24) & 0xff);
|
||||
*(*b)++ = (char)((v >> 16) & 0xff);
|
||||
*(*b)++ = (char)((v >> 8) & 0xff);
|
||||
*(*b)++ = (char)(v & 0xff);
|
||||
}
|
||||
|
||||
static void mpack_uint(char **buf, uint32_t val)
|
||||
{
|
||||
if (val > 0xffff) {
|
||||
mpack_w(buf, 0xce);
|
||||
mpack_w4(buf, val);
|
||||
} else if (val > 0xff) {
|
||||
mpack_w(buf, 0xcd);
|
||||
mpack_w2(buf, val);
|
||||
} else if (val > 0x7f) {
|
||||
mpack_w(buf, 0xcc);
|
||||
mpack_w(buf, val);
|
||||
} else {
|
||||
mpack_w(buf, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void mpack_array(char **buf, uint32_t len)
|
||||
{
|
||||
if (len < 0x10) {
|
||||
mpack_w(buf, 0x90 | len);
|
||||
} else if (len < 0x10000) {
|
||||
mpack_w(buf, 0xdc);
|
||||
mpack_w2(buf, len);
|
||||
} else {
|
||||
mpack_w(buf, 0xdd);
|
||||
mpack_w4(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
static char *mpack_array_dyn16(char **buf)
|
||||
{
|
||||
mpack_w(buf, 0xdc);
|
||||
char *pos = *buf;
|
||||
mpack_w2(buf, 0xFFEF);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void mpack_str(char **buf, const char *str)
|
||||
{
|
||||
assert(sizeof(schar_T) - 1 < 0x20);
|
||||
size_t len = STRLEN(str);
|
||||
mpack_w(buf, 0xa0 | len);
|
||||
memcpy(*buf, str, len);
|
||||
*buf += len;
|
||||
}
|
||||
|
||||
void remote_ui_disconnect(uint64_t channel_id)
|
||||
{
|
||||
UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
|
||||
@ -47,9 +135,9 @@ void remote_ui_disconnect(uint64_t channel_id)
|
||||
return;
|
||||
}
|
||||
UIData *data = ui->data;
|
||||
api_free_array(data->buffer); // Destroy pending screen updates.
|
||||
kv_destroy(data->call_buf);
|
||||
pmap_del(uint64_t)(&connected_uis, channel_id);
|
||||
xfree(ui->data);
|
||||
xfree(data);
|
||||
ui->data = NULL; // Flag UI as "stopped".
|
||||
ui_detach_impl(ui, channel_id);
|
||||
xfree(ui);
|
||||
@ -159,10 +247,19 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
|
||||
|
||||
UIData *data = xmalloc(sizeof(UIData));
|
||||
data->channel_id = channel_id;
|
||||
data->buffer = (Array)ARRAY_DICT_INIT;
|
||||
data->cur_event = NULL;
|
||||
data->hl_id = 0;
|
||||
data->client_col = -1;
|
||||
data->nevents_pos = NULL;
|
||||
data->nevents = 0;
|
||||
data->flushed_events = false;
|
||||
data->ncalls_pos = NULL;
|
||||
data->ncalls = 0;
|
||||
data->buf_wptr = data->buf;
|
||||
data->temp_buf = NULL;
|
||||
data->wildmenu_active = false;
|
||||
data->call_buf = (Array)ARRAY_DICT_INIT;
|
||||
kv_ensure_space(data->call_buf, 16);
|
||||
ui->data = data;
|
||||
|
||||
pmap_put(uint64_t)(&connected_uis, channel_id, ui);
|
||||
@ -433,34 +530,128 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
|
||||
ui->pum_pos = true;
|
||||
}
|
||||
|
||||
static void flush_event(UIData *data)
|
||||
{
|
||||
if (data->cur_event) {
|
||||
mpack_w2(&data->ncalls_pos, data->ncalls);
|
||||
data->cur_event = NULL;
|
||||
}
|
||||
if (!data->nevents_pos) {
|
||||
assert(BUF_POS(data) == 0);
|
||||
char **buf = &data->buf_wptr;
|
||||
// [2, "redraw", [...]]
|
||||
mpack_array(buf, 3);
|
||||
mpack_uint(buf, 2);
|
||||
mpack_str(buf, "redraw");
|
||||
data->nevents_pos = mpack_array_dyn16(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int write_cb(void *vdata, const char *buf, size_t len)
|
||||
{
|
||||
UIData *data = (UIData *)vdata;
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->pack_totlen += len;
|
||||
if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) {
|
||||
data->buf_overflow = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(data->buf_wptr, buf, len);
|
||||
data->buf_wptr += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool prepare_call(UI *ui, const char *name)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
|
||||
if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) {
|
||||
remote_ui_flush_buf(ui);
|
||||
}
|
||||
|
||||
// To optimize data transfer(especially for "grid_line"), we bundle adjacent
|
||||
// calls to same method together, so only add a new call entry if the last
|
||||
// method call is different from "name"
|
||||
|
||||
if (!data->cur_event || !strequal(data->cur_event, name)) {
|
||||
flush_event(data);
|
||||
data->cur_event = name;
|
||||
char **buf = &data->buf_wptr;
|
||||
data->ncalls_pos = mpack_array_dyn16(buf);
|
||||
mpack_str(buf, name);
|
||||
data->nevents++;
|
||||
data->ncalls = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
|
||||
static void push_call(UI *ui, const char *name, Array args)
|
||||
{
|
||||
Array call = ARRAY_DICT_INIT;
|
||||
UIData *data = ui->data;
|
||||
bool pending = data->nevents_pos;
|
||||
char *buf_pos_save = data->buf_wptr;
|
||||
|
||||
// To optimize data transfer(especially for "put"), we bundle adjacent
|
||||
// calls to same method together, so only add a new call entry if the last
|
||||
// method call is different from "name"
|
||||
if (kv_size(data->buffer)) {
|
||||
call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array;
|
||||
bool new_event = prepare_call(ui, name);
|
||||
|
||||
msgpack_packer pac;
|
||||
data->pack_totlen = 0;
|
||||
data->buf_overflow = false;
|
||||
msgpack_packer_init(&pac, data, write_cb);
|
||||
msgpack_rpc_from_array(args, &pac);
|
||||
if (data->buf_overflow) {
|
||||
data->buf_wptr = buf_pos_save;
|
||||
if (new_event) {
|
||||
data->cur_event = NULL;
|
||||
data->nevents--;
|
||||
}
|
||||
if (pending) {
|
||||
remote_ui_flush_buf(ui);
|
||||
}
|
||||
|
||||
if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) {
|
||||
call = (Array)ARRAY_DICT_INIT;
|
||||
ADD(data->buffer, ARRAY_OBJ(call));
|
||||
ADD(call, STRING_OBJ(cstr_to_string(name)));
|
||||
if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) {
|
||||
// TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set)
|
||||
data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen);
|
||||
data->buf_wptr = data->temp_buf;
|
||||
char **buf = &data->buf_wptr;
|
||||
mpack_array(buf, 3);
|
||||
mpack_uint(buf, 2);
|
||||
mpack_str(buf, "redraw");
|
||||
mpack_array(buf, 1);
|
||||
mpack_array(buf, 2);
|
||||
mpack_str(buf, name);
|
||||
} else {
|
||||
prepare_call(ui, name);
|
||||
}
|
||||
data->pack_totlen = 0;
|
||||
data->buf_overflow = false;
|
||||
msgpack_rpc_from_array(args, &pac);
|
||||
|
||||
ADD(call, ARRAY_OBJ(args));
|
||||
kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call;
|
||||
if (data->temp_buf) {
|
||||
size_t size = (size_t)(data->buf_wptr - data->temp_buf);
|
||||
WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree);
|
||||
rpc_write_raw(data->channel_id, buf);
|
||||
data->temp_buf = NULL;
|
||||
data->buf_wptr = data->buf;
|
||||
data->nevents_pos = NULL;
|
||||
}
|
||||
}
|
||||
data->ncalls++;
|
||||
}
|
||||
|
||||
static void remote_ui_grid_clear(UI *ui, Integer grid)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
if (ui->ui_ext[kUILinegrid]) {
|
||||
ADD(args, INTEGER_OBJ(grid));
|
||||
ADD_C(args, INTEGER_OBJ(grid));
|
||||
}
|
||||
const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear";
|
||||
push_call(ui, name, args);
|
||||
@ -468,12 +659,13 @@ static void remote_ui_grid_clear(UI *ui, Integer grid)
|
||||
|
||||
static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
if (ui->ui_ext[kUILinegrid]) {
|
||||
ADD(args, INTEGER_OBJ(grid));
|
||||
ADD_C(args, INTEGER_OBJ(grid));
|
||||
}
|
||||
ADD(args, INTEGER_OBJ(width));
|
||||
ADD(args, INTEGER_OBJ(height));
|
||||
ADD_C(args, INTEGER_OBJ(width));
|
||||
ADD_C(args, INTEGER_OBJ(height));
|
||||
const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize";
|
||||
push_call(ui, name, args);
|
||||
}
|
||||
@ -481,35 +673,36 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h
|
||||
static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left,
|
||||
Integer right, Integer rows, Integer cols)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
if (ui->ui_ext[kUILinegrid]) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(grid));
|
||||
ADD(args, INTEGER_OBJ(top));
|
||||
ADD(args, INTEGER_OBJ(bot));
|
||||
ADD(args, INTEGER_OBJ(left));
|
||||
ADD(args, INTEGER_OBJ(right));
|
||||
ADD(args, INTEGER_OBJ(rows));
|
||||
ADD(args, INTEGER_OBJ(cols));
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(grid));
|
||||
ADD_C(args, INTEGER_OBJ(top));
|
||||
ADD_C(args, INTEGER_OBJ(bot));
|
||||
ADD_C(args, INTEGER_OBJ(left));
|
||||
ADD_C(args, INTEGER_OBJ(right));
|
||||
ADD_C(args, INTEGER_OBJ(rows));
|
||||
ADD_C(args, INTEGER_OBJ(cols));
|
||||
push_call(ui, "grid_scroll", args);
|
||||
} else {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(top));
|
||||
ADD(args, INTEGER_OBJ(bot - 1));
|
||||
ADD(args, INTEGER_OBJ(left));
|
||||
ADD(args, INTEGER_OBJ(right - 1));
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(top));
|
||||
ADD_C(args, INTEGER_OBJ(bot - 1));
|
||||
ADD_C(args, INTEGER_OBJ(left));
|
||||
ADD_C(args, INTEGER_OBJ(right - 1));
|
||||
push_call(ui, "set_scroll_region", args);
|
||||
|
||||
args = (Array)ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(rows));
|
||||
args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(rows));
|
||||
push_call(ui, "scroll", args);
|
||||
|
||||
// some clients have "clear" being affected by scroll region,
|
||||
// so reset it.
|
||||
args = (Array)ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(0));
|
||||
ADD(args, INTEGER_OBJ(ui->height - 1));
|
||||
ADD(args, INTEGER_OBJ(0));
|
||||
ADD(args, INTEGER_OBJ(ui->width - 1));
|
||||
args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(0));
|
||||
ADD_C(args, INTEGER_OBJ(ui->height - 1));
|
||||
ADD_C(args, INTEGER_OBJ(0));
|
||||
ADD_C(args, INTEGER_OBJ(ui->width - 1));
|
||||
push_call(ui, "set_scroll_region", args);
|
||||
}
|
||||
}
|
||||
@ -520,26 +713,27 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
|
||||
if (!ui->ui_ext[kUITermColors]) {
|
||||
HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp);
|
||||
}
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(rgb_fg));
|
||||
ADD(args, INTEGER_OBJ(rgb_bg));
|
||||
ADD(args, INTEGER_OBJ(rgb_sp));
|
||||
ADD(args, INTEGER_OBJ(cterm_fg));
|
||||
ADD(args, INTEGER_OBJ(cterm_bg));
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(rgb_fg));
|
||||
ADD_C(args, INTEGER_OBJ(rgb_bg));
|
||||
ADD_C(args, INTEGER_OBJ(rgb_sp));
|
||||
ADD_C(args, INTEGER_OBJ(cterm_fg));
|
||||
ADD_C(args, INTEGER_OBJ(cterm_bg));
|
||||
push_call(ui, "default_colors_set", args);
|
||||
|
||||
// Deprecated
|
||||
if (!ui->ui_ext[kUILinegrid]) {
|
||||
args = (Array)ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
|
||||
args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
|
||||
push_call(ui, "update_fg", args);
|
||||
|
||||
args = (Array)ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
|
||||
args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
|
||||
push_call(ui, "update_bg", args);
|
||||
|
||||
args = (Array)ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
|
||||
args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
|
||||
push_call(ui, "update_sp", args);
|
||||
}
|
||||
}
|
||||
@ -550,25 +744,29 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt
|
||||
if (!ui->ui_ext[kUILinegrid]) {
|
||||
return;
|
||||
}
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
|
||||
ADD(args, INTEGER_OBJ(id));
|
||||
ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true)));
|
||||
ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false)));
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(id));
|
||||
ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true)));
|
||||
ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false)));
|
||||
|
||||
if (ui->ui_ext[kUIHlState]) {
|
||||
ADD(args, ARRAY_OBJ(copy_array(info)));
|
||||
ADD_C(args, ARRAY_OBJ(info));
|
||||
} else {
|
||||
ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT));
|
||||
ADD_C(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT));
|
||||
}
|
||||
|
||||
push_call(ui, "hl_attr_define", args);
|
||||
// TODO(bfredl): could be elided
|
||||
api_free_dictionary(kv_A(args, 1).data.dictionary);
|
||||
api_free_dictionary(kv_A(args, 2).data.dictionary);
|
||||
}
|
||||
|
||||
static void remote_ui_highlight_set(UI *ui, int id)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
|
||||
if (data->hl_id == id) {
|
||||
return;
|
||||
@ -576,18 +774,20 @@ static void remote_ui_highlight_set(UI *ui, int id)
|
||||
data->hl_id = id;
|
||||
Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb);
|
||||
|
||||
ADD(args, DICTIONARY_OBJ(hl));
|
||||
ADD_C(args, DICTIONARY_OBJ(hl));
|
||||
push_call(ui, "highlight_set", args);
|
||||
api_free_dictionary(kv_A(args, 0).data.dictionary);
|
||||
}
|
||||
|
||||
/// "true" cursor used only for input focus
|
||||
static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
|
||||
{
|
||||
if (ui->ui_ext[kUILinegrid]) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(grid));
|
||||
ADD(args, INTEGER_OBJ(row));
|
||||
ADD(args, INTEGER_OBJ(col));
|
||||
UIData *data = ui->data;
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(grid));
|
||||
ADD_C(args, INTEGER_OBJ(row));
|
||||
ADD_C(args, INTEGER_OBJ(col));
|
||||
push_call(ui, "grid_cursor_goto", args);
|
||||
} else {
|
||||
UIData *data = ui->data;
|
||||
@ -606,9 +806,9 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
|
||||
}
|
||||
data->client_row = row;
|
||||
data->client_col = col;
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(row));
|
||||
ADD(args, INTEGER_OBJ(col));
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, INTEGER_OBJ(row));
|
||||
ADD_C(args, INTEGER_OBJ(col));
|
||||
push_call(ui, "cursor_goto", args);
|
||||
}
|
||||
|
||||
@ -616,8 +816,8 @@ static void remote_ui_put(UI *ui, const char *cell)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
data->client_col++;
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, STRING_OBJ(cstr_to_string(cell)));
|
||||
Array args = data->call_buf;
|
||||
ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell)));
|
||||
push_call(ui, "put", args);
|
||||
}
|
||||
|
||||
@ -627,41 +827,64 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
if (ui->ui_ext[kUILinegrid]) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(grid));
|
||||
ADD(args, INTEGER_OBJ(row));
|
||||
ADD(args, INTEGER_OBJ(startcol));
|
||||
Array cells = ARRAY_DICT_INIT;
|
||||
int repeat = 0;
|
||||
prepare_call(ui, "grid_line");
|
||||
data->ncalls++;
|
||||
|
||||
char **buf = &data->buf_wptr;
|
||||
mpack_array(buf, 4);
|
||||
mpack_uint(buf, (uint32_t)grid);
|
||||
mpack_uint(buf, (uint32_t)row);
|
||||
mpack_uint(buf, (uint32_t)startcol);
|
||||
char *lenpos = mpack_array_dyn16(buf);
|
||||
|
||||
uint32_t repeat = 0;
|
||||
size_t ncells = (size_t)(endcol - startcol);
|
||||
int last_hl = -1;
|
||||
uint32_t nelem = 0;
|
||||
for (size_t i = 0; i < ncells; i++) {
|
||||
repeat++;
|
||||
if (i == ncells - 1 || attrs[i] != attrs[i + 1]
|
||||
|| STRCMP(chunk[i], chunk[i + 1])) {
|
||||
Array cell = ARRAY_DICT_INIT;
|
||||
ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i])));
|
||||
if (attrs[i] != last_hl || repeat > 1) {
|
||||
ADD(cell, INTEGER_OBJ(attrs[i]));
|
||||
if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) {
|
||||
// close to overflowing the redraw buffer. finish this event,
|
||||
// flush, and start a new "grid_line" event at the current position.
|
||||
// For simplicity leave place for the final "clear" element
|
||||
// as well, hence the factor of 2 in the check.
|
||||
mpack_w2(&lenpos, nelem);
|
||||
remote_ui_flush_buf(ui);
|
||||
|
||||
prepare_call(ui, "grid_line");
|
||||
data->ncalls++;
|
||||
mpack_array(buf, 4);
|
||||
mpack_uint(buf, (uint32_t)grid);
|
||||
mpack_uint(buf, (uint32_t)row);
|
||||
mpack_uint(buf, (uint32_t)startcol + (uint32_t)i - repeat + 1);
|
||||
lenpos = mpack_array_dyn16(buf);
|
||||
nelem = 0;
|
||||
last_hl = -1;
|
||||
}
|
||||
uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1);
|
||||
nelem++;
|
||||
mpack_array(buf, csize);
|
||||
mpack_str(buf, (const char *)chunk[i]);
|
||||
if (csize >= 2) {
|
||||
mpack_uint(buf, (uint32_t)attrs[i]);
|
||||
if (csize >= 3) {
|
||||
mpack_uint(buf, repeat);
|
||||
}
|
||||
}
|
||||
last_hl = attrs[i];
|
||||
}
|
||||
if (repeat > 1) {
|
||||
ADD(cell, INTEGER_OBJ(repeat));
|
||||
}
|
||||
ADD(cells, ARRAY_OBJ(cell));
|
||||
repeat = 0;
|
||||
}
|
||||
}
|
||||
if (endcol < clearcol) {
|
||||
Array cell = ARRAY_DICT_INIT;
|
||||
ADD(cell, STRING_OBJ(cstr_to_string(" ")));
|
||||
ADD(cell, INTEGER_OBJ(clearattr));
|
||||
ADD(cell, INTEGER_OBJ(clearcol - endcol));
|
||||
ADD(cells, ARRAY_OBJ(cell));
|
||||
nelem++;
|
||||
mpack_array(buf, 3);
|
||||
mpack_str(buf, " ");
|
||||
mpack_uint(buf, (uint32_t)clearattr);
|
||||
mpack_uint(buf, (uint32_t)(clearcol - endcol));
|
||||
}
|
||||
ADD(args, ARRAY_OBJ(cells));
|
||||
|
||||
push_call(ui, "grid_line", args);
|
||||
mpack_w2(&lenpos, nelem);
|
||||
} else {
|
||||
for (int i = 0; i < endcol - startcol; i++) {
|
||||
remote_ui_cursor_goto(ui, row, startcol + i);
|
||||
@ -688,16 +911,47 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush the internal packing buffer to the client.
|
||||
///
|
||||
/// This might happen multiple times before the actual ui_flush, if the
|
||||
/// total redraw size is large!
|
||||
static void remote_ui_flush_buf(UI *ui)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
if (!data->nevents_pos) {
|
||||
return;
|
||||
}
|
||||
if (data->cur_event) {
|
||||
flush_event(data);
|
||||
}
|
||||
mpack_w2(&data->nevents_pos, data->nevents);
|
||||
data->nevents = 0;
|
||||
data->nevents_pos = NULL;
|
||||
|
||||
// TODO(bfredl): elide copy by a length one free-list like the arena
|
||||
size_t size = BUF_POS(data);
|
||||
WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree);
|
||||
rpc_write_raw(data->channel_id, buf);
|
||||
data->buf_wptr = data->buf;
|
||||
// we have sent events to the client, but possibly not yet the final "flush"
|
||||
// event.
|
||||
data->flushed_events = true;
|
||||
}
|
||||
|
||||
/// An intentional flush (vsync) when Nvim is finished redrawing the screen
|
||||
///
|
||||
/// Clients can know this happened by a final "flush" event at the end of the
|
||||
/// "redraw" batch.
|
||||
static void remote_ui_flush(UI *ui)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
if (data->buffer.size > 0) {
|
||||
if (data->nevents > 0 || data->flushed_events) {
|
||||
if (!ui->ui_ext[kUILinegrid]) {
|
||||
remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
|
||||
}
|
||||
push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
|
||||
rpc_send_event(data->channel_id, "redraw", data->buffer);
|
||||
data->buffer = (Array)ARRAY_DICT_INIT;
|
||||
remote_ui_flush_buf(ui);
|
||||
data->flushed_events = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -732,7 +986,7 @@ static Array translate_firstarg(UI *ui, Array args)
|
||||
return new_args;
|
||||
}
|
||||
|
||||
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||
static void remote_ui_event(UI *ui, char *name, Array args)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
if (!ui->ui_ext[kUILinegrid]) {
|
||||
@ -741,21 +995,24 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||
if (strequal(name, "cmdline_show")) {
|
||||
Array new_args = translate_firstarg(ui, args);
|
||||
push_call(ui, name, new_args);
|
||||
api_free_array(new_args);
|
||||
return;
|
||||
} else if (strequal(name, "cmdline_block_show")) {
|
||||
Array new_args = ARRAY_DICT_INIT;
|
||||
Array new_args = data->call_buf;
|
||||
Array block = args.items[0].data.array;
|
||||
Array new_block = ARRAY_DICT_INIT;
|
||||
for (size_t i = 0; i < block.size; i++) {
|
||||
ADD(new_block,
|
||||
ARRAY_OBJ(translate_contents(ui, block.items[i].data.array)));
|
||||
}
|
||||
ADD(new_args, ARRAY_OBJ(new_block));
|
||||
ADD_C(new_args, ARRAY_OBJ(new_block));
|
||||
push_call(ui, name, new_args);
|
||||
api_free_array(new_block);
|
||||
return;
|
||||
} else if (strequal(name, "cmdline_block_append")) {
|
||||
Array new_args = translate_firstarg(ui, args);
|
||||
push_call(ui, name, new_args);
|
||||
api_free_array(new_args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -766,18 +1023,19 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||
data->wildmenu_active = (args.items[4].data.integer == -1)
|
||||
|| !ui->ui_ext[kUIPopupmenu];
|
||||
if (data->wildmenu_active) {
|
||||
Array new_args = ARRAY_DICT_INIT;
|
||||
Array new_args = data->call_buf;
|
||||
Array items = args.items[0].data.array;
|
||||
Array new_items = ARRAY_DICT_INIT;
|
||||
for (size_t i = 0; i < items.size; i++) {
|
||||
ADD(new_items, copy_object(items.items[i].data.array.items[0]));
|
||||
}
|
||||
ADD(new_args, ARRAY_OBJ(new_items));
|
||||
ADD_C(new_args, ARRAY_OBJ(new_items));
|
||||
push_call(ui, "wildmenu_show", new_args);
|
||||
api_free_array(new_items);
|
||||
if (args.items[1].data.integer != -1) {
|
||||
Array new_args2 = ARRAY_DICT_INIT;
|
||||
ADD(new_args2, args.items[1]);
|
||||
push_call(ui, "wildmenu_select", new_args);
|
||||
Array new_args2 = data->call_buf;
|
||||
ADD_C(new_args2, args.items[1]);
|
||||
push_call(ui, "wildmenu_select", new_args2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -792,18 +1050,7 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||
}
|
||||
}
|
||||
|
||||
Array my_args = ARRAY_DICT_INIT;
|
||||
// Objects are currently single-reference
|
||||
// make a copy, but only if necessary
|
||||
if (*args_consumed) {
|
||||
for (size_t i = 0; i < args.size; i++) {
|
||||
ADD(my_args, copy_object(args.items[i]));
|
||||
}
|
||||
} else {
|
||||
my_args = args;
|
||||
*args_consumed = true;
|
||||
}
|
||||
push_call(ui, name, my_args);
|
||||
push_call(ui, name, args);
|
||||
}
|
||||
|
||||
static void remote_ui_inspect(UI *ui, Dictionary *info)
|
||||
|
@ -99,7 +99,7 @@ void raw_line(Integer grid, Integer row, Integer startcol,
|
||||
LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
|
||||
FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
|
||||
|
||||
void event(char *name, Array args, bool *args_consumed)
|
||||
void event(char *name, Array args)
|
||||
FUNC_API_NOEXPORT;
|
||||
|
||||
void win_pos(Integer grid, Window win, Integer startrow,
|
||||
|
@ -7272,7 +7272,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
|
||||
|
||||
redraw_tabline = true;
|
||||
if (h != tabline_height()) {
|
||||
shell_new_rows();
|
||||
win_new_screen_rows();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3309,45 +3309,53 @@ draw_cmdline_no_arabicshape:
|
||||
|
||||
static void ui_ext_cmdline_show(CmdlineInfo *line)
|
||||
{
|
||||
Arena arena = ARENA_EMPTY;
|
||||
arena_start(&arena, &ui_ext_fixblk);
|
||||
Array content = ARRAY_DICT_INIT;
|
||||
if (cmdline_star) {
|
||||
content = arena_array(&arena, 1);
|
||||
size_t len = 0;
|
||||
for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
|
||||
len++;
|
||||
}
|
||||
char *buf = xmallocz(len);
|
||||
char *buf = arena_alloc(&arena, len, false);
|
||||
memset(buf, '*', len);
|
||||
Array item = ARRAY_DICT_INIT;
|
||||
ADD(item, INTEGER_OBJ(0));
|
||||
ADD(item, STRING_OBJ(((String) { .data = buf, .size = len })));
|
||||
ADD(content, ARRAY_OBJ(item));
|
||||
Array item = arena_array(&arena, 2);
|
||||
ADD_C(item, INTEGER_OBJ(0));
|
||||
ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len)));
|
||||
ADD_C(content, ARRAY_OBJ(item));
|
||||
} else if (kv_size(line->last_colors.colors)) {
|
||||
content = arena_array(&arena, kv_size(line->last_colors.colors));
|
||||
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
|
||||
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
|
||||
Array item = ARRAY_DICT_INIT;
|
||||
ADD(item, INTEGER_OBJ(chunk.attr));
|
||||
Array item = arena_array(&arena, 2);
|
||||
ADD_C(item, INTEGER_OBJ(chunk.attr));
|
||||
|
||||
assert(chunk.end >= chunk.start);
|
||||
ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start,
|
||||
ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start,
|
||||
(size_t)(chunk.end - chunk.start))));
|
||||
ADD(content, ARRAY_OBJ(item));
|
||||
ADD_C(content, ARRAY_OBJ(item));
|
||||
}
|
||||
} else {
|
||||
Array item = ARRAY_DICT_INIT;
|
||||
ADD(item, INTEGER_OBJ(0));
|
||||
ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff))));
|
||||
ADD(content, ARRAY_OBJ(item));
|
||||
Array item = arena_array(&arena, 2);
|
||||
ADD_C(item, INTEGER_OBJ(0));
|
||||
ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff))));
|
||||
content = arena_array(&arena, 1);
|
||||
ADD_C(content, ARRAY_OBJ(item));
|
||||
}
|
||||
char charbuf[2] = { (char)line->cmdfirstc, 0 };
|
||||
ui_call_cmdline_show(content, line->cmdpos,
|
||||
cchar_to_string((char)line->cmdfirstc),
|
||||
cstr_to_string((char *)(line->cmdprompt)),
|
||||
cstr_as_string(charbuf),
|
||||
cstr_as_string((char *)(line->cmdprompt)),
|
||||
line->cmdindent,
|
||||
line->level);
|
||||
if (line->special_char) {
|
||||
ui_call_cmdline_special_char(cchar_to_string(line->special_char),
|
||||
charbuf[0] = line->special_char;
|
||||
ui_call_cmdline_special_char(cstr_as_string(charbuf),
|
||||
line->special_shift,
|
||||
line->level);
|
||||
}
|
||||
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
|
||||
}
|
||||
|
||||
void ui_ext_cmdline_block_append(size_t indent, const char *line)
|
||||
@ -3363,9 +3371,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line)
|
||||
ADD(content, ARRAY_OBJ(item));
|
||||
ADD(cmdline_block, ARRAY_OBJ(content));
|
||||
if (cmdline_block.size > 1) {
|
||||
ui_call_cmdline_block_append(copy_array(content));
|
||||
ui_call_cmdline_block_append(content);
|
||||
} else {
|
||||
ui_call_cmdline_block_show(copy_array(cmdline_block));
|
||||
ui_call_cmdline_block_show(cmdline_block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3385,7 +3393,7 @@ void cmdline_screen_cleared(void)
|
||||
}
|
||||
|
||||
if (cmdline_block.size) {
|
||||
ui_call_cmdline_block_show(copy_array(cmdline_block));
|
||||
ui_call_cmdline_block_show(cmdline_block);
|
||||
}
|
||||
|
||||
int prev_level = ccline.level - 1;
|
||||
@ -3442,7 +3450,8 @@ void putcmdline(char c, int shift)
|
||||
}
|
||||
msg_no_more = false;
|
||||
} else if (ccline.redraw_state != kCmdRedrawAll) {
|
||||
ui_call_cmdline_special_char(cchar_to_string(c), shift,
|
||||
char charbuf[2] = { c, 0 };
|
||||
ui_call_cmdline_special_char(cstr_as_string(charbuf), shift,
|
||||
ccline.level);
|
||||
}
|
||||
cursorcmd();
|
||||
|
@ -36,20 +36,12 @@ local function write_signature(output, ev, prefix, notype)
|
||||
output:write(')')
|
||||
end
|
||||
|
||||
local function write_arglist(output, ev, need_copy)
|
||||
output:write(' Array args = ARRAY_DICT_INIT;\n')
|
||||
local function write_arglist(output, ev)
|
||||
for j = 1, #ev.parameters do
|
||||
local param = ev.parameters[j]
|
||||
local kind = string.upper(param[1])
|
||||
local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT")
|
||||
output:write(' ADD(args, ')
|
||||
if do_copy then
|
||||
output:write('copy_object(')
|
||||
end
|
||||
output:write(' ADD_C(args, ')
|
||||
output:write(kind..'_OBJ('..param[2]..')')
|
||||
if do_copy then
|
||||
output:write(')')
|
||||
end
|
||||
output:write(');\n')
|
||||
end
|
||||
end
|
||||
@ -119,7 +111,9 @@ for i = 1, #events do
|
||||
remote_output:write('static void remote_ui_'..ev.name)
|
||||
write_signature(remote_output, ev, 'UI *ui')
|
||||
remote_output:write('\n{\n')
|
||||
write_arglist(remote_output, ev, true)
|
||||
remote_output:write(' UIData *data = ui->data;\n')
|
||||
remote_output:write(' Array args = data->call_buf;\n')
|
||||
write_arglist(remote_output, ev)
|
||||
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
|
||||
remote_output:write('}\n\n')
|
||||
end
|
||||
@ -186,9 +180,10 @@ for i = 1, #events do
|
||||
write_signature(call_output, ev, '')
|
||||
call_output:write('\n{\n')
|
||||
if ev.remote_only then
|
||||
write_arglist(call_output, ev, false)
|
||||
call_output:write(' Array args = call_buf;\n')
|
||||
write_arglist(call_output, ev)
|
||||
call_output:write(' UI_LOG('..ev.name..');\n')
|
||||
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
||||
call_output:write(' ui_call_event("'..ev.name..'", args);\n')
|
||||
elseif ev.compositor_impl then
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
|
||||
|
@ -121,6 +121,9 @@
|
||||
#define kv_push(v, x) \
|
||||
(*kv_pushp(v) = (x))
|
||||
|
||||
#define kv_pushp_c(v) ((v).items + ((v).size++))
|
||||
#define kv_push_c(v, x) (*kv_pushp_c(v) = (x))
|
||||
|
||||
#define kv_a(v, i) \
|
||||
(*(((v).capacity <= (size_t)(i) \
|
||||
? ((v).capacity = (v).size = (i) + 1, \
|
||||
|
@ -786,6 +786,7 @@ void free_all_mem(void)
|
||||
decor_free_all_mem();
|
||||
|
||||
nlua_free_all_mem();
|
||||
ui_free_all_mem();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -51,6 +51,10 @@ typedef struct {
|
||||
// inits an empty arena. use arena_start() to actually allocate space!
|
||||
#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
|
||||
|
||||
#define kv_fixsize_arena(a, v, s) \
|
||||
((v).capacity = (s), \
|
||||
(v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true))
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "memory.h.generated.h"
|
||||
#endif
|
||||
|
@ -1068,6 +1068,7 @@ void ex_messages(void *const eap_p)
|
||||
}
|
||||
}
|
||||
ui_call_msg_history_show(entries);
|
||||
api_free_array(entries);
|
||||
msg_ext_history_visible = true;
|
||||
wait_return(false);
|
||||
} else {
|
||||
@ -1262,11 +1263,11 @@ void wait_return(int redraw)
|
||||
msg_ext_keep_after_cmdline = true;
|
||||
}
|
||||
|
||||
// If the window size changed set_shellsize() will redraw the screen.
|
||||
// If the screen size changed screen_resize() will redraw the screen.
|
||||
// Otherwise the screen is only redrawn if 'redraw' is set and no ':'
|
||||
// typed.
|
||||
tmpState = State;
|
||||
State = oldState; // restore State before set_shellsize
|
||||
State = oldState; // restore State before screen_resize()
|
||||
setmouse();
|
||||
msg_check();
|
||||
need_wait_return = false;
|
||||
@ -3136,12 +3137,13 @@ void msg_ext_ui_flush(void)
|
||||
|
||||
msg_ext_emit_chunk();
|
||||
if (msg_ext_chunks.size > 0) {
|
||||
ui_call_msg_show(cstr_to_string(msg_ext_kind),
|
||||
ui_call_msg_show(cstr_as_string((char *)msg_ext_kind),
|
||||
msg_ext_chunks, msg_ext_overwrite);
|
||||
if (!msg_ext_overwrite) {
|
||||
msg_ext_visible++;
|
||||
}
|
||||
msg_ext_kind = NULL;
|
||||
api_free_array(msg_ext_chunks);
|
||||
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
|
||||
msg_ext_cur_len = 0;
|
||||
msg_ext_overwrite = false;
|
||||
@ -3155,6 +3157,7 @@ void msg_ext_flush_showmode(void)
|
||||
if (ui_has(kUIMessages)) {
|
||||
msg_ext_emit_chunk();
|
||||
ui_call_msg_showmode(msg_ext_chunks);
|
||||
api_free_array(msg_ext_chunks);
|
||||
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
|
||||
msg_ext_cur_len = 0;
|
||||
}
|
||||
|
@ -360,6 +360,17 @@ free_ret:
|
||||
api_clear_error(&error);
|
||||
}
|
||||
|
||||
bool rpc_write_raw(uint64_t id, WBuffer *buffer)
|
||||
{
|
||||
Channel *channel = find_rpc_channel(id);
|
||||
if (!channel) {
|
||||
wstream_release_wbuffer(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return channel_write(channel, buffer);
|
||||
}
|
||||
|
||||
static bool channel_write(Channel *channel, WBuffer *buffer)
|
||||
{
|
||||
bool success;
|
||||
|
@ -11,10 +11,6 @@
|
||||
# include "msgpack_rpc/unpacker.c.generated.h"
|
||||
#endif
|
||||
|
||||
#define kv_fixsize_arena(a, v, s) \
|
||||
((v).capacity = (s), \
|
||||
(v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true))
|
||||
|
||||
Object unpack(const char *data, size_t size, Error *err)
|
||||
{
|
||||
Unpacker unpacker;
|
||||
|
@ -2755,13 +2755,13 @@ static void display_showcmd(void)
|
||||
showcmd_is_clear = (len == 0);
|
||||
|
||||
if (ui_has(kUIMessages)) {
|
||||
Array content = ARRAY_DICT_INIT;
|
||||
MAXSIZE_TEMP_ARRAY(content, 1);
|
||||
MAXSIZE_TEMP_ARRAY(chunk, 2);
|
||||
if (len > 0) {
|
||||
Array chunk = ARRAY_DICT_INIT;
|
||||
// placeholder for future highlight support
|
||||
ADD(chunk, INTEGER_OBJ(0));
|
||||
ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
|
||||
ADD(content, ARRAY_OBJ(chunk));
|
||||
ADD_C(chunk, INTEGER_OBJ(0));
|
||||
ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf)));
|
||||
ADD_C(content, ARRAY_OBJ(chunk));
|
||||
}
|
||||
ui_call_msg_showcmd(content);
|
||||
return;
|
||||
|
@ -4528,7 +4528,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
|
||||
last_status(false); // (re)set last window status line.
|
||||
} else if (pp == &p_stal) {
|
||||
// (re)set tab page line
|
||||
shell_new_rows(); // recompute window positions and heights
|
||||
win_new_screen_rows(); // recompute window positions and heights
|
||||
} else if (pp == &curwin->w_p_fdl) {
|
||||
newFoldLevel();
|
||||
} else if (pp == &curwin->w_p_fml) {
|
||||
@ -4617,7 +4617,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
|
||||
p_columns = MIN_COLUMNS;
|
||||
}
|
||||
|
||||
// True max size is defined by check_shellsize()
|
||||
// True max size is defined by check_screensize()
|
||||
p_lines = MIN(p_lines, INT_MAX);
|
||||
p_columns = MIN(p_columns, INT_MAX);
|
||||
|
||||
@ -4635,7 +4635,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
|
||||
// messages.
|
||||
Rows = (int)p_lines;
|
||||
Columns = (int)p_columns;
|
||||
check_shellsize();
|
||||
check_screensize();
|
||||
if (cmdline_row > Rows - p_ch && Rows > p_ch) {
|
||||
assert(p_ch >= 0 && Rows - p_ch <= INT_MAX);
|
||||
cmdline_row = (int)(Rows - p_ch);
|
||||
|
@ -151,17 +151,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
|
||||
if (pum_external) {
|
||||
if (array_changed) {
|
||||
Array arr = ARRAY_DICT_INIT;
|
||||
Arena arena = ARENA_EMPTY;
|
||||
arena_start(&arena, &ui_ext_fixblk);
|
||||
Array arr = arena_array(&arena, (size_t)size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Array item = ARRAY_DICT_INIT;
|
||||
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text)));
|
||||
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind)));
|
||||
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra)));
|
||||
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info)));
|
||||
ADD(arr, ARRAY_OBJ(item));
|
||||
Array item = arena_array(&arena, 4);
|
||||
ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_text)));
|
||||
ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_kind)));
|
||||
ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_extra)));
|
||||
ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_info)));
|
||||
ADD_C(arr, ARRAY_OBJ(item));
|
||||
}
|
||||
ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
|
||||
pum_anchor_grid);
|
||||
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
|
||||
} else {
|
||||
ui_call_popupmenu_select(selected);
|
||||
return;
|
||||
@ -438,7 +441,7 @@ void pum_redraw(void)
|
||||
if (ui_has(kUIMultigrid)) {
|
||||
const char *anchor = pum_above ? "SW" : "NW";
|
||||
int row_off = pum_above ? -pum_height : 0;
|
||||
ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor),
|
||||
ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor),
|
||||
pum_anchor_grid, pum_row - row_off, pum_col - col_off,
|
||||
false, pum_grid.zindex);
|
||||
}
|
||||
|
@ -5578,9 +5578,9 @@ void check_for_delay(bool check_msg_scroll)
|
||||
///
|
||||
/// There may be some time between setting Rows and Columns and (re)allocating
|
||||
/// default_grid arrays. This happens when starting up and when
|
||||
/// (manually) changing the shell size. Always use default_grid.rows and
|
||||
/// (manually) changing the screen size. Always use default_grid.rows and
|
||||
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
|
||||
/// and Columns for positioning text etc. where the final size of the shell is
|
||||
/// and Columns for positioning text etc. where the final size of the screen is
|
||||
/// needed.
|
||||
void screenalloc(void)
|
||||
{
|
||||
@ -5615,14 +5615,14 @@ retry:
|
||||
*/
|
||||
++RedrawingDisabled;
|
||||
|
||||
// win_new_shellsize will recompute floats position, but tell the
|
||||
// win_new_screensize will recompute floats position, but tell the
|
||||
// compositor to not redraw them yet
|
||||
ui_comp_set_screen_valid(false);
|
||||
if (msg_grid.chars) {
|
||||
msg_grid_invalid = true;
|
||||
}
|
||||
|
||||
win_new_shellsize(); // fit the windows in the new sized shell
|
||||
win_new_screensize(); // fit the windows in the new sized screen
|
||||
|
||||
comp_col(); // recompute columns for shown command and ruler
|
||||
|
||||
@ -6341,35 +6341,49 @@ void draw_tabline(void)
|
||||
|
||||
void ui_ext_tabline_update(void)
|
||||
{
|
||||
Array tabs = ARRAY_DICT_INIT;
|
||||
Arena arena = ARENA_EMPTY;
|
||||
arena_start(&arena, &ui_ext_fixblk);
|
||||
|
||||
size_t n_tabs = 0;
|
||||
FOR_ALL_TABS(tp) {
|
||||
Dictionary tab_info = ARRAY_DICT_INIT;
|
||||
PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle));
|
||||
n_tabs++;
|
||||
}
|
||||
|
||||
Array tabs = arena_array(&arena, n_tabs);
|
||||
FOR_ALL_TABS(tp) {
|
||||
Dictionary tab_info = arena_dict(&arena, 2);
|
||||
PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle));
|
||||
|
||||
win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin;
|
||||
get_trans_bufname(cwp->w_buffer);
|
||||
PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
|
||||
PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
|
||||
|
||||
ADD(tabs, DICTIONARY_OBJ(tab_info));
|
||||
ADD_C(tabs, DICTIONARY_OBJ(tab_info));
|
||||
}
|
||||
|
||||
Array buffers = ARRAY_DICT_INIT;
|
||||
size_t n_buffers = 0;
|
||||
FOR_ALL_BUFFERS(buf) {
|
||||
n_buffers += buf->b_p_bl ? 1 : 0;
|
||||
}
|
||||
|
||||
Array buffers = arena_array(&arena, n_buffers);
|
||||
FOR_ALL_BUFFERS(buf) {
|
||||
// Do not include unlisted buffers
|
||||
if (!buf->b_p_bl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Dictionary buffer_info = ARRAY_DICT_INIT;
|
||||
PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
|
||||
Dictionary buffer_info = arena_dict(&arena, 2);
|
||||
PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
|
||||
|
||||
get_trans_bufname(buf);
|
||||
PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
|
||||
PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
|
||||
|
||||
ADD(buffers, DICTIONARY_OBJ(buffer_info));
|
||||
ADD_C(buffers, DICTIONARY_OBJ(buffer_info));
|
||||
}
|
||||
|
||||
ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
|
||||
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6618,11 +6632,11 @@ static void win_redr_ruler(win_T *wp, bool always)
|
||||
}
|
||||
|
||||
if (ui_has(kUIMessages) && !part_of_status) {
|
||||
Array content = ARRAY_DICT_INIT;
|
||||
Array chunk = ARRAY_DICT_INIT;
|
||||
ADD(chunk, INTEGER_OBJ(attr));
|
||||
ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer)));
|
||||
ADD(content, ARRAY_OBJ(chunk));
|
||||
MAXSIZE_TEMP_ARRAY(content, 1);
|
||||
MAXSIZE_TEMP_ARRAY(chunk, 2);
|
||||
ADD_C(chunk, INTEGER_OBJ(attr));
|
||||
ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer)));
|
||||
ADD_C(content, ARRAY_OBJ(chunk));
|
||||
ui_call_msg_ruler(content);
|
||||
did_show_ext_ruler = true;
|
||||
} else {
|
||||
@ -6744,7 +6758,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
|
||||
prev_col_off = cur_col_off;
|
||||
}
|
||||
|
||||
/// Set dimensions of the Nvim application "shell".
|
||||
/// Set dimensions of the Nvim application "screen".
|
||||
void screen_resize(int width, int height)
|
||||
{
|
||||
// Avoid recursiveness, can happen when setting the window size causes
|
||||
@ -6775,7 +6789,7 @@ void screen_resize(int width, int height)
|
||||
|
||||
Rows = height;
|
||||
Columns = width;
|
||||
check_shellsize();
|
||||
check_screensize();
|
||||
int max_p_ch = Rows - min_rows() + 1;
|
||||
if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
|
||||
p_ch = max_p_ch ? max_p_ch : 1;
|
||||
@ -6852,49 +6866,23 @@ void screen_resize(int width, int height)
|
||||
resizing_screen = false;
|
||||
}
|
||||
|
||||
/// Check if the new Nvim application "shell" dimensions are valid.
|
||||
/// Check if the new Nvim application "screen" dimensions are valid.
|
||||
/// Correct it if it's too small or way too big.
|
||||
void check_shellsize(void)
|
||||
void check_screensize(void)
|
||||
{
|
||||
// Limit Rows and Columns to avoid an overflow in Rows * Columns.
|
||||
if (Rows < min_rows()) {
|
||||
// need room for one window and command line
|
||||
Rows = min_rows();
|
||||
} else if (Rows > 1000) {
|
||||
Rows = 1000;
|
||||
}
|
||||
limit_screen_size();
|
||||
}
|
||||
|
||||
// Limit Rows and Columns to avoid an overflow in Rows * Columns.
|
||||
void limit_screen_size(void)
|
||||
{
|
||||
if (Columns < MIN_COLUMNS) {
|
||||
Columns = MIN_COLUMNS;
|
||||
} else if (Columns > 10000) {
|
||||
Columns = 10000;
|
||||
}
|
||||
|
||||
if (Rows > 1000) {
|
||||
Rows = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
void win_new_shellsize(void)
|
||||
{
|
||||
static long old_Rows = 0;
|
||||
static long old_Columns = 0;
|
||||
|
||||
if (old_Rows != Rows) {
|
||||
// If 'window' uses the whole screen, keep it using that.
|
||||
// Don't change it when set with "-w size" on the command line.
|
||||
if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) {
|
||||
p_window = Rows - 1;
|
||||
}
|
||||
old_Rows = Rows;
|
||||
shell_new_rows(); // update window sizes
|
||||
}
|
||||
if (old_Columns != Columns) {
|
||||
old_Columns = Columns;
|
||||
shell_new_columns(); // update window sizes
|
||||
}
|
||||
}
|
||||
|
||||
win_T *get_win_by_grid_handle(handle_T handle)
|
||||
|
@ -64,6 +64,8 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
|
||||
static bool has_mouse = false;
|
||||
static int pending_has_mouse = -1;
|
||||
|
||||
static Array call_buf = ARRAY_DICT_INIT;
|
||||
|
||||
#if MIN_LOG_LEVEL > LOGLVL_DBG
|
||||
# define UI_LOG(funname)
|
||||
#else
|
||||
@ -123,6 +125,12 @@ void ui_init(void)
|
||||
default_grid.handle = 1;
|
||||
msg_grid_adj.target = &default_grid;
|
||||
ui_comp_init();
|
||||
kv_ensure_space(call_buf, 16);
|
||||
}
|
||||
|
||||
void ui_free_all_mem(void)
|
||||
{
|
||||
kv_destroy(call_buf);
|
||||
}
|
||||
|
||||
void ui_builtin_start(void)
|
||||
@ -173,15 +181,6 @@ bool ui_active(void)
|
||||
return ui_count > 1;
|
||||
}
|
||||
|
||||
void ui_event(char *name, Array args)
|
||||
{
|
||||
bool args_consumed = false;
|
||||
ui_call_event(name, args, &args_consumed);
|
||||
if (!args_consumed) {
|
||||
api_free_array(args);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_refresh(void)
|
||||
{
|
||||
if (!ui_active()) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/memory.h"
|
||||
|
||||
typedef enum {
|
||||
kUICmdline = 0,
|
||||
@ -46,6 +47,8 @@ enum {
|
||||
|
||||
typedef int LineFlags;
|
||||
|
||||
EXTERN ArenaMem ui_ext_fixblk INIT(= NULL);
|
||||
|
||||
struct ui_t {
|
||||
bool rgb;
|
||||
bool override; ///< Force highest-requested UI capabilities.
|
||||
|
@ -893,7 +893,7 @@ void ui_ext_win_position(win_T *wp)
|
||||
|
||||
wp->w_grid_alloc.zindex = wp->w_float_config.zindex;
|
||||
if (ui_has(kUIMultigrid)) {
|
||||
String anchor = cstr_to_string(float_anchor_str[c.anchor]);
|
||||
String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]);
|
||||
ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
|
||||
grid->handle, row, col, c.focusable,
|
||||
wp->w_grid_alloc.zindex);
|
||||
@ -967,8 +967,8 @@ static int check_split_disallowed(void)
|
||||
* "flags":
|
||||
* WSP_ROOM: require enough room for new window
|
||||
* WSP_VERT: vertical split.
|
||||
* WSP_TOP: open window at the top-left of the shell (help window).
|
||||
* WSP_BOT: open window at the bottom-right of the shell (quickfix window).
|
||||
* WSP_TOP: open window at the top-left of the screen (help window).
|
||||
* WSP_BOT: open window at the bottom-right of the screen (quickfix window).
|
||||
* WSP_HELP: creating the help window, keep layout snapshot
|
||||
*
|
||||
* return FAIL for failure, OK otherwise
|
||||
@ -2499,7 +2499,7 @@ void close_windows(buf_T *buf, bool keep_curwin)
|
||||
|
||||
redraw_tabline = true;
|
||||
if (h != tabline_height()) {
|
||||
shell_new_rows();
|
||||
win_new_screen_rows();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2606,7 +2606,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
|
||||
|
||||
win_close_othertab(win, free_buf, prev_curtab);
|
||||
if (h != tabline_height()) {
|
||||
shell_new_rows();
|
||||
win_new_screen_rows();
|
||||
}
|
||||
}
|
||||
entering_window(curwin);
|
||||
@ -4350,10 +4350,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
|
||||
}
|
||||
|
||||
if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) {
|
||||
shell_new_rows();
|
||||
win_new_screen_rows();
|
||||
}
|
||||
if (curtab->tp_old_Columns != Columns && starting == 0) {
|
||||
shell_new_columns(); // update window widths
|
||||
win_new_screen_cols(); // update window widths
|
||||
}
|
||||
|
||||
lastused_tabpage = old_curtab;
|
||||
@ -5277,11 +5277,29 @@ static void frame_remove(frame_T *frp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from win_new_shellsize() after Rows changed.
|
||||
* This only does the current tab page, others must be done when made active.
|
||||
*/
|
||||
void shell_new_rows(void)
|
||||
void win_new_screensize(void)
|
||||
{
|
||||
static long old_Rows = 0;
|
||||
static long old_Columns = 0;
|
||||
|
||||
if (old_Rows != Rows) {
|
||||
// If 'window' uses the whole screen, keep it using that.
|
||||
// Don't change it when set with "-w size" on the command line.
|
||||
if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) {
|
||||
p_window = Rows - 1;
|
||||
}
|
||||
old_Rows = Rows;
|
||||
win_new_screen_rows(); // update window sizes
|
||||
}
|
||||
if (old_Columns != Columns) {
|
||||
old_Columns = Columns;
|
||||
win_new_screen_cols(); // update window sizes
|
||||
}
|
||||
}
|
||||
/// Called from win_new_screensize() after Rows changed.
|
||||
///
|
||||
/// This only does the current tab page, others must be done when made active.
|
||||
void win_new_screen_rows(void)
|
||||
{
|
||||
int h = (int)ROWS_AVAIL;
|
||||
|
||||
@ -5305,10 +5323,8 @@ void shell_new_rows(void)
|
||||
curtab->tp_ch_used = p_ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from win_new_shellsize() after Columns changed.
|
||||
*/
|
||||
void shell_new_columns(void)
|
||||
/// Called from win_new_screensize() after Columns changed.
|
||||
void win_new_screen_cols(void)
|
||||
{
|
||||
if (firstwin == NULL) { // not initialized yet
|
||||
return;
|
||||
|
@ -6,6 +6,9 @@ local feed = helpers.feed
|
||||
local feed_command = helpers.feed_command
|
||||
local insert = helpers.insert
|
||||
local funcs = helpers.funcs
|
||||
local meths = helpers.meths
|
||||
local split = helpers.split
|
||||
local dedent = helpers.dedent
|
||||
|
||||
describe("multibyte rendering", function()
|
||||
local screen
|
||||
@ -115,6 +118,35 @@ describe("multibyte rendering", function()
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('works with a lot of unicode (zalgo) text', function()
|
||||
screen:try_resize(65, 10)
|
||||
meths.buf_set_lines(0,0,-1,true, split(dedent [[
|
||||
L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚
|
||||
ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊
|
||||
U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚
|
||||
u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎
|
||||
D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇
|
||||
ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́
|
||||
š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕
|
||||
o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚]],
|
||||
'\n'))
|
||||
|
||||
-- tests that we can handle overflow of the buffer
|
||||
-- for redraw events (4096 bytes) gracefully
|
||||
screen:expect{grid=[[
|
||||
^L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚ |
|
||||
ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊ |
|
||||
U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚ |
|
||||
u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎ |
|
||||
D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇ |
|
||||
ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́ |
|
||||
š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕ |
|
||||
o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚ |
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('multibyte rendering: statusline', function()
|
||||
|
Loading…
Reference in New Issue
Block a user