mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge PR #1810 'abstract_ui fixes and improvements(continuation)'
This commit is contained in:
commit
5f24549ab1
@ -76,6 +76,7 @@
|
||||
#include "nvim/tag.h"
|
||||
#include "nvim/tempfile.h"
|
||||
#include "nvim/term.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/mouse.h"
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/version.h"
|
||||
@ -10023,6 +10024,8 @@ static void f_has(typval_T *argvars, typval_T *rettv)
|
||||
#endif
|
||||
} else if (STRICMP(name, "syntax_items") == 0) {
|
||||
n = syntax_present(curwin);
|
||||
} else if (STRICMP(name, "gui_running") == 0) {
|
||||
n = ui_rgb_attached();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14440,7 +14443,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv)
|
||||
if (modec != 't' && modec != 'c' && modec != 'g')
|
||||
modec = 0; /* replace invalid with current */
|
||||
} else {
|
||||
if (t_colors > 1)
|
||||
if (abstract_ui || t_colors > 1)
|
||||
modec = 'c';
|
||||
else
|
||||
modec = 't';
|
||||
|
@ -261,10 +261,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (params.want_full_screen && !silent_mode) {
|
||||
if (embedded_mode) {
|
||||
// In embedded mode don't do terminal-related initializations, assume an
|
||||
// initial screen size of 80x20
|
||||
full_screen = true;
|
||||
screen_resize(80, 20, false);
|
||||
// embedded mode implies abstract_ui
|
||||
termcapinit((uint8_t *)"abstract_ui");
|
||||
} else {
|
||||
// set terminal name and get terminal capabilities (will set full_screen)
|
||||
@ -278,7 +275,9 @@ int main(int argc, char **argv)
|
||||
event_init();
|
||||
|
||||
if (abstract_ui) {
|
||||
full_screen = true;
|
||||
t_colors = 256;
|
||||
T_CCO = (uint8_t *)"256";
|
||||
} else {
|
||||
// Print a warning if stdout is not a terminal TODO(tarruda): Remove this
|
||||
// check once the new terminal UI is implemented
|
||||
|
@ -45,6 +45,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
uint64_t id;
|
||||
size_t pending_requests;
|
||||
PMap(cstr_t) *subscribed_events;
|
||||
bool is_job, closed;
|
||||
msgpack_unpacker *unpacker;
|
||||
@ -83,7 +84,6 @@ static uint64_t next_id = 1;
|
||||
static PMap(uint64_t) *channels = NULL;
|
||||
static PMap(cstr_t) *event_strings = NULL;
|
||||
static msgpack_sbuffer out_buffer;
|
||||
static size_t pending_requests = 0;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "msgpack_rpc/channel.c.generated.h"
|
||||
@ -103,14 +103,7 @@ void channel_init(void)
|
||||
}
|
||||
|
||||
if (abstract_ui) {
|
||||
// Add handler for "attach_ui"
|
||||
remote_ui_init();
|
||||
String method = cstr_as_string("attach_ui");
|
||||
MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .defer = true};
|
||||
msgpack_rpc_add_method_handler(method, handler);
|
||||
method = cstr_as_string("detach_ui");
|
||||
handler.fn = remote_ui_detach;
|
||||
msgpack_rpc_add_method_handler(method, handler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,20 +193,21 @@ bool channel_send_event(uint64_t id, char *name, Array args)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pending_requests) {
|
||||
DelayedNotification p = {
|
||||
.channel = channel,
|
||||
.method = cstr_to_string(name),
|
||||
.args = args
|
||||
};
|
||||
// Pending request, queue the notification for sending later
|
||||
*kl_pushp(DelayedNotification, delayed_notifications) = p;
|
||||
} else {
|
||||
if (channel) {
|
||||
send_event(channel, name, args);
|
||||
if (channel) {
|
||||
if (channel->pending_requests) {
|
||||
DelayedNotification p = {
|
||||
.channel = channel,
|
||||
.method = cstr_to_string(name),
|
||||
.args = args
|
||||
};
|
||||
// Pending request, queue the notification for sending later
|
||||
*kl_pushp(DelayedNotification, delayed_notifications) = p;
|
||||
} else {
|
||||
broadcast_event(name, args);
|
||||
send_event(channel, name, args);
|
||||
}
|
||||
} else {
|
||||
// TODO(tarruda): Implement event broadcasting in vimscript
|
||||
broadcast_event(name, args);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -246,10 +240,10 @@ Object channel_send_call(uint64_t id,
|
||||
// Push the frame
|
||||
ChannelCallFrame frame = {request_id, false, false, NIL};
|
||||
kv_push(ChannelCallFrame *, channel->call_stack, &frame);
|
||||
pending_requests++;
|
||||
channel->pending_requests++;
|
||||
event_poll_until(-1, frame.returned);
|
||||
(void)kv_pop(channel->call_stack);
|
||||
pending_requests--;
|
||||
channel->pending_requests--;
|
||||
|
||||
if (frame.errored) {
|
||||
api_set_error(err, Exception, "%s", frame.result.data.string.data);
|
||||
@ -261,7 +255,7 @@ Object channel_send_call(uint64_t id,
|
||||
free_channel(channel);
|
||||
}
|
||||
|
||||
if (!pending_requests) {
|
||||
if (!channel->pending_requests) {
|
||||
send_delayed_notifications();
|
||||
}
|
||||
|
||||
@ -644,6 +638,7 @@ static void close_channel(Channel *channel)
|
||||
uv_handle_t *handle = (uv_handle_t *)channel->data.streams.uv;
|
||||
if (handle) {
|
||||
uv_close(handle, close_cb);
|
||||
free_channel(channel);
|
||||
} else {
|
||||
event_push((Event) { .handler = on_stdio_close }, false);
|
||||
}
|
||||
@ -687,6 +682,7 @@ static Channel *register_channel(void)
|
||||
rv->closed = false;
|
||||
rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
|
||||
rv->id = next_id++;
|
||||
rv->pending_requests = 0;
|
||||
rv->subscribed_events = pmap_new(cstr_t)();
|
||||
rv->next_request_id = 1;
|
||||
kv_init(rv->call_stack);
|
||||
|
@ -26,18 +26,44 @@ static PMap(uint64_t) *connected_uis = NULL;
|
||||
void remote_ui_init(void)
|
||||
{
|
||||
connected_uis = pmap_new(uint64_t)();
|
||||
// Add handler for "attach_ui"
|
||||
String method = cstr_as_string("ui_attach");
|
||||
MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .defer = false};
|
||||
msgpack_rpc_add_method_handler(method, handler);
|
||||
method = cstr_as_string("ui_detach");
|
||||
handler.fn = remote_ui_detach;
|
||||
msgpack_rpc_add_method_handler(method, handler);
|
||||
method = cstr_as_string("ui_try_resize");
|
||||
handler.fn = remote_ui_try_resize;
|
||||
msgpack_rpc_add_method_handler(method, handler);
|
||||
}
|
||||
|
||||
Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, Array args,
|
||||
Error *error)
|
||||
void remote_ui_disconnect(uint64_t channel_id)
|
||||
{
|
||||
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
||||
if (!ui) {
|
||||
return;
|
||||
}
|
||||
UIData *data = ui->data;
|
||||
// destroy pending screen updates
|
||||
api_free_array(data->buffer);
|
||||
pmap_del(uint64_t)(connected_uis, channel_id);
|
||||
free(ui->data);
|
||||
ui_detach(ui);
|
||||
free(ui);
|
||||
}
|
||||
|
||||
static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id,
|
||||
Array args, Error *error)
|
||||
{
|
||||
if (pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||
api_set_error(error, Exception, _("UI already attached for channel"));
|
||||
return NIL;
|
||||
}
|
||||
|
||||
if (args.size != 2 || args.items[0].type != kObjectTypeInteger
|
||||
if (args.size != 3 || args.items[0].type != kObjectTypeInteger
|
||||
|| args.items[1].type != kObjectTypeInteger
|
||||
|| args.items[2].type != kObjectTypeBoolean
|
||||
|| args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) {
|
||||
api_set_error(error, Validation,
|
||||
_("Arguments must be a pair of positive integers "
|
||||
@ -50,6 +76,7 @@ Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, Array args,
|
||||
UI *ui = xcalloc(1, sizeof(UI));
|
||||
ui->width = (int)args.items[0].data.integer;
|
||||
ui->height = (int)args.items[1].data.integer;
|
||||
ui->rgb = args.items[2].data.boolean;
|
||||
ui->data = data;
|
||||
ui->resize = remote_ui_resize;
|
||||
ui->clear = remote_ui_clear;
|
||||
@ -67,16 +94,19 @@ Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, Array args,
|
||||
ui->put = remote_ui_put;
|
||||
ui->bell = remote_ui_bell;
|
||||
ui->visual_bell = remote_ui_visual_bell;
|
||||
ui->update_fg = remote_ui_update_fg;
|
||||
ui->update_bg = remote_ui_update_bg;
|
||||
ui->flush = remote_ui_flush;
|
||||
ui->suspend = remote_ui_suspend;
|
||||
ui->set_title = remote_ui_set_title;
|
||||
ui->set_icon = remote_ui_set_icon;
|
||||
pmap_put(uint64_t)(connected_uis, channel_id, ui);
|
||||
ui_attach(ui);
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
Object remote_ui_detach(uint64_t channel_id, uint64_t request_id, Array args,
|
||||
Error *error)
|
||||
static Object remote_ui_detach(uint64_t channel_id, uint64_t request_id,
|
||||
Array args, Error *error)
|
||||
{
|
||||
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||
api_set_error(error, Exception, _("UI is not attached for channel"));
|
||||
@ -86,21 +116,30 @@ Object remote_ui_detach(uint64_t channel_id, uint64_t request_id, Array args,
|
||||
return NIL;
|
||||
}
|
||||
|
||||
void remote_ui_disconnect(uint64_t channel_id)
|
||||
static Object remote_ui_try_resize(uint64_t channel_id, uint64_t request_id,
|
||||
Array args, Error *error)
|
||||
{
|
||||
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
||||
if (!ui) {
|
||||
return;
|
||||
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||
api_set_error(error, Exception, _("UI is not attached for channel"));
|
||||
}
|
||||
UIData *data = ui->data;
|
||||
// destroy pending screen updates
|
||||
api_free_array(data->buffer);
|
||||
pmap_del(uint64_t)(connected_uis, channel_id);
|
||||
free(ui->data);
|
||||
ui_detach(ui);
|
||||
free(ui);
|
||||
|
||||
if (args.size != 2 || args.items[0].type != kObjectTypeInteger
|
||||
|| args.items[1].type != kObjectTypeInteger
|
||||
|| args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) {
|
||||
api_set_error(error, Validation,
|
||||
_("Arguments must be a pair of positive integers "
|
||||
"representing the remote screen width/height"));
|
||||
return NIL;
|
||||
}
|
||||
|
||||
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
||||
ui->width = (int)args.items[0].data.integer;
|
||||
ui->height = (int)args.items[1].data.integer;
|
||||
ui_refresh();
|
||||
return NIL;
|
||||
}
|
||||
|
||||
|
||||
static void push_call(UI *ui, char *name, Array args)
|
||||
{
|
||||
Array call = ARRAY_DICT_INIT;
|
||||
@ -214,10 +253,6 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
|
||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.standout) {
|
||||
PUT(hl, "standout", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.underline) {
|
||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
||||
}
|
||||
@ -266,6 +301,20 @@ static void remote_ui_visual_bell(UI *ui)
|
||||
push_call(ui, "visual_bell", args);
|
||||
}
|
||||
|
||||
static void remote_ui_update_fg(UI *ui, int fg)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(fg));
|
||||
push_call(ui, "update_fg", args);
|
||||
}
|
||||
|
||||
static void remote_ui_update_bg(UI *ui, int bg)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, INTEGER_OBJ(bg));
|
||||
push_call(ui, "update_bg", args);
|
||||
}
|
||||
|
||||
static void remote_ui_flush(UI *ui)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
@ -275,6 +324,20 @@ static void remote_ui_flush(UI *ui)
|
||||
|
||||
static void remote_ui_suspend(UI *ui)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
remote_ui_disconnect(data->channel_id);
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
push_call(ui, "suspend", args);
|
||||
}
|
||||
|
||||
static void remote_ui_set_title(UI *ui, char *title)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, STRING_OBJ(cstr_to_string(title)));
|
||||
push_call(ui, "set_title", args);
|
||||
}
|
||||
|
||||
static void remote_ui_set_icon(UI *ui, char *icon)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
ADD(args, STRING_OBJ(cstr_to_string(icon)));
|
||||
push_call(ui, "set_icon", args);
|
||||
}
|
||||
|
@ -187,14 +187,20 @@ size_t input_enqueue(String keys)
|
||||
unsigned int new_size = trans_special((uint8_t **)&ptr, buf, false);
|
||||
|
||||
if (!new_size) {
|
||||
if (*ptr == '<') {
|
||||
// Invalid key sequence, skip until the next '>' or until *end
|
||||
do {
|
||||
ptr++;
|
||||
} while (ptr < end && *ptr != '>');
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
// copy the character unmodified
|
||||
*buf = (uint8_t)*ptr++;
|
||||
new_size = 1;
|
||||
}
|
||||
|
||||
new_size = handle_mouse_event(&ptr, buf, new_size);
|
||||
// TODO(tarruda): Don't produce past unclosed '<' characters, except if
|
||||
// there's a lot of characters after the '<'
|
||||
rbuffer_write(input_buffer, (char *)buf, new_size);
|
||||
}
|
||||
|
||||
@ -217,7 +223,8 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
|
||||
mouse_code = buf[5];
|
||||
}
|
||||
|
||||
if (mouse_code < KE_LEFTMOUSE || mouse_code > KE_RIGHTRELEASE) {
|
||||
if (!((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
|
||||
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT))) {
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
@ -226,7 +233,7 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
|
||||
// find mouse coordinates, and it would be too expensive to refactor this
|
||||
// now.
|
||||
int col, row, advance;
|
||||
if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance)) {
|
||||
if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) {
|
||||
if (col >= 0 && row >= 0) {
|
||||
mouse_row = row;
|
||||
mouse_col = col;
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "nvim/option_defs.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/ui.h"
|
||||
|
||||
#define DYNAMIC_BUFFER_INIT {NULL, 0, 0}
|
||||
|
||||
@ -414,6 +413,7 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer,
|
||||
|
||||
char *start = output;
|
||||
size_t off = 0;
|
||||
int lastrow = (int)Rows - 1;
|
||||
while (off < remaining) {
|
||||
if (output[off] == NL) {
|
||||
// Insert the line
|
||||
@ -421,10 +421,8 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer,
|
||||
if (to_buffer) {
|
||||
ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
|
||||
} else {
|
||||
// pending data from the output buffer has been flushed to the screen,
|
||||
// safe to call ui_write directly
|
||||
ui_write((char_u *)output, (int)off);
|
||||
ui_write((char_u *)"\r\n", 2);
|
||||
screen_del_lines(0, 0, 1, (int)Rows, true, NULL);
|
||||
screen_puts_len((char_u *)output, (int)off, lastrow, 0, 0);
|
||||
}
|
||||
size_t skip = off + 1;
|
||||
output += skip;
|
||||
@ -448,8 +446,8 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer,
|
||||
// remember that the NL was missing
|
||||
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
|
||||
} else {
|
||||
ui_write((char_u *)output, (int)remaining);
|
||||
ui_write((char_u *)"\r\n", 2);
|
||||
screen_del_lines(0, 0, 1, (int)Rows, true, NULL);
|
||||
screen_puts_len((char_u *)output, (int)remaining, lastrow, 0, 0);
|
||||
}
|
||||
output += remaining;
|
||||
} else if (to_buffer) {
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/tempfile.h"
|
||||
#include "nvim/term.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/time.h"
|
||||
@ -181,22 +182,26 @@ void mch_settitle(char_u *title, char_u *icon)
|
||||
* Note: if "t_ts" is set, title is set with escape sequence rather
|
||||
* than x11 calls, because the x11 calls don't always work
|
||||
*/
|
||||
if ((type || *T_TS != NUL) && title != NULL) {
|
||||
if ((type || *T_TS != NUL || abstract_ui) && title != NULL) {
|
||||
if (oldtitle == NULL
|
||||
) /* first call but not in GUI, save title */
|
||||
(void)get_x11_title(FALSE);
|
||||
|
||||
if (*T_TS != NUL) /* it's OK if t_fs is empty */
|
||||
if (abstract_ui) {
|
||||
ui_set_title((char *)title);
|
||||
} else if (*T_TS != NUL) /* it's OK if t_fs is empty */
|
||||
term_settitle(title);
|
||||
did_set_title = TRUE;
|
||||
}
|
||||
|
||||
if ((type || *T_CIS != NUL) && icon != NULL) {
|
||||
if ((type || *T_CIS != NUL || abstract_ui) && icon != NULL) {
|
||||
if (oldicon == NULL
|
||||
) /* first call, save icon */
|
||||
get_x11_icon(FALSE);
|
||||
|
||||
if (*T_CIS != NUL) {
|
||||
if (abstract_ui) {
|
||||
ui_set_icon((char *)icon);
|
||||
} else if (*T_CIS != NUL) {
|
||||
out_str(T_CIS); /* set icon start */
|
||||
out_str_nf(icon);
|
||||
out_str(T_CIE); /* set icon end */
|
||||
|
@ -5971,7 +5971,7 @@ void screen_stop_highlight(void)
|
||||
*/
|
||||
void reset_cterm_colors(void)
|
||||
{
|
||||
if (t_colors > 1) {
|
||||
if (!abstract_ui && t_colors > 1) {
|
||||
/* set Normal cterm colors */
|
||||
if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0) {
|
||||
out_str(T_OP);
|
||||
@ -6150,8 +6150,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
|
||||
return;
|
||||
|
||||
/* it's a "normal" terminal when not in a GUI or cterm */
|
||||
norm_term = (
|
||||
t_colors <= 1);
|
||||
norm_term = (!abstract_ui && t_colors <= 1);
|
||||
for (row = start_row; row < end_row; ++row) {
|
||||
if (has_mbyte
|
||||
) {
|
||||
@ -6675,8 +6674,8 @@ static void linecopy(int to, int from, win_T *wp)
|
||||
*/
|
||||
int can_clear(char_u *p)
|
||||
{
|
||||
return *p != NUL && (t_colors <= 1
|
||||
|| cterm_normal_bg_color == 0 || *T_UT != NUL);
|
||||
return abstract_ui || (*p != NUL && (t_colors <= 1
|
||||
|| cterm_normal_bg_color == 0 || *T_UT != NUL));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -8186,8 +8185,13 @@ void screen_resize(int width, int height, int mustset)
|
||||
Columns = width;
|
||||
}
|
||||
check_shellsize();
|
||||
height = Rows;
|
||||
width = Columns;
|
||||
|
||||
if (abstract_ui) {
|
||||
// Clear the output buffer to ensure UIs don't receive redraw command meant
|
||||
// for invalid screen sizes.
|
||||
out_buf_clear();
|
||||
ui_resize(width, height);
|
||||
} else {
|
||||
mch_set_shellsize();
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/syntax_defs.h"
|
||||
#include "nvim/term.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/time.h"
|
||||
|
||||
@ -6036,6 +6037,7 @@ int load_colors(char_u *name)
|
||||
apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
|
||||
|
||||
recursive = FALSE;
|
||||
ui_refresh();
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -6438,8 +6440,7 @@ do_highlight (
|
||||
HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
|
||||
}
|
||||
color &= 7; /* truncate to 8 colors */
|
||||
} else if (t_colors == 16 || t_colors == 88
|
||||
|| t_colors == 256) {
|
||||
} else if (t_colors == 16 || t_colors == 88 || t_colors == 256) {
|
||||
/*
|
||||
* Guess: if the termcap entry ends in 'm', it is
|
||||
* probably an xterm-like terminal. Use the changed
|
||||
@ -6449,7 +6450,7 @@ do_highlight (
|
||||
p = T_CAF;
|
||||
else
|
||||
p = T_CSF;
|
||||
if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
|
||||
if (abstract_ui || (*p != NUL && *(p + STRLEN(p) - 1) == 'm'))
|
||||
switch (t_colors) {
|
||||
case 16:
|
||||
color = color_numbers_8[i];
|
||||
@ -6632,6 +6633,10 @@ do_highlight (
|
||||
if (is_normal_group) {
|
||||
HL_TABLE()[idx].sg_term_attr = 0;
|
||||
HL_TABLE()[idx].sg_cterm_attr = 0;
|
||||
if (abstract_ui) {
|
||||
// If the normal group has changed, it is simpler to refresh every UI
|
||||
ui_refresh();
|
||||
}
|
||||
} else
|
||||
set_hl_attr(idx);
|
||||
HL_TABLE()[idx].sg_scriptID = current_SID;
|
||||
@ -6860,7 +6865,7 @@ int hl_combine_attr(int char_attr, int prim_attr)
|
||||
if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
|
||||
return char_attr | prim_attr;
|
||||
|
||||
if (t_colors > 1) {
|
||||
if (abstract_ui || t_colors > 1) {
|
||||
if (char_attr > HL_ALL)
|
||||
char_aep = syn_cterm_attr2entry(char_attr);
|
||||
if (char_aep != NULL)
|
||||
@ -6924,7 +6929,7 @@ int syn_attr2attr(int attr)
|
||||
{
|
||||
attrentry_T *aep;
|
||||
|
||||
if (t_colors > 1)
|
||||
if (abstract_ui || t_colors > 1)
|
||||
aep = syn_cterm_attr2entry(attr);
|
||||
else
|
||||
aep = syn_term_attr2entry(attr);
|
||||
@ -7194,9 +7199,10 @@ set_hl_attr (
|
||||
* For the color term mode: If there are other than "normal"
|
||||
* highlighting attributes, need to allocate an attr number.
|
||||
*/
|
||||
if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
|
||||
if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
|
||||
&& sgp->sg_rgb_fg == -1 && sgp->sg_rgb_bg == -1) {
|
||||
sgp->sg_cterm_attr = sgp->sg_cterm;
|
||||
else {
|
||||
} else {
|
||||
at_en.ae_attr = abstract_ui ? sgp->sg_gui : sgp->sg_cterm;
|
||||
at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
|
||||
at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
|
||||
@ -7351,7 +7357,7 @@ int syn_id2attr(int hl_id)
|
||||
hl_id = syn_get_final_id(hl_id);
|
||||
sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
|
||||
|
||||
if (t_colors > 1)
|
||||
if (abstract_ui || t_colors > 1)
|
||||
attr = sgp->sg_cterm_attr;
|
||||
else
|
||||
attr = sgp->sg_term_attr;
|
||||
|
@ -170,6 +170,7 @@ static struct builtin_term builtin_termcaps[] =
|
||||
{(int)KS_DL, "\033|d"},
|
||||
{(int)KS_CDL, "\033|%p1%dD"},
|
||||
{(int)KS_CS, "\033|%p1%d;%p2%dR"},
|
||||
{(int)KS_CSV, "\033|%p1%d;%p2%dV"},
|
||||
{(int)KS_CL, "\033|C"},
|
||||
// attributes switched on with 'h', off with * 'H'
|
||||
{(int)KS_ME, "\033|31H"}, // HL_ALL
|
||||
@ -1816,17 +1817,20 @@ void term_write(char_u *s, size_t len)
|
||||
static char_u out_buf[OUT_SIZE + 1];
|
||||
static int out_pos = 0; /* number of chars in out_buf */
|
||||
|
||||
// Clear the output buffer
|
||||
void out_buf_clear(void)
|
||||
{
|
||||
out_pos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* out_flush(): flush the output buffer
|
||||
*/
|
||||
void out_flush(void)
|
||||
{
|
||||
if (out_pos != 0) {
|
||||
/* set out_pos to 0 before ui_write, to avoid recursiveness */
|
||||
int len = out_pos;
|
||||
out_pos = 0;
|
||||
ui_write(out_buf, len);
|
||||
}
|
||||
int len = out_pos;
|
||||
out_pos = 0;
|
||||
ui_write(out_buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2202,7 +2206,11 @@ void win_new_shellsize(void)
|
||||
*/
|
||||
void shell_resized(void)
|
||||
{
|
||||
screen_resize(0, 0, FALSE);
|
||||
if (abstract_ui) {
|
||||
ui_refresh();
|
||||
} else {
|
||||
screen_resize(0, 0, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
139
src/nvim/ui.c
139
src/nvim/ui.c
@ -60,11 +60,8 @@ static struct {
|
||||
int top, bot, left, right;
|
||||
} sr;
|
||||
static int current_highlight_mask = 0;
|
||||
static HlAttrs current_attrs = {
|
||||
false, false, false, false, false, false, -1, -1
|
||||
};
|
||||
static bool cursor_enabled = true;
|
||||
static int height = INT_MAX, width = INT_MAX;
|
||||
static int height, width;
|
||||
|
||||
// This set of macros allow us to use UI_CALL to invoke any function on
|
||||
// registered UI instances. The functions can have 0-5 arguments(configurable
|
||||
@ -98,6 +95,10 @@ void ui_write(uint8_t *s, int len)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
|
||||
char_u *tofree = NULL;
|
||||
|
||||
if (output_conv.vc_type != CONV_NONE) {
|
||||
@ -113,6 +114,16 @@ void ui_write(uint8_t *s, int len)
|
||||
free(tofree);
|
||||
}
|
||||
|
||||
bool ui_rgb_attached(void)
|
||||
{
|
||||
for (size_t i = 0; i < ui_count; i++) {
|
||||
if (uis[i]->rgb) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the machine has job control, use it to suspend the program,
|
||||
* otherwise fake it by starting a new shell.
|
||||
@ -122,11 +133,24 @@ void ui_suspend(void)
|
||||
{
|
||||
if (abstract_ui) {
|
||||
UI_CALL(suspend);
|
||||
UI_CALL(flush);
|
||||
} else {
|
||||
mch_suspend();
|
||||
}
|
||||
}
|
||||
|
||||
void ui_set_title(char *title)
|
||||
{
|
||||
UI_CALL(set_title, title);
|
||||
UI_CALL(flush);
|
||||
}
|
||||
|
||||
void ui_set_icon(char *icon)
|
||||
{
|
||||
UI_CALL(set_icon, icon);
|
||||
UI_CALL(flush);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to get the current Vim shell size. Put the result in Rows and Columns.
|
||||
* Use the new sizes as defaults for 'columns' and 'lines'.
|
||||
@ -165,8 +189,31 @@ void ui_cursor_shape(void)
|
||||
}
|
||||
}
|
||||
|
||||
void ui_resize(int width, int height)
|
||||
void ui_refresh(void)
|
||||
{
|
||||
if (!ui_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width = INT_MAX, height = INT_MAX;
|
||||
|
||||
for (size_t i = 0; i < ui_count; i++) {
|
||||
UI *ui = uis[i];
|
||||
width = ui->width < width ? ui->width : width;
|
||||
height = ui->height < height ? ui->height : height;
|
||||
}
|
||||
|
||||
screen_resize(width, height, true);
|
||||
}
|
||||
|
||||
void ui_resize(int new_width, int new_height)
|
||||
{
|
||||
width = new_width;
|
||||
height = new_height;
|
||||
|
||||
UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1));
|
||||
UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1));
|
||||
|
||||
sr.top = 0;
|
||||
sr.bot = height - 1;
|
||||
sr.left = 0;
|
||||
@ -240,7 +287,7 @@ void ui_attach(UI *ui)
|
||||
}
|
||||
|
||||
uis[ui_count++] = ui;
|
||||
resized(ui);
|
||||
ui_refresh();
|
||||
}
|
||||
|
||||
void ui_detach(UI *ui)
|
||||
@ -267,17 +314,8 @@ void ui_detach(UI *ui)
|
||||
|
||||
ui_count--;
|
||||
|
||||
if (ui->width == width || ui->height == height) {
|
||||
// It is possible that the UI being detached had the smallest screen,
|
||||
// so check for the new minimum dimensions
|
||||
width = height = INT_MAX;
|
||||
for (size_t i = 0; i < ui_count; i++) {
|
||||
check_dimensions(uis[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_count) {
|
||||
screen_resize(width, height, true);
|
||||
ui_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,8 +333,7 @@ static void highlight_start(int mask)
|
||||
return;
|
||||
}
|
||||
|
||||
set_highlight_args(current_highlight_mask, ¤t_attrs);
|
||||
UI_CALL(highlight_set, current_attrs);
|
||||
set_highlight_args(current_highlight_mask);
|
||||
}
|
||||
|
||||
static void highlight_stop(int mask)
|
||||
@ -309,12 +346,12 @@ static void highlight_stop(int mask)
|
||||
current_highlight_mask &= ~mask;
|
||||
}
|
||||
|
||||
set_highlight_args(current_highlight_mask, ¤t_attrs);
|
||||
UI_CALL(highlight_set, current_attrs);
|
||||
set_highlight_args(current_highlight_mask);
|
||||
}
|
||||
|
||||
static void set_highlight_args(int mask, HlAttrs *attrs)
|
||||
static void set_highlight_args(int mask)
|
||||
{
|
||||
HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 };
|
||||
attrentry_T *aep = NULL;
|
||||
|
||||
if (mask > HL_ALL) {
|
||||
@ -322,18 +359,40 @@ static void set_highlight_args(int mask, HlAttrs *attrs)
|
||||
mask = aep ? aep->ae_attr : 0;
|
||||
}
|
||||
|
||||
attrs->bold = mask & HL_BOLD;
|
||||
attrs->standout = mask & HL_STANDOUT;
|
||||
attrs->underline = mask & HL_UNDERLINE;
|
||||
attrs->undercurl = mask & HL_UNDERCURL;
|
||||
attrs->italic = mask & HL_ITALIC;
|
||||
attrs->reverse = mask & HL_INVERSE;
|
||||
attrs->foreground = aep && aep->fg_color >= 0 ? aep->fg_color : normal_fg;
|
||||
attrs->background = aep && aep->bg_color >= 0 ? aep->bg_color : normal_bg;
|
||||
rgb_attrs.bold = mask & HL_BOLD;
|
||||
rgb_attrs.underline = mask & HL_UNDERLINE;
|
||||
rgb_attrs.undercurl = mask & HL_UNDERCURL;
|
||||
rgb_attrs.italic = mask & HL_ITALIC;
|
||||
rgb_attrs.reverse = mask & (HL_INVERSE | HL_STANDOUT);
|
||||
HlAttrs cterm_attrs = rgb_attrs;
|
||||
|
||||
if (aep) {
|
||||
if (aep->fg_color != normal_fg) {
|
||||
rgb_attrs.foreground = aep->fg_color;
|
||||
}
|
||||
|
||||
if (aep->bg_color != normal_bg) {
|
||||
rgb_attrs.background = aep->bg_color;
|
||||
}
|
||||
|
||||
if (cterm_normal_fg_color != aep->ae_u.cterm.fg_color) {
|
||||
cterm_attrs.foreground = aep->ae_u.cterm.fg_color - 1;
|
||||
}
|
||||
|
||||
if (cterm_normal_bg_color != aep->ae_u.cterm.bg_color) {
|
||||
cterm_attrs.background = aep->ae_u.cterm.bg_color - 1;
|
||||
}
|
||||
}
|
||||
|
||||
UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs));
|
||||
}
|
||||
|
||||
static void parse_abstract_ui_codes(uint8_t *ptr, int len)
|
||||
{
|
||||
if (!ui_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
int arg1 = 0, arg2 = 0;
|
||||
uint8_t *end = ptr + len, *p, c;
|
||||
bool update_cursor = false;
|
||||
@ -444,6 +503,9 @@ static void parse_abstract_ui_codes(uint8_t *ptr, int len)
|
||||
UI_CALL(put, NULL, 0);
|
||||
col++;
|
||||
}
|
||||
if (col >= width) {
|
||||
ui_linefeed();
|
||||
}
|
||||
p += clen;
|
||||
}
|
||||
ptr = p;
|
||||
@ -457,25 +519,6 @@ static void parse_abstract_ui_codes(uint8_t *ptr, int len)
|
||||
UI_CALL(flush);
|
||||
}
|
||||
|
||||
static void resized(UI *ui)
|
||||
{
|
||||
check_dimensions(ui);
|
||||
screen_resize(width, height, true);
|
||||
}
|
||||
|
||||
static void check_dimensions(UI *ui)
|
||||
{
|
||||
// The internal screen dimensions are always the minimum required to fit on
|
||||
// all connected screens
|
||||
if (ui->width < width) {
|
||||
width = ui->width;
|
||||
}
|
||||
|
||||
if (ui->height < height) {
|
||||
height = ui->height;
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_linefeed(void)
|
||||
{
|
||||
int new_col = 0;
|
||||
|
@ -6,13 +6,14 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
bool bold, standout, underline, undercurl, italic, reverse;
|
||||
bool bold, underline, undercurl, italic, reverse;
|
||||
int foreground, background;
|
||||
} HlAttrs;
|
||||
|
||||
typedef struct ui_t UI;
|
||||
|
||||
struct ui_t {
|
||||
bool rgb;
|
||||
int width, height;
|
||||
void *data;
|
||||
void (*resize)(UI *ui, int rows, int columns);
|
||||
@ -32,7 +33,11 @@ struct ui_t {
|
||||
void (*bell)(UI *ui);
|
||||
void (*visual_bell)(UI *ui);
|
||||
void (*flush)(UI *ui);
|
||||
void (*update_fg)(UI *ui, int fg);
|
||||
void (*update_bg)(UI *ui, int bg);
|
||||
void (*suspend)(UI *ui);
|
||||
void (*set_title)(UI *ui, char *title);
|
||||
void (*set_icon)(UI *ui, char *icon);
|
||||
};
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
|
@ -23,18 +23,18 @@ describe('visual block shift and tab characters', function()
|
||||
abcdefghijklmnopqrstuvwxyz]])
|
||||
|
||||
feed('gg')
|
||||
feed([[fe<C-v>4jR<esc>ugvr1:'<,'>yank A<cr>]])
|
||||
feed([[fe<C-v>4jR<esc>ugvr1:'<lt>,'>yank A<cr>]])
|
||||
execute('/^abcdefgh')
|
||||
feed('<C-v>4jI <esc>j<<11|D')
|
||||
feed('<C-v>4jI <esc>j<lt><lt>11|D')
|
||||
feed('j7|a <esc>')
|
||||
feed('j7|a <esc>')
|
||||
feed('j7|a <esc>4k13|<C-v>4j<')
|
||||
feed('j7|a <esc>4k13|<C-v>4j<lt>')
|
||||
execute('$-5,$yank A')
|
||||
execute([[$-4,$s/\s\+//g]])
|
||||
feed('<C-v>4kI <esc>j<<')
|
||||
feed('<C-v>4kI <esc>j<lt><lt>')
|
||||
feed('j7|a <esc>')
|
||||
feed('j7|a <esc>')
|
||||
feed('j7|a <esc>4k13|<C-v>4j3<')
|
||||
feed('j7|a <esc>4k13|<C-v>4j3<lt>')
|
||||
execute('$-4,$yank A')
|
||||
|
||||
-- Put @a and clean empty lines
|
||||
|
@ -1,7 +1,30 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim
|
||||
local execute = helpers.execute
|
||||
local execute, request, eq = helpers.execute, helpers.request, helpers.eq
|
||||
|
||||
|
||||
describe('color scheme compatibility', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
it('t_Co is set to 256 by default', function()
|
||||
eq('256', request('vim_eval', '&t_Co'))
|
||||
request('vim_set_option', 't_Co', '88')
|
||||
eq('88', request('vim_eval', '&t_Co'))
|
||||
end)
|
||||
|
||||
it('emulates gui_running when a rgb UI is attached', function()
|
||||
eq(0, request('vim_eval', 'has("gui_running")'))
|
||||
local screen = Screen.new()
|
||||
screen:attach()
|
||||
eq(1, request('vim_eval', 'has("gui_running")'))
|
||||
screen:detach()
|
||||
eq(0, request('vim_eval', 'has("gui_running")'))
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
describe('Default highlight groups', function()
|
||||
-- Test the default attributes for highlight groups shown by the :highlight
|
||||
@ -24,7 +47,6 @@ describe('Default highlight groups', function()
|
||||
after_each(function()
|
||||
screen:detach()
|
||||
end)
|
||||
|
||||
it('window status bar', function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {reverse = true, bold = true}, -- StatusLine
|
||||
@ -142,7 +164,6 @@ describe('Default highlight groups', function()
|
||||
end)
|
||||
|
||||
it('end of file markers', function()
|
||||
nvim('command', 'hi Normal guibg=black')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|
|
||||
|
@ -1,6 +1,7 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim
|
||||
local insert, execute = helpers.insert, helpers.execute
|
||||
|
||||
describe('Mouse input', function()
|
||||
local screen, hlgroup_colors
|
||||
@ -154,4 +155,87 @@ describe('Mouse input', function()
|
||||
]])
|
||||
feed('<cr>')
|
||||
end)
|
||||
|
||||
it('mouse whell will target the hovered window', function()
|
||||
feed('ggdG')
|
||||
insert([[
|
||||
Inserting
|
||||
text
|
||||
with
|
||||
many
|
||||
lines
|
||||
to
|
||||
test
|
||||
mouse scrolling
|
||||
]])
|
||||
screen:try_resize(53, 14)
|
||||
execute('sp', 'vsp')
|
||||
screen:expect([[
|
||||
lines |lines |
|
||||
to |to |
|
||||
test |test |
|
||||
mouse scrolling |mouse scrolling |
|
||||
^ | |
|
||||
~ |~ |
|
||||
[No Name] [+] [No Name] [+] |
|
||||
to |
|
||||
test |
|
||||
mouse scrolling |
|
||||
|
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
:vsp |
|
||||
]])
|
||||
feed('<MouseUp><0,0>')
|
||||
screen:expect([[
|
||||
mouse scrolling |lines |
|
||||
^ |to |
|
||||
~ |test |
|
||||
~ |mouse scrolling |
|
||||
~ | |
|
||||
~ |~ |
|
||||
[No Name] [+] [No Name] [+] |
|
||||
to |
|
||||
test |
|
||||
mouse scrolling |
|
||||
|
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('<MouseDown><27,0>')
|
||||
screen:expect([[
|
||||
mouse scrolling |text |
|
||||
^ |with |
|
||||
~ |many |
|
||||
~ |lines |
|
||||
~ |to |
|
||||
~ |test |
|
||||
[No Name] [+] [No Name] [+] |
|
||||
to |
|
||||
test |
|
||||
mouse scrolling |
|
||||
|
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('<MouseDown><27,7><MouseDown>')
|
||||
screen:expect([[
|
||||
mouse scrolling |text |
|
||||
^ |with |
|
||||
~ |many |
|
||||
~ |lines |
|
||||
~ |to |
|
||||
~ |test |
|
||||
[No Name] [+] [No Name] [+] |
|
||||
Inserting |
|
||||
text |
|
||||
with |
|
||||
many |
|
||||
lines |
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
@ -83,6 +83,21 @@ local eq, dedent = helpers.eq, helpers.dedent
|
||||
local Screen = {}
|
||||
Screen.__index = Screen
|
||||
|
||||
local debug_screen
|
||||
|
||||
|
||||
function Screen.debug(command)
|
||||
if not command then
|
||||
command = 'pynvim -n -g -c '
|
||||
end
|
||||
command = command .. request('vim_eval', '$NVIM_LISTEN_ADDRESS')
|
||||
if debug_screen then
|
||||
debug_screen:close()
|
||||
end
|
||||
debug_screen = io.popen(command, 'r')
|
||||
debug_screen:read()
|
||||
end
|
||||
|
||||
function Screen.new(width, height)
|
||||
if not width then
|
||||
width = 53
|
||||
@ -90,24 +105,22 @@ function Screen.new(width, height)
|
||||
if not height then
|
||||
height = 14
|
||||
end
|
||||
return setmetatable({
|
||||
local self = setmetatable({
|
||||
title = '',
|
||||
icon = '',
|
||||
bell = false,
|
||||
visual_bell = false,
|
||||
suspended = false,
|
||||
_default_attr_ids = nil,
|
||||
_width = width,
|
||||
_height = height,
|
||||
_rows = new_cell_grid(width, height),
|
||||
_mode = 'normal',
|
||||
_mouse_enabled = true,
|
||||
_bell = false,
|
||||
_visual_bell = false,
|
||||
_suspended = true,
|
||||
_attrs = {},
|
||||
_cursor = {
|
||||
enabled = true, row = 1, col = 1
|
||||
},
|
||||
_scroll_region = {
|
||||
top = 1, bot = height, left = 1, right = width
|
||||
}
|
||||
}, Screen)
|
||||
self:_handle_resize(width, height)
|
||||
return self
|
||||
end
|
||||
|
||||
function Screen:set_default_attr_ids(attr_ids)
|
||||
@ -115,13 +128,15 @@ function Screen:set_default_attr_ids(attr_ids)
|
||||
end
|
||||
|
||||
function Screen:attach()
|
||||
request('attach_ui', self._width, self._height)
|
||||
self._suspended = false
|
||||
request('ui_attach', self._width, self._height, true)
|
||||
end
|
||||
|
||||
function Screen:detach()
|
||||
request('detach_ui')
|
||||
self._suspended = true
|
||||
request('ui_detach')
|
||||
end
|
||||
|
||||
function Screen:try_resize(columns, rows)
|
||||
request('ui_try_resize', columns, rows)
|
||||
end
|
||||
|
||||
function Screen:expect(expected, attr_ids)
|
||||
@ -134,7 +149,7 @@ function Screen:expect(expected, attr_ids)
|
||||
table.insert(expected_rows, row)
|
||||
end
|
||||
local ids = attr_ids or self._default_attr_ids
|
||||
self:_wait(function()
|
||||
self:wait(function()
|
||||
for i = 1, self._height do
|
||||
local expected_row = expected_rows[i]
|
||||
local actual_row = self:_row_repr(self._rows[i], ids)
|
||||
@ -146,7 +161,7 @@ function Screen:expect(expected, attr_ids)
|
||||
end)
|
||||
end
|
||||
|
||||
function Screen:_wait(check, timeout)
|
||||
function Screen:wait(check, timeout)
|
||||
local err, checked = false
|
||||
local function notification_cb(method, args)
|
||||
assert(method == 'redraw')
|
||||
@ -181,16 +196,30 @@ function Screen:_redraw(updates)
|
||||
end
|
||||
|
||||
function Screen:_handle_resize(width, height)
|
||||
self._rows = new_cell_grid(width, height)
|
||||
local rows = {}
|
||||
for i = 1, height do
|
||||
local cols = {}
|
||||
for j = 1, width do
|
||||
table.insert(cols, {text = ' ', attrs = {}})
|
||||
end
|
||||
table.insert(rows, cols)
|
||||
end
|
||||
self._rows = rows
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._scroll_region = {
|
||||
top = 1, bot = height, left = 1, right = width
|
||||
}
|
||||
end
|
||||
|
||||
function Screen:_handle_clear()
|
||||
self:_clear_block(1, self._height, 1, self._width)
|
||||
self:_clear_block(self._scroll_region.top, self._scroll_region.bot,
|
||||
self._scroll_region.left, self._scroll_region.right)
|
||||
end
|
||||
|
||||
function Screen:_handle_eol_clear()
|
||||
local row, col = self._cursor.row, self._cursor.col
|
||||
self:_clear_block(row, 1, col, self._width - col)
|
||||
self:_clear_block(row, 1, col, self._scroll_region.right - col)
|
||||
end
|
||||
|
||||
function Screen:_handle_cursor_goto(row, col)
|
||||
@ -250,11 +279,14 @@ function Screen:_handle_scroll(count)
|
||||
for i = start, stop, step do
|
||||
local target = self._rows[i]
|
||||
local source = self._rows[i + count]
|
||||
self:_copy_row_section(target, source, left, right)
|
||||
for j = left, right do
|
||||
target[j].text = source[j].text
|
||||
target[j].attrs = source[j].attrs
|
||||
end
|
||||
end
|
||||
|
||||
-- clear invalid rows
|
||||
for i = stop + 1, stop + count, step do
|
||||
for i = stop + step, stop + count, step do
|
||||
self:_clear_row_section(i, left, right)
|
||||
end
|
||||
end
|
||||
@ -271,15 +303,31 @@ function Screen:_handle_put(str)
|
||||
end
|
||||
|
||||
function Screen:_handle_bell()
|
||||
self._bell = true
|
||||
self.bell = true
|
||||
end
|
||||
|
||||
function Screen:_handle_visual_bell()
|
||||
self._visual_bell = true
|
||||
self.visual_bell = true
|
||||
end
|
||||
|
||||
function Screen:_handle_update_fg(fg)
|
||||
self._fg = fg
|
||||
end
|
||||
|
||||
function Screen:_handle_update_bg(bg)
|
||||
self._bg = bg
|
||||
end
|
||||
|
||||
function Screen:_handle_suspend()
|
||||
self._suspended = true
|
||||
self.suspended = true
|
||||
end
|
||||
|
||||
function Screen:_handle_set_title(title)
|
||||
self.title = title
|
||||
end
|
||||
|
||||
function Screen:_handle_set_icon(icon)
|
||||
self.icon = icon
|
||||
end
|
||||
|
||||
function Screen:_clear_block(top, lines, left, columns)
|
||||
@ -296,13 +344,6 @@ function Screen:_clear_row_section(rownum, startcol, stopcol)
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:_copy_row_section(target, source, startcol, stopcol)
|
||||
for i = startcol, stopcol do
|
||||
target[i].text = source[i].text
|
||||
target[i].attrs = source[i].attrs
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:_row_repr(row, attr_ids)
|
||||
local rv = {}
|
||||
local current_attr_id
|
||||
@ -353,18 +394,6 @@ function backward_find_meaningful(tbl, from)
|
||||
return from
|
||||
end
|
||||
|
||||
function new_cell_grid(width, height)
|
||||
local rows = {}
|
||||
for i = 1, height do
|
||||
local cols = {}
|
||||
for j = 1, width do
|
||||
table.insert(cols, {text = ' ', attrs = {}})
|
||||
end
|
||||
table.insert(rows, cols)
|
||||
end
|
||||
return rows
|
||||
end
|
||||
|
||||
function get_attr_id(attr_ids, attrs)
|
||||
if not attr_ids then
|
||||
return
|
||||
|
@ -16,6 +16,69 @@ describe('Screen', function()
|
||||
screen:detach()
|
||||
end)
|
||||
|
||||
describe(':suspend', function()
|
||||
it('is forwarded to the UI', function()
|
||||
local function check()
|
||||
if not screen.suspended then
|
||||
return 'Screen was not suspended'
|
||||
end
|
||||
end
|
||||
execute('suspend')
|
||||
screen:wait(check)
|
||||
screen.suspended = false
|
||||
feed('<c-z>')
|
||||
screen:wait(check)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('bell/visual bell', function()
|
||||
it('is forwarded to the UI', function()
|
||||
feed('<left>')
|
||||
screen:wait(function()
|
||||
if not screen.bell or screen.visual_bell then
|
||||
return 'Bell was not sent'
|
||||
end
|
||||
end)
|
||||
screen.bell = false
|
||||
execute('set visualbell')
|
||||
feed('<left>')
|
||||
screen:wait(function()
|
||||
if not screen.visual_bell or screen.bell then
|
||||
return 'Visual bell was not sent'
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':set title', function()
|
||||
it('is forwarded to the UI', function()
|
||||
local expected = 'test-title'
|
||||
execute('set titlestring='..expected)
|
||||
execute('set title')
|
||||
screen:wait(function()
|
||||
local actual = screen.title
|
||||
if actual ~= expected then
|
||||
return 'Expected title to be "'..expected..'" but was "'..actual..'"'
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':set icon', function()
|
||||
it('is forwarded to the UI', function()
|
||||
local expected = 'test-icon'
|
||||
execute('set iconstring='..expected)
|
||||
execute('set icon')
|
||||
screen:wait(function()
|
||||
local actual = screen.icon
|
||||
if actual ~= expected then
|
||||
return 'Expected title to be "'..expected..'" but was "'..actual..'"'
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
describe('window', function()
|
||||
describe('split', function()
|
||||
it('horizontal', function()
|
||||
@ -95,6 +158,8 @@ describe('Screen', function()
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -221,4 +286,193 @@ describe('Screen', function()
|
||||
feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('scrolling and clearing', function()
|
||||
before_each(function()
|
||||
insert([[
|
||||
Inserting
|
||||
text
|
||||
with
|
||||
many
|
||||
lines
|
||||
to
|
||||
test
|
||||
scrolling
|
||||
and
|
||||
clearing
|
||||
in
|
||||
split
|
||||
windows
|
||||
]])
|
||||
execute('sp', 'vsp', 'vsp')
|
||||
screen:expect([[
|
||||
and |and |and |
|
||||
clearing |clearing |clearing |
|
||||
in |in |in |
|
||||
split |split |split |
|
||||
windows |windows |windows |
|
||||
^ | | |
|
||||
[No Name] [+] [No Name] [+] [No Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('only affects the current scroll region', function()
|
||||
feed('6k')
|
||||
screen:expect([[
|
||||
^crolling |and |and |
|
||||
and |clearing |clearing |
|
||||
clearing |in |in |
|
||||
in |split |split |
|
||||
split |windows |windows |
|
||||
windows | | |
|
||||
[No Name] [+] [No Name] [+] [No Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('<c-w>l')
|
||||
screen:expect([[
|
||||
scrolling |and |and |
|
||||
and |clearing |clearing |
|
||||
clearing |in |in |
|
||||
in |split |split |
|
||||
split |windows |windows |
|
||||
windows |^ | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('gg')
|
||||
screen:expect([[
|
||||
scrolling |^nserting |and |
|
||||
and |text |clearing |
|
||||
clearing |with |in |
|
||||
in |many |split |
|
||||
split |lines |windows |
|
||||
windows |to | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('7j')
|
||||
screen:expect([[
|
||||
scrolling |with |and |
|
||||
and |many |clearing |
|
||||
clearing |lines |in |
|
||||
in |to |split |
|
||||
split |test |windows |
|
||||
windows |^crolling | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('2j')
|
||||
screen:expect([[
|
||||
scrolling |lines |and |
|
||||
and |to |clearing |
|
||||
clearing |test |in |
|
||||
in |scrolling |split |
|
||||
split |and |windows |
|
||||
windows |^learing | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('5k')
|
||||
screen:expect([[
|
||||
scrolling |^ines |and |
|
||||
and |to |clearing |
|
||||
clearing |test |in |
|
||||
in |scrolling |split |
|
||||
split |and |windows |
|
||||
windows |clearing | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
feed('k')
|
||||
screen:expect([[
|
||||
scrolling |^any |and |
|
||||
and |lines |clearing |
|
||||
clearing |to |in |
|
||||
in |test |split |
|
||||
split |scrolling |windows |
|
||||
windows |and | |
|
||||
[No Name] [+] [No Name] [+] <Name] [+] |
|
||||
clearing |
|
||||
in |
|
||||
split |
|
||||
windows |
|
||||
|
|
||||
[No Name] [+] |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('resize', function()
|
||||
before_each(function()
|
||||
screen:try_resize(25, 5)
|
||||
feed('iresize')
|
||||
end)
|
||||
|
||||
it('rebuilds the whole screen', function()
|
||||
screen:expect([[
|
||||
resize^ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
-- INSERT -- |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('has minimum width/height values', function()
|
||||
screen:try_resize(1, 1)
|
||||
screen:expect([[
|
||||
-- INS^RT --|
|
||||
|
|
||||
]])
|
||||
feed('<esc>:ls')
|
||||
screen:expect([[
|
||||
resize |
|
||||
:ls^ |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
6
third-party/CMakeLists.txt
vendored
6
third-party/CMakeLists.txt
vendored
@ -73,9 +73,9 @@ set(LIBTERMKEY_URL https://github.com/neovim/libtermkey/archive/7b3bdafdf589d084
|
||||
set(LIBTERMKEY_SHA1 28bfe54dfd9269910a132b51dee7725a2121578d)
|
||||
set(LIBTERMKEY_MD5 f0bac9c2467cc80c821be937ea5c13bc)
|
||||
|
||||
set(LIBTICKIT_URL https://github.com/neovim/libtickit/archive/0430ba2f43fdf1c31bca66def52a2537c581ade5.tar.gz)
|
||||
set(LIBTICKIT_SHA1 732b145a4dab06e6f1b40a352424f808730726bf)
|
||||
set(LIBTICKIT_MD5 3fcb635e572851472fc5009709d980fe)
|
||||
set(LIBTICKIT_URL https://github.com/neovim/libtickit/archive/33f4afb3891df05955429acbf5b406dfe87ec22b.tar.gz)
|
||||
set(LIBTICKIT_SHA1 3aab459b9fb3cd83e85ac2e08f05e5f162c8c9d2)
|
||||
set(LIBTICKIT_MD5 19ee9271c16716620d0906db74158ec6)
|
||||
|
||||
if(USE_BUNDLED_LIBUNIBILIUM)
|
||||
ExternalProject_Add(libunibilium
|
||||
|
Loading…
Reference in New Issue
Block a user