mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge PR #2007 'Fixes for the new TUI'
This commit is contained in:
commit
366662d932
@ -148,22 +148,34 @@ size_t input_enqueue(String keys)
|
|||||||
uint8_t buf[6] = {0};
|
uint8_t buf[6] = {0};
|
||||||
unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true);
|
unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true);
|
||||||
|
|
||||||
if (!new_size) {
|
if (new_size) {
|
||||||
if (*ptr == '<') {
|
new_size = handle_mouse_event(&ptr, buf, new_size);
|
||||||
// Invalid key sequence, skip until the next '>' or until *end
|
rbuffer_write(input_buffer, (char *)buf, new_size);
|
||||||
do {
|
continue;
|
||||||
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);
|
if (*ptr == '<') {
|
||||||
rbuffer_write(input_buffer, (char *)buf, new_size);
|
// Invalid key sequence, skip until the next '>' or until *end
|
||||||
|
do {
|
||||||
|
ptr++;
|
||||||
|
} while (ptr < end && *ptr != '>');
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the character, escaping CSI and K_SPECIAL
|
||||||
|
if ((uint8_t)*ptr == CSI) {
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){K_SPECIAL}, 1);
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){KS_EXTRA}, 1);
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){KE_CSI}, 1);
|
||||||
|
} else if ((uint8_t)*ptr == K_SPECIAL) {
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){K_SPECIAL}, 1);
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){KS_SPECIAL}, 1);
|
||||||
|
rbuffer_write(input_buffer, (char *)&(uint8_t){KE_FILLER}, 1);
|
||||||
|
} else {
|
||||||
|
rbuffer_write(input_buffer, ptr, 1);
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t rv = (size_t)(ptr - keys.data);
|
size_t rv = (size_t)(ptr - keys.data);
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#include <termkey.h>
|
#include <termkey.h>
|
||||||
|
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
|
#include "nvim/misc2.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/rstream.h"
|
#include "nvim/os/rstream.h"
|
||||||
|
|
||||||
|
#define PASTETOGGLE_KEY "<f37>"
|
||||||
|
|
||||||
struct term_input {
|
struct term_input {
|
||||||
int in_fd;
|
int in_fd;
|
||||||
@ -113,7 +115,7 @@ static int get_key_code_timeout(void)
|
|||||||
bool timeout = false;
|
bool timeout = false;
|
||||||
// Check 'timeout' and 'ttimeout' to determine if we should send ESC
|
// Check 'timeout' and 'ttimeout' to determine if we should send ESC
|
||||||
// after 'ttimeoutlen'. See :help 'ttimeout' for more information
|
// after 'ttimeoutlen'. See :help 'ttimeout' for more information
|
||||||
Error err;
|
Error err = ERROR_INIT;
|
||||||
timeout = vim_get_option(cstr_as_string("timeout"), &err).data.boolean;
|
timeout = vim_get_option(cstr_as_string("timeout"), &err).data.boolean;
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
timeout = vim_get_option(cstr_as_string("ttimeout"), &err).data.boolean;
|
timeout = vim_get_option(cstr_as_string("ttimeout"), &err).data.boolean;
|
||||||
@ -164,6 +166,49 @@ static void timer_cb(uv_timer_t *handle)
|
|||||||
tk_getkeys(handle->data, true);
|
tk_getkeys(handle->data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool handle_bracketed_paste(TermInput *input)
|
||||||
|
{
|
||||||
|
char *ptr = rbuffer_read_ptr(input->read_buffer);
|
||||||
|
size_t len = rbuffer_pending(input->read_buffer);
|
||||||
|
if (len > 5 && (!strncmp(ptr, "\x1b[200~", 6)
|
||||||
|
|| !strncmp(ptr, "\x1b[201~", 6))) {
|
||||||
|
bool enable = ptr[4] == '0';
|
||||||
|
// Advance past the sequence
|
||||||
|
rbuffer_consumed(input->read_buffer, 6);
|
||||||
|
if (enable) {
|
||||||
|
// Get the current mode
|
||||||
|
int state = get_real_state();
|
||||||
|
if (state & NORMAL) {
|
||||||
|
// Enter insert mode
|
||||||
|
input_enqueue(cstr_as_string("i"));
|
||||||
|
} else if (state & VISUAL) {
|
||||||
|
// Remove the selected text and enter insert mode
|
||||||
|
input_enqueue(cstr_as_string("c"));
|
||||||
|
} else if (!(state & INSERT)) {
|
||||||
|
// Don't mess with the paste option
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input_enqueue(cstr_as_string(PASTETOGGLE_KEY));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_forced_escape(TermInput *input)
|
||||||
|
{
|
||||||
|
char *ptr = rbuffer_read_ptr(input->read_buffer);
|
||||||
|
size_t len = rbuffer_pending(input->read_buffer);
|
||||||
|
if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) {
|
||||||
|
// skip the ESC and NUL and push one <esc> to the input buffer
|
||||||
|
termkey_push_bytes(input->tk, ptr, 1);
|
||||||
|
rbuffer_consumed(input->read_buffer, 2);
|
||||||
|
tk_getkeys(input, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void read_cb(RStream *rstream, void *rstream_data, bool eof)
|
static void read_cb(RStream *rstream, void *rstream_data, bool eof)
|
||||||
{
|
{
|
||||||
if (eof) {
|
if (eof) {
|
||||||
@ -174,15 +219,11 @@ static void read_cb(RStream *rstream, void *rstream_data, bool eof)
|
|||||||
TermInput *input = rstream_data;
|
TermInput *input = rstream_data;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
char *ptr = rbuffer_read_ptr(input->read_buffer);
|
if (handle_bracketed_paste(input) || handle_forced_escape(input)) {
|
||||||
size_t len = rbuffer_pending(input->read_buffer);
|
|
||||||
if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) {
|
|
||||||
// skip the ESC and NUL and push one <esc> to the input buffer
|
|
||||||
termkey_push_bytes(input->tk, ptr, 1);
|
|
||||||
rbuffer_consumed(input->read_buffer, 2);
|
|
||||||
tk_getkeys(input, true);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
char *ptr = rbuffer_read_ptr(input->read_buffer);
|
||||||
|
size_t len = rbuffer_pending(input->read_buffer);
|
||||||
// Find the next 'esc' and push everything up to it(excluding)
|
// Find the next 'esc' and push everything up to it(excluding)
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) {
|
for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) {
|
||||||
@ -228,6 +269,11 @@ static TermInput *term_input_new(void)
|
|||||||
// initialize a timer handle for handling ESC with libtermkey
|
// initialize a timer handle for handling ESC with libtermkey
|
||||||
uv_timer_init(uv_default_loop(), &rv->timer_handle);
|
uv_timer_init(uv_default_loop(), &rv->timer_handle);
|
||||||
rv->timer_handle.data = rv;
|
rv->timer_handle.data = rv;
|
||||||
|
// Set the pastetoggle option to a special key that will be sent when
|
||||||
|
// \e[20{0,1}~/ are received
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
vim_set_option(cstr_as_string("pastetoggle"),
|
||||||
|
STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ typedef struct {
|
|||||||
Cell **screen;
|
Cell **screen;
|
||||||
struct {
|
struct {
|
||||||
size_t enable_mouse, disable_mouse;
|
size_t enable_mouse, disable_mouse;
|
||||||
|
size_t enable_bracketed_paste, disable_bracketed_paste;
|
||||||
|
size_t enter_insert_mode, exit_insert_mode;
|
||||||
} unibi_ext;
|
} unibi_ext;
|
||||||
} TUIData;
|
} TUIData;
|
||||||
|
|
||||||
@ -101,8 +103,10 @@ void tui_start(void)
|
|||||||
}
|
}
|
||||||
fix_terminfo(data);
|
fix_terminfo(data);
|
||||||
// Enter alternate screen and clear
|
// Enter alternate screen and clear
|
||||||
unibi_out(ui, unibi_enter_ca_mode, NULL);
|
unibi_out(ui, unibi_enter_ca_mode);
|
||||||
unibi_out(ui, unibi_clear_screen, NULL);
|
unibi_out(ui, unibi_clear_screen);
|
||||||
|
// Enable bracketed paste
|
||||||
|
unibi_out(ui, (int)data->unibi_ext.enable_bracketed_paste);
|
||||||
|
|
||||||
// setup output handle in a separate event loop(we wanna do synchronous
|
// setup output handle in a separate event loop(we wanna do synchronous
|
||||||
// write to the tty)
|
// write to the tty)
|
||||||
@ -159,9 +163,11 @@ static void tui_stop(UI *ui)
|
|||||||
// Destroy output stuff
|
// Destroy output stuff
|
||||||
tui_normal_mode(ui);
|
tui_normal_mode(ui);
|
||||||
tui_mouse_off(ui);
|
tui_mouse_off(ui);
|
||||||
unibi_out(ui, unibi_exit_attribute_mode, NULL);
|
unibi_out(ui, unibi_exit_attribute_mode);
|
||||||
unibi_out(ui, unibi_cursor_normal, NULL);
|
unibi_out(ui, unibi_cursor_normal);
|
||||||
unibi_out(ui, unibi_exit_ca_mode, NULL);
|
unibi_out(ui, unibi_exit_ca_mode);
|
||||||
|
// Disable bracketed paste
|
||||||
|
unibi_out(ui, (int)data->unibi_ext.disable_bracketed_paste);
|
||||||
flush_buf(ui);
|
flush_buf(ui);
|
||||||
uv_close((uv_handle_t *)&data->output_handle, NULL);
|
uv_close((uv_handle_t *)&data->output_handle, NULL);
|
||||||
uv_run(data->write_loop, UV_RUN_DEFAULT);
|
uv_run(data->write_loop, UV_RUN_DEFAULT);
|
||||||
@ -177,8 +183,8 @@ static void tui_stop(UI *ui)
|
|||||||
pmap_free(cstr_t)(data->option_cache);
|
pmap_free(cstr_t)(data->option_cache);
|
||||||
destroy_screen(data);
|
destroy_screen(data);
|
||||||
free(data);
|
free(data);
|
||||||
free(ui);
|
|
||||||
ui_detach(ui);
|
ui_detach(ui);
|
||||||
|
free(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void try_resize(Event ev)
|
static void try_resize(Event ev)
|
||||||
@ -208,39 +214,41 @@ static bool attrs_differ(HlAttrs a1, HlAttrs a2)
|
|||||||
static void update_attrs(UI *ui, HlAttrs attrs)
|
static void update_attrs(UI *ui, HlAttrs attrs)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
unibi_out(ui, unibi_exit_attribute_mode, NULL);
|
|
||||||
|
if (!attrs_differ(attrs, data->print_attrs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->print_attrs = attrs;
|
||||||
|
unibi_out(ui, unibi_exit_attribute_mode);
|
||||||
|
|
||||||
data->params[0].i = attrs.foreground != -1 ? attrs.foreground : data->fg;
|
data->params[0].i = attrs.foreground != -1 ? attrs.foreground : data->fg;
|
||||||
if (data->params[0].i != -1) {
|
if (data->params[0].i != -1) {
|
||||||
unibi_out(ui, unibi_set_a_foreground, NULL);
|
unibi_out(ui, unibi_set_a_foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->params[0].i = attrs.background != -1 ? attrs.background : data->bg;
|
data->params[0].i = attrs.background != -1 ? attrs.background : data->bg;
|
||||||
if (data->params[0].i != -1) {
|
if (data->params[0].i != -1) {
|
||||||
unibi_out(ui, unibi_set_a_background, NULL);
|
unibi_out(ui, unibi_set_a_background);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.bold) {
|
if (attrs.bold) {
|
||||||
unibi_out(ui, unibi_enter_bold_mode, NULL);
|
unibi_out(ui, unibi_enter_bold_mode);
|
||||||
}
|
}
|
||||||
if (attrs.italic) {
|
if (attrs.italic) {
|
||||||
unibi_out(ui, unibi_enter_italics_mode, NULL);
|
unibi_out(ui, unibi_enter_italics_mode);
|
||||||
}
|
}
|
||||||
if (attrs.underline) {
|
if (attrs.underline) {
|
||||||
unibi_out(ui, unibi_enter_underline_mode, NULL);
|
unibi_out(ui, unibi_enter_underline_mode);
|
||||||
}
|
}
|
||||||
if (attrs.reverse) {
|
if (attrs.reverse) {
|
||||||
unibi_out(ui, unibi_enter_reverse_mode, NULL);
|
unibi_out(ui, unibi_enter_reverse_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_cell(UI *ui, Cell *ptr)
|
static void print_cell(UI *ui, Cell *ptr)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
update_attrs(ui, ptr->attrs);
|
||||||
if (attrs_differ(ptr->attrs, data->print_attrs)) {
|
|
||||||
update_attrs(ui, ptr->attrs);
|
|
||||||
data->print_attrs = ptr->attrs;
|
|
||||||
}
|
|
||||||
out(ui, ptr->data);
|
out(ui, ptr->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +259,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,
|
|||||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
HlAttrs clear_attrs = EMPTY_ATTRS;
|
||||||
clear_attrs.foreground = data->fg;
|
clear_attrs.foreground = data->fg;
|
||||||
clear_attrs.background = data->bg;
|
clear_attrs.background = data->bg;
|
||||||
|
update_attrs(ui, clear_attrs);
|
||||||
|
|
||||||
bool cleared = false;
|
bool cleared = false;
|
||||||
if (refresh && data->bg == -1 && right == ui->width -1) {
|
if (refresh && data->bg == -1 && right == ui->width -1) {
|
||||||
@ -259,10 +268,10 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,
|
|||||||
if (left == 0) {
|
if (left == 0) {
|
||||||
if (bot == ui->height - 1) {
|
if (bot == ui->height - 1) {
|
||||||
if (top == 0) {
|
if (top == 0) {
|
||||||
unibi_out(ui, unibi_clear_screen, NULL);
|
unibi_out(ui, unibi_clear_screen);
|
||||||
} else {
|
} else {
|
||||||
unibi_goto(ui, top, 0);
|
unibi_goto(ui, top, 0);
|
||||||
unibi_out(ui, unibi_clr_eos, NULL);
|
unibi_out(ui, unibi_clr_eos);
|
||||||
}
|
}
|
||||||
cleared = true;
|
cleared = true;
|
||||||
}
|
}
|
||||||
@ -272,7 +281,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,
|
|||||||
// iterate through each line and clear with clr_eol
|
// iterate through each line and clear with clr_eol
|
||||||
for (int row = top; row <= bot; ++row) {
|
for (int row = top; row <= bot; ++row) {
|
||||||
unibi_goto(ui, row, left);
|
unibi_goto(ui, row, left);
|
||||||
unibi_out(ui, unibi_clr_eol, NULL);
|
unibi_out(ui, unibi_clr_eol);
|
||||||
}
|
}
|
||||||
cleared = true;
|
cleared = true;
|
||||||
}
|
}
|
||||||
@ -334,34 +343,36 @@ static void tui_cursor_goto(UI *ui, int row, int col)
|
|||||||
|
|
||||||
static void tui_cursor_on(UI *ui)
|
static void tui_cursor_on(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, unibi_cursor_normal, NULL);
|
unibi_out(ui, unibi_cursor_normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_cursor_off(UI *ui)
|
static void tui_cursor_off(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, unibi_cursor_invisible, NULL);
|
unibi_out(ui, unibi_cursor_invisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_mouse_on(UI *ui)
|
static void tui_mouse_on(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
unibi_out(ui, (int)data->unibi_ext.enable_mouse, NULL);
|
unibi_out(ui, (int)data->unibi_ext.enable_mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_mouse_off(UI *ui)
|
static void tui_mouse_off(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
unibi_out(ui, (int)data->unibi_ext.disable_mouse, NULL);
|
unibi_out(ui, (int)data->unibi_ext.disable_mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_insert_mode(UI *ui)
|
static void tui_insert_mode(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, -1, "t_SI");
|
TUIData *data = ui->data;
|
||||||
|
unibi_out(ui, (int)data->unibi_ext.enter_insert_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_normal_mode(UI *ui)
|
static void tui_normal_mode(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, -1, "t_EI");
|
TUIData *data = ui->data;
|
||||||
|
unibi_out(ui, (int)data->unibi_ext.exit_insert_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
|
static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
|
||||||
@ -391,7 +402,7 @@ static void tui_scroll(UI *ui, int count)
|
|||||||
// Change terminal scroll region and move cursor to the top
|
// Change terminal scroll region and move cursor to the top
|
||||||
data->params[0].i = top;
|
data->params[0].i = top;
|
||||||
data->params[1].i = bot;
|
data->params[1].i = bot;
|
||||||
unibi_out(ui, unibi_change_scroll_region, NULL);
|
unibi_out(ui, unibi_change_scroll_region);
|
||||||
unibi_goto(ui, top, left);
|
unibi_goto(ui, top, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,10 +415,10 @@ static void tui_scroll(UI *ui, int count)
|
|||||||
step = 1;
|
step = 1;
|
||||||
if (data->can_use_terminal_scroll) {
|
if (data->can_use_terminal_scroll) {
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
unibi_out(ui, unibi_delete_line, NULL);
|
unibi_out(ui, unibi_delete_line);
|
||||||
} else {
|
} else {
|
||||||
data->params[0].i = count;
|
data->params[0].i = count;
|
||||||
unibi_out(ui, unibi_parm_delete_line, NULL);
|
unibi_out(ui, unibi_parm_delete_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,10 +428,10 @@ static void tui_scroll(UI *ui, int count)
|
|||||||
step = -1;
|
step = -1;
|
||||||
if (data->can_use_terminal_scroll) {
|
if (data->can_use_terminal_scroll) {
|
||||||
if (count == -1) {
|
if (count == -1) {
|
||||||
unibi_out(ui, unibi_insert_line, NULL);
|
unibi_out(ui, unibi_insert_line);
|
||||||
} else {
|
} else {
|
||||||
data->params[0].i = -count;
|
data->params[0].i = -count;
|
||||||
unibi_out(ui, unibi_parm_insert_line, NULL);
|
unibi_out(ui, unibi_parm_insert_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,7 +440,7 @@ static void tui_scroll(UI *ui, int count)
|
|||||||
// Restore terminal scroll region and cursor
|
// Restore terminal scroll region and cursor
|
||||||
data->params[0].i = 0;
|
data->params[0].i = 0;
|
||||||
data->params[1].i = ui->height - 1;
|
data->params[1].i = ui->height - 1;
|
||||||
unibi_out(ui, unibi_change_scroll_region, NULL);
|
unibi_out(ui, unibi_change_scroll_region);
|
||||||
unibi_goto(ui, data->row, data->col);
|
unibi_goto(ui, data->row, data->col);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,12 +491,12 @@ static void tui_put(UI *ui, uint8_t *text, size_t size)
|
|||||||
|
|
||||||
static void tui_bell(UI *ui)
|
static void tui_bell(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, unibi_bell, NULL);
|
unibi_out(ui, unibi_bell);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_visual_bell(UI *ui)
|
static void tui_visual_bell(UI *ui)
|
||||||
{
|
{
|
||||||
unibi_out(ui, unibi_flash_screen, NULL);
|
unibi_out(ui, unibi_flash_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_update_fg(UI *ui, int fg)
|
static void tui_update_fg(UI *ui, int fg)
|
||||||
@ -527,9 +538,9 @@ static void tui_set_title(UI *ui, char *title)
|
|||||||
&& unibi_get_str(data->ut, unibi_from_status_line))) {
|
&& unibi_get_str(data->ut, unibi_from_status_line))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unibi_out(ui, unibi_to_status_line, NULL);
|
unibi_out(ui, unibi_to_status_line);
|
||||||
out(ui, title);
|
out(ui, title);
|
||||||
unibi_out(ui, unibi_from_status_line, NULL);
|
unibi_out(ui, unibi_from_status_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_set_icon(UI *ui, char *icon)
|
static void tui_set_icon(UI *ui, char *icon)
|
||||||
@ -613,23 +624,19 @@ static void unibi_goto(UI *ui, int row, int col)
|
|||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
data->params[0].i = row;
|
data->params[0].i = row;
|
||||||
data->params[1].i = col;
|
data->params[1].i = col;
|
||||||
unibi_out(ui, unibi_cursor_address, NULL);
|
unibi_out(ui, unibi_cursor_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unibi_out(UI *ui, int unibi_index, char *nvim_override)
|
static void unibi_out(UI *ui, int unibi_index)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
|
|
||||||
const char *str = NULL;
|
const char *str = NULL;
|
||||||
|
|
||||||
if (nvim_override) {
|
if (unibi_index < unibi_string_begin_) {
|
||||||
str = get_term_option(ui, nvim_override);
|
str = unibi_get_ext_str(data->ut, (unsigned)unibi_index);
|
||||||
} else if (unibi_index >= 0) {
|
} else {
|
||||||
if (unibi_index < unibi_string_begin_) {
|
str = unibi_get_str(data->ut, (unsigned)unibi_index);
|
||||||
str = unibi_get_ext_str(data->ut, (unsigned)unibi_index);
|
|
||||||
} else {
|
|
||||||
str = unibi_get_str(data->ut, (unsigned)unibi_index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str) {
|
if (str) {
|
||||||
@ -662,12 +669,17 @@ static void fix_terminfo(TUIData *data)
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool inside_tmux = os_getenv("TMUX") != NULL;
|
||||||
|
|
||||||
#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
|
#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
|
||||||
|
|
||||||
if (STARTS_WITH(term, "rxvt")) {
|
if (STARTS_WITH(term, "rxvt")) {
|
||||||
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B");
|
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B");
|
||||||
unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l");
|
unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l");
|
||||||
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
|
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
|
||||||
|
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2");
|
||||||
|
} else if (STARTS_WITH(term, "xterm")) {
|
||||||
|
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
|
||||||
} else if (STARTS_WITH(term, "screen")) {
|
} else if (STARTS_WITH(term, "screen")) {
|
||||||
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
|
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
|
||||||
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
|
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
|
||||||
@ -680,17 +692,45 @@ static void fix_terminfo(TUIData *data)
|
|||||||
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m");
|
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m");
|
||||||
unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr");
|
unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr");
|
||||||
unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J");
|
unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J");
|
||||||
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2");
|
|
||||||
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
|
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt") || inside_tmux) {
|
||||||
|
data->unibi_ext.enable_bracketed_paste = unibi_add_ext_str(ut, NULL,
|
||||||
|
"\x1b[?2004h");
|
||||||
|
data->unibi_ext.disable_bracketed_paste = unibi_add_ext_str(ut, NULL,
|
||||||
|
"\x1b[?2004l");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE") == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
|
||||||
|
// Support changing cursor shape on some popular terminals.
|
||||||
|
const char *term_prog = os_getenv("TERM_PROGRAM");
|
||||||
|
|
||||||
|
if ((term_prog && !strcmp(term_prog, "iTerm.app"))
|
||||||
|
|| os_getenv("ITERM_SESSION_ID") != NULL) {
|
||||||
|
// iterm
|
||||||
|
data->unibi_ext.enter_insert_mode = unibi_add_ext_str(ut, NULL,
|
||||||
|
TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07"));
|
||||||
|
data->unibi_ext.exit_insert_mode = unibi_add_ext_str(ut, NULL,
|
||||||
|
TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07"));
|
||||||
|
} else {
|
||||||
|
// xterm-like sequences for blinking bar and solid block
|
||||||
|
data->unibi_ext.enter_insert_mode = unibi_add_ext_str(ut, NULL,
|
||||||
|
TMUX_WRAP("\x1b[5 q"));
|
||||||
|
data->unibi_ext.exit_insert_mode = unibi_add_ext_str(ut, NULL,
|
||||||
|
TMUX_WRAP("\x1b[2 q"));
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
// Fill some empty slots with common terminal strings
|
// Fill some empty slots with common terminal strings
|
||||||
data->unibi_ext.enable_mouse = unibi_add_ext_str(ut, NULL,
|
data->unibi_ext.enable_mouse = unibi_add_ext_str(ut, NULL,
|
||||||
"\x1b[?1002h\x1b[?1006h");
|
"\x1b[?1002h\x1b[?1006h");
|
||||||
data->unibi_ext.disable_mouse = unibi_add_ext_str(ut, NULL,
|
data->unibi_ext.disable_mouse = unibi_add_ext_str(ut, NULL,
|
||||||
"\x1b[?1002l\x1b[?1006l");
|
"\x1b[?1002l\x1b[?1006l");
|
||||||
|
|
||||||
unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH");
|
unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH");
|
||||||
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m");
|
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m");
|
||||||
unibi_set_if_empty(ut, unibi_set_a_foreground,
|
unibi_set_if_empty(ut, unibi_set_a_foreground,
|
||||||
@ -724,23 +764,6 @@ static void flush_buf(UI *ui)
|
|||||||
data->bufpos = 0;
|
data->bufpos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_term_option(UI *ui, char *option)
|
|
||||||
{
|
|
||||||
TUIData *data = ui->data;
|
|
||||||
|
|
||||||
char *rv = pmap_get(cstr_t)(data->option_cache, option);
|
|
||||||
if (!rv) {
|
|
||||||
Error err;
|
|
||||||
Object val = vim_get_option(cstr_as_string(option), &err);
|
|
||||||
if (val.type == kObjectTypeString) {
|
|
||||||
rv = val.data.string.data;
|
|
||||||
pmap_put(cstr_t)(data->option_cache, option, rv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_screen(TUIData *data)
|
static void destroy_screen(TUIData *data)
|
||||||
{
|
{
|
||||||
if (data->screen) {
|
if (data->screen) {
|
||||||
|
@ -16,7 +16,7 @@ describe('mapping', function()
|
|||||||
|
|
||||||
-- Abbreviations with р (0x80) should work.
|
-- Abbreviations with р (0x80) should work.
|
||||||
execute('inoreab чкпр vim')
|
execute('inoreab чкпр vim')
|
||||||
feed('GAчкпр <cr><esc>')
|
feed('GAчкпр <esc>')
|
||||||
|
|
||||||
-- langmap should not get remapped in insert mode.
|
-- langmap should not get remapped in insert mode.
|
||||||
execute('inoremap { FAIL_ilangmap')
|
execute('inoremap { FAIL_ilangmap')
|
||||||
@ -27,10 +27,11 @@ describe('mapping', function()
|
|||||||
execute('inoremap <expr> { "FAIL_iexplangmap"')
|
execute('inoremap <expr> { "FAIL_iexplangmap"')
|
||||||
feed('o+<esc>')
|
feed('o+<esc>')
|
||||||
|
|
||||||
|
|
||||||
-- Assert buffer contents.
|
-- Assert buffer contents.
|
||||||
expect([[
|
expect([[
|
||||||
test starts here:
|
test starts here:
|
||||||
vim
|
vim
|
||||||
+
|
+
|
||||||
+]])
|
+]])
|
||||||
end)
|
end)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
local helpers = require('test.functional.helpers')
|
local helpers = require('test.functional.helpers')
|
||||||
local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim
|
local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim
|
||||||
local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq
|
local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq
|
||||||
|
local expect = helpers.expect
|
||||||
|
|
||||||
describe('mappings', function()
|
describe('mappings', function()
|
||||||
local cid
|
local cid
|
||||||
@ -38,3 +39,10 @@ describe('mappings', function()
|
|||||||
check_mapping('<a-s-c-up>', '<c-s-a-up>')
|
check_mapping('<a-s-c-up>', '<c-s-a-up>')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('input utf sequences that contain CSI/K_SPECIAL', function()
|
||||||
|
it('ok', function()
|
||||||
|
feed('i…<esc>')
|
||||||
|
expect('…')
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user