mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge PR #3360 'More fixes for 0.1'
This commit is contained in:
commit
536c0ba27e
@ -1246,8 +1246,9 @@ do_shell (
|
||||
// 1" command to the terminal.
|
||||
ui_cursor_goto(msg_row, msg_col);
|
||||
(void)call_shell(cmd, flags, NULL);
|
||||
did_check_timestamps = FALSE;
|
||||
need_check_timestamps = TRUE;
|
||||
msg_didout = true;
|
||||
did_check_timestamps = false;
|
||||
need_check_timestamps = true;
|
||||
|
||||
// put the message cursor at the end of the screen, avoids wait_return()
|
||||
// to overwrite the text that the external command showed
|
||||
|
@ -7624,7 +7624,7 @@ void update_topline_cursor(void)
|
||||
*/
|
||||
static void ex_normal(exarg_T *eap)
|
||||
{
|
||||
if (curbuf->terminal) {
|
||||
if (curbuf->terminal && State & TERM_FOCUS) {
|
||||
EMSG("Can't re-enter normal mode from terminal mode");
|
||||
return;
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ getcmdline (
|
||||
input_enable_events();
|
||||
do {
|
||||
c = safe_vgetc();
|
||||
} while (c == K_IGNORE);
|
||||
} while (c == K_IGNORE || c == K_PASTE);
|
||||
input_disable_events();
|
||||
|
||||
if (c == K_EVENT) {
|
||||
|
@ -153,6 +153,7 @@ static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */
|
||||
static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */
|
||||
|
||||
static int last_recorded_len = 0; /* number of last recorded chars */
|
||||
static const uint8_t ui_toggle[] = { K_SPECIAL, KS_EXTRA, KE_PASTE, 0 };
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "getchar.c.generated.h"
|
||||
@ -1873,14 +1874,15 @@ static int vgetorpeek(int advance)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for match with 'pastetoggle' */
|
||||
if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
|
||||
for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen];
|
||||
++mlen)
|
||||
if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off
|
||||
+ mlen])
|
||||
break;
|
||||
if (p_pt[mlen] == NUL) { /* match */
|
||||
// Check for a key that can toggle the 'paste' option
|
||||
if (mp == NULL && (State & (INSERT|NORMAL))) {
|
||||
bool match = typebuf_match_len(ui_toggle, &mlen);
|
||||
if (!match && mlen != typebuf.tb_len && *p_pt != NUL) {
|
||||
// didn't match ui_toggle_key and didn't try the whole typebuf,
|
||||
// check the 'pastetoggle'
|
||||
match = typebuf_match_len(p_pt, &mlen);
|
||||
}
|
||||
if (match) {
|
||||
/* write chars to script file(s) */
|
||||
if (mlen > typebuf.tb_maplen)
|
||||
gotchars(typebuf.tb_buf + typebuf.tb_off
|
||||
@ -4238,3 +4240,14 @@ static char_u * translate_mapping (
|
||||
ga_append(&ga, NUL);
|
||||
return (char_u *)(ga.ga_data);
|
||||
}
|
||||
|
||||
static bool typebuf_match_len(const uint8_t *str, int *mlen)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < typebuf.tb_len && str[i]; i++) {
|
||||
if (str[i] != typebuf.tb_buf[typebuf.tb_off + i])
|
||||
break;
|
||||
}
|
||||
*mlen = i;
|
||||
return str[i] == NUL; // matched the whole string
|
||||
}
|
||||
|
@ -284,6 +284,7 @@ static struct key_name_entry {
|
||||
{K_SNR, (char_u *)"SNR"},
|
||||
{K_PLUG, (char_u *)"Plug"},
|
||||
{K_CURSORHOLD, (char_u *)"CursorHold"},
|
||||
{K_PASTE, (char_u *)"Paste"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
|
@ -247,6 +247,8 @@ enum key_extra {
|
||||
, KE_FOCUSGAINED /* focus gained */
|
||||
, KE_FOCUSLOST /* focus lost */
|
||||
, KE_EVENT // event
|
||||
, KE_PASTE // special key to toggle the 'paste' option.
|
||||
// sent only by UIs
|
||||
};
|
||||
|
||||
/*
|
||||
@ -437,6 +439,7 @@ enum key_extra {
|
||||
|
||||
#define K_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD)
|
||||
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
|
||||
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)
|
||||
|
||||
/* Bits for modifier mask */
|
||||
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */
|
||||
|
@ -171,10 +171,17 @@ size_t input_enqueue(String keys)
|
||||
}
|
||||
|
||||
if (*ptr == '<') {
|
||||
// Invalid key sequence, skip until the next '>' or until *end
|
||||
char *old_ptr = ptr;
|
||||
// Invalid or incomplete key sequence, skip until the next '>' or until
|
||||
// *end
|
||||
do {
|
||||
ptr++;
|
||||
} while (ptr < end && *ptr != '>');
|
||||
if (*ptr != '>') {
|
||||
// Incomplete key sequence, return without consuming.
|
||||
ptr = old_ptr;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
@ -24,11 +24,13 @@ RBuffer *rbuffer_new(size_t capacity)
|
||||
rv->size = 0;
|
||||
rv->write_ptr = rv->read_ptr = rv->start_ptr;
|
||||
rv->end_ptr = rv->start_ptr + capacity;
|
||||
rv->temp = NULL;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void rbuffer_free(RBuffer *buf)
|
||||
{
|
||||
xfree(buf->temp);
|
||||
xfree(buf);
|
||||
}
|
||||
|
||||
@ -69,12 +71,20 @@ char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL
|
||||
return buf->write_ptr;
|
||||
}
|
||||
|
||||
// Set read and write pointer for an empty RBuffer to the beginning of the
|
||||
// buffer.
|
||||
// Reset an RBuffer so read_ptr is at the beginning of the memory. If
|
||||
// necessary, this moves existing data by allocating temporary memory.
|
||||
void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (buf->size == 0) {
|
||||
buf->write_ptr = buf->read_ptr = buf->start_ptr;
|
||||
size_t temp_size;
|
||||
if ((temp_size = rbuffer_size(buf))) {
|
||||
if (buf->temp == NULL) {
|
||||
buf->temp = xmalloc(rbuffer_capacity(buf));
|
||||
}
|
||||
rbuffer_read(buf, buf->temp, buf->size);
|
||||
}
|
||||
buf->read_ptr = buf->write_ptr = buf->start_ptr;
|
||||
if (temp_size) {
|
||||
rbuffer_write(buf, buf->temp, temp_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,8 @@ struct rbuffer {
|
||||
rbuffer_callback full_cb, nonfull_cb;
|
||||
void *data;
|
||||
size_t size;
|
||||
// helper memory used to by rbuffer_reset if required
|
||||
char *temp;
|
||||
char *end_ptr, *read_ptr, *write_ptr;
|
||||
char start_ptr[];
|
||||
};
|
||||
|
@ -9,7 +9,8 @@
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
|
||||
#define PASTETOGGLE_KEY "<f37>"
|
||||
#define PASTETOGGLE_KEY "<Paste>"
|
||||
#define KEY_BUFFER_SIZE 0xfff
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "tui/input.c.generated.h"
|
||||
@ -20,6 +21,9 @@ void term_input_init(TermInput *input, Loop *loop)
|
||||
input->loop = loop;
|
||||
input->paste_enabled = false;
|
||||
input->in_fd = 0;
|
||||
input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE);
|
||||
uv_mutex_init(&input->key_buffer_mutex);
|
||||
uv_cond_init(&input->key_buffer_cond);
|
||||
|
||||
const char *term = os_getenv("TERM");
|
||||
if (!term) {
|
||||
@ -34,15 +38,13 @@ void term_input_init(TermInput *input, Loop *loop)
|
||||
rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff, input);
|
||||
// initialize a timer handle for handling ESC with libtermkey
|
||||
time_watcher_init(loop, &input->timer_handle, input);
|
||||
// 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);
|
||||
}
|
||||
|
||||
void term_input_destroy(TermInput *input)
|
||||
{
|
||||
rbuffer_free(input->key_buffer);
|
||||
uv_mutex_destroy(&input->key_buffer_mutex);
|
||||
uv_cond_destroy(&input->key_buffer_cond);
|
||||
time_watcher_close(&input->timer_handle, NULL);
|
||||
stream_close(&input->read_stream, NULL);
|
||||
termkey_destroy(input->tk);
|
||||
@ -59,25 +61,56 @@ void term_input_stop(TermInput *input)
|
||||
time_watcher_stop(&input->timer_handle);
|
||||
}
|
||||
|
||||
static void input_enqueue_event(void **argv)
|
||||
{
|
||||
char *buf = argv[0];
|
||||
input_enqueue(cstr_as_string(buf));
|
||||
xfree(buf);
|
||||
}
|
||||
|
||||
static void input_done_event(void **argv)
|
||||
{
|
||||
input_done();
|
||||
}
|
||||
|
||||
static void enqueue_input(char *buf, size_t size)
|
||||
static void wait_input_enqueue(void **argv)
|
||||
{
|
||||
loop_schedule(&loop, event_create(1, input_enqueue_event, 1,
|
||||
xstrndup(buf, size)));
|
||||
TermInput *input = argv[0];
|
||||
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
|
||||
size_t consumed = input_enqueue((String){.data = buf, .size = len});
|
||||
if (consumed) {
|
||||
rbuffer_consumed(input->key_buffer, consumed);
|
||||
}
|
||||
rbuffer_reset(input->key_buffer);
|
||||
if (consumed < len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uv_mutex_lock(&input->key_buffer_mutex);
|
||||
input->waiting = false;
|
||||
uv_cond_signal(&input->key_buffer_cond);
|
||||
uv_mutex_unlock(&input->key_buffer_mutex);
|
||||
}
|
||||
|
||||
static void forward_simple_utf8(TermKeyKey *key)
|
||||
static void flush_input(TermInput *input, bool wait_until_empty)
|
||||
{
|
||||
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
||||
do {
|
||||
uv_mutex_lock(&input->key_buffer_mutex);
|
||||
loop_schedule(&loop, event_create(1, wait_input_enqueue, 1, input));
|
||||
input->waiting = true;
|
||||
while (input->waiting) {
|
||||
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
||||
}
|
||||
uv_mutex_unlock(&input->key_buffer_mutex);
|
||||
} while (rbuffer_size(input->key_buffer) > drain_boundary);
|
||||
}
|
||||
|
||||
static void enqueue_input(TermInput *input, char *buf, size_t size)
|
||||
{
|
||||
if (rbuffer_size(input->key_buffer) >
|
||||
rbuffer_capacity(input->key_buffer) - 0xff) {
|
||||
// don't ever let the buffer get too full or we risk putting incomplete keys
|
||||
// into it
|
||||
flush_input(input, false);
|
||||
}
|
||||
rbuffer_write(input->key_buffer, buf, size);
|
||||
}
|
||||
|
||||
static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
|
||||
{
|
||||
size_t len = 0;
|
||||
char buf[64];
|
||||
@ -92,11 +125,10 @@ static void forward_simple_utf8(TermKeyKey *key)
|
||||
ptr++;
|
||||
}
|
||||
|
||||
buf[len] = 0;
|
||||
enqueue_input(buf, len);
|
||||
enqueue_input(input, buf, len);
|
||||
}
|
||||
|
||||
static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
|
||||
static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
|
||||
{
|
||||
size_t len;
|
||||
char buf[64];
|
||||
@ -105,19 +137,19 @@ static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
|
||||
&& key->code.sym == TERMKEY_SYM_ESCAPE) {
|
||||
len = (size_t)snprintf(buf, sizeof(buf), "<Esc>");
|
||||
} else {
|
||||
len = termkey_strfkey(tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
|
||||
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
|
||||
}
|
||||
|
||||
enqueue_input(buf, len);
|
||||
enqueue_input(input, buf, len);
|
||||
}
|
||||
|
||||
static void forward_mouse_event(TermKey *tk, TermKeyKey *key)
|
||||
static void forward_mouse_event(TermInput *input, TermKeyKey *key)
|
||||
{
|
||||
char buf[64];
|
||||
size_t len = 0;
|
||||
int button, row, col;
|
||||
TermKeyMouseEvent ev;
|
||||
termkey_interpret_mouse(tk, key, &ev, &button, &row, &col);
|
||||
termkey_interpret_mouse(input->tk, key, &ev, &button, &row, &col);
|
||||
|
||||
if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) {
|
||||
return;
|
||||
@ -159,7 +191,7 @@ static void forward_mouse_event(TermKey *tk, TermKeyKey *key)
|
||||
}
|
||||
|
||||
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
|
||||
enqueue_input(buf, len);
|
||||
enqueue_input(input, buf, len);
|
||||
}
|
||||
|
||||
static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
|
||||
@ -189,17 +221,17 @@ static void tk_getkeys(TermInput *input, bool force)
|
||||
|
||||
while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
|
||||
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
|
||||
forward_simple_utf8(&key);
|
||||
forward_simple_utf8(input, &key);
|
||||
} else if (key.type == TERMKEY_TYPE_UNICODE ||
|
||||
key.type == TERMKEY_TYPE_FUNCTION ||
|
||||
key.type == TERMKEY_TYPE_KEYSYM) {
|
||||
forward_modified_utf8(input->tk, &key);
|
||||
forward_modified_utf8(input, &key);
|
||||
} else if (key.type == TERMKEY_TYPE_MOUSE) {
|
||||
forward_mouse_event(input->tk, &key);
|
||||
forward_mouse_event(input, &key);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != TERMKEY_RES_AGAIN) {
|
||||
if (result != TERMKEY_RES_AGAIN || input->paste_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,21 +262,7 @@ static bool handle_bracketed_paste(TermInput *input)
|
||||
if (input->paste_enabled == enable) {
|
||||
return true;
|
||||
}
|
||||
if (enable) {
|
||||
// Get the current mode
|
||||
int state = get_real_state();
|
||||
if (state & NORMAL) {
|
||||
// Enter insert mode
|
||||
enqueue_input("i", 1);
|
||||
} else if (state & VISUAL) {
|
||||
// Remove the selected text and enter insert mode
|
||||
enqueue_input("c", 1);
|
||||
} else if (!(state & INSERT)) {
|
||||
// Don't mess with the paste option
|
||||
return true;
|
||||
}
|
||||
}
|
||||
enqueue_input(PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
|
||||
enqueue_input(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
|
||||
input->paste_enabled = enable;
|
||||
return true;
|
||||
}
|
||||
@ -326,7 +344,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
|
||||
}
|
||||
}
|
||||
} while (rbuffer_size(input->read_stream.buffer));
|
||||
|
||||
flush_input(input, true);
|
||||
// Make sure the next input escape sequence fits into the ring buffer
|
||||
// without wrap around, otherwise it could be misinterpreted.
|
||||
rbuffer_reset(input->read_stream.buffer);
|
||||
|
@ -10,10 +10,14 @@
|
||||
typedef struct term_input {
|
||||
int in_fd;
|
||||
bool paste_enabled;
|
||||
bool waiting;
|
||||
TermKey *tk;
|
||||
TimeWatcher timer_handle;
|
||||
Loop *loop;
|
||||
Stream read_stream;
|
||||
RBuffer *key_buffer;
|
||||
uv_mutex_t key_buffer_mutex;
|
||||
uv_cond_t key_buffer_cond;
|
||||
} TermInput;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
|
@ -372,5 +372,6 @@ return {
|
||||
set_session = set_session,
|
||||
write_file = write_file,
|
||||
rmdir = rmdir,
|
||||
mkdir = lfs.mkdir,
|
||||
exc_exec = exc_exec,
|
||||
}
|
||||
|
49
test/functional/shell/bang_filter_spec.lua
Normal file
49
test/functional/shell/bang_filter_spec.lua
Normal file
@ -0,0 +1,49 @@
|
||||
-- Specs for bang/filter commands
|
||||
|
||||
local helpers = require('test.functional.helpers')
|
||||
local feed, execute, clear = helpers.feed, helpers.execute, helpers.clear
|
||||
local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir
|
||||
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
|
||||
describe('issues', function()
|
||||
local screen
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
rmdir('bang_filter_spec')
|
||||
mkdir('bang_filter_spec')
|
||||
write_file('bang_filter_spec/f1', 'f1')
|
||||
write_file('bang_filter_spec/f2', 'f2')
|
||||
write_file('bang_filter_spec/f3', 'f3')
|
||||
screen = Screen.new()
|
||||
screen:attach()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
rmdir('bang_filter_spec')
|
||||
end)
|
||||
|
||||
it('#3269 Last line of shell output is not truncated', function()
|
||||
execute([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]])
|
||||
feed([[\l]])
|
||||
screen:expect([[
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
:!ls bang_filter_spec |
|
||||
|
|
||||
f1 |
|
||||
f2 |
|
||||
f3 |
|
||||
Press ENTER or type command to continue^ |
|
||||
]])
|
||||
end)
|
||||
|
||||
end)
|
@ -1,7 +1,7 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local nvim_dir = helpers.nvim_dir
|
||||
local execute, nvim = helpers.execute, helpers.nvim
|
||||
local execute, nvim, wait = helpers.execute, helpers.nvim, helpers.wait
|
||||
|
||||
local function feed_data(data)
|
||||
nvim('set_var', 'term_data', data)
|
||||
@ -31,13 +31,15 @@ local function clear_attrs() feed_termcode('[0;10m') end
|
||||
local function enable_mouse() feed_termcode('[?1002h') end
|
||||
local function disable_mouse() feed_termcode('[?1002l') end
|
||||
|
||||
local default_command = '["'..nvim_dir..'/tty-test'..'"]'
|
||||
|
||||
|
||||
local function screen_setup(extra_height)
|
||||
local function screen_setup(extra_height, command)
|
||||
nvim('command', 'highlight TermCursor cterm=reverse')
|
||||
nvim('command', 'highlight TermCursorNC ctermbg=11')
|
||||
nvim('set_var', 'terminal_scrollback_buffer_size', 10)
|
||||
if not extra_height then extra_height = 0 end
|
||||
if not command then command = default_command end
|
||||
local screen = Screen.new(50, 7 + extra_height)
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {reverse = true}, -- focused cursor
|
||||
@ -56,25 +58,29 @@ local function screen_setup(extra_height)
|
||||
-- tty-test puts the terminal into raw mode and echoes all input. tests are
|
||||
-- done by feeding it with terminfo codes to control the display and
|
||||
-- verifying output with screen:expect.
|
||||
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
||||
-- wait for "tty ready" to be printed before each test or the terminal may
|
||||
-- still be in canonical mode(will echo characters for example)
|
||||
--
|
||||
local empty_line = ' '
|
||||
local expected = {
|
||||
'tty ready ',
|
||||
'{1: } ',
|
||||
empty_line,
|
||||
empty_line,
|
||||
empty_line,
|
||||
empty_line,
|
||||
}
|
||||
for i = 1, extra_height do
|
||||
table.insert(expected, empty_line)
|
||||
end
|
||||
execute('enew | call termopen('..command..') | startinsert')
|
||||
if command == default_command then
|
||||
-- wait for "tty ready" to be printed before each test or the terminal may
|
||||
-- still be in canonical mode(will echo characters for example)
|
||||
--
|
||||
local empty_line = ' '
|
||||
local expected = {
|
||||
'tty ready ',
|
||||
'{1: } ',
|
||||
empty_line,
|
||||
empty_line,
|
||||
empty_line,
|
||||
empty_line,
|
||||
}
|
||||
for i = 1, extra_height do
|
||||
table.insert(expected, empty_line)
|
||||
end
|
||||
|
||||
table.insert(expected, '-- TERMINAL -- ')
|
||||
screen:expect(table.concat(expected, '\n'))
|
||||
table.insert(expected, '-- TERMINAL -- ')
|
||||
screen:expect(table.concat(expected, '\n'))
|
||||
else
|
||||
wait()
|
||||
end
|
||||
return screen
|
||||
end
|
||||
|
||||
|
106
test/functional/terminal/tui_spec.lua
Normal file
106
test/functional/terminal/tui_spec.lua
Normal file
@ -0,0 +1,106 @@
|
||||
-- Some sanity checks for the TUI using the builtin terminal emulator
|
||||
-- as a simple way to send keys and assert screen state.
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local helpers = require('test.functional.helpers')
|
||||
local thelpers = require('test.functional.terminal.helpers')
|
||||
local feed = thelpers.feed_data
|
||||
local execute = helpers.execute
|
||||
|
||||
describe('tui', function()
|
||||
local screen
|
||||
|
||||
before_each(function()
|
||||
helpers.clear()
|
||||
screen = thelpers.screen_setup(0, '["'..helpers.nvim_prog..'", "-u", "NONE", "--cmd", "set noswapfile"]')
|
||||
screen.timeout = 30000 -- pasting can be really slow in the TUI
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
[No Name] |
|
||||
|
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
screen:detach()
|
||||
end)
|
||||
|
||||
it('accepts basic utf-8 input', function()
|
||||
feed('iabc\ntest1\ntest2')
|
||||
screen:expect([[
|
||||
abc |
|
||||
test1 |
|
||||
test2{1: } |
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
-- INSERT -- |
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
feed('\x1b')
|
||||
screen:expect([[
|
||||
abc |
|
||||
test1 |
|
||||
test{1:2} |
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
|
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('automatically sends <Paste> for bracketed paste sequences', function()
|
||||
feed('i\x1b[200~')
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
[No Name] |
|
||||
-- INSERT (paste) -- |
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
feed('pasted from terminal')
|
||||
screen:expect([[
|
||||
pasted from terminal{1: } |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
-- INSERT (paste) -- |
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
feed('\x1b[201~')
|
||||
screen:expect([[
|
||||
pasted from terminal{1: } |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
[No Name] [+] |
|
||||
-- INSERT -- |
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can handle arbitrarily long bursts of input', function()
|
||||
execute('set ruler')
|
||||
local t = {}
|
||||
for i = 1, 3000 do
|
||||
t[i] = 'item ' .. tostring(i)
|
||||
end
|
||||
feed('i\x1b[200~')
|
||||
feed(table.concat(t, '\n'))
|
||||
feed('\x1b[201~')
|
||||
screen:expect([[
|
||||
item 2997 |
|
||||
item 2998 |
|
||||
item 2999 |
|
||||
item 3000{1: } |
|
||||
[No Name] [+] 3000,10 Bot|
|
||||
-- INSERT -- |
|
||||
-- TERMINAL -- |
|
||||
]])
|
||||
end)
|
||||
end)
|
@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')
|
||||
local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim
|
||||
local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq
|
||||
local expect = helpers.expect
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
describe('mappings', function()
|
||||
local cid
|
||||
@ -40,7 +41,76 @@ describe('mappings', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('feeding large chunks of input with <Paste>', function()
|
||||
local screen
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = Screen.new()
|
||||
screen:attach()
|
||||
execute('set ruler')
|
||||
end)
|
||||
|
||||
it('ok', function()
|
||||
local t = {}
|
||||
for i = 1, 20000 do
|
||||
t[i] = 'item ' .. tostring(i)
|
||||
end
|
||||
feed('i<Paste>')
|
||||
screen:expect([[
|
||||
^ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
-- INSERT (paste) -- |
|
||||
]])
|
||||
feed(table.concat(t, '<Enter>'))
|
||||
screen:expect([[
|
||||
item 19988 |
|
||||
item 19989 |
|
||||
item 19990 |
|
||||
item 19991 |
|
||||
item 19992 |
|
||||
item 19993 |
|
||||
item 19994 |
|
||||
item 19995 |
|
||||
item 19996 |
|
||||
item 19997 |
|
||||
item 19998 |
|
||||
item 19999 |
|
||||
item 20000^ |
|
||||
-- INSERT (paste) -- |
|
||||
]])
|
||||
feed('<Paste>')
|
||||
screen:expect([[
|
||||
item 19988 |
|
||||
item 19989 |
|
||||
item 19990 |
|
||||
item 19991 |
|
||||
item 19992 |
|
||||
item 19993 |
|
||||
item 19994 |
|
||||
item 19995 |
|
||||
item 19996 |
|
||||
item 19997 |
|
||||
item 19998 |
|
||||
item 19999 |
|
||||
item 20000^ |
|
||||
-- INSERT -- 20000,11 Bot |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('input utf sequences that contain CSI/K_SPECIAL', function()
|
||||
before_each(clear)
|
||||
it('ok', function()
|
||||
feed('i…<esc>')
|
||||
expect('…')
|
||||
|
@ -163,6 +163,7 @@ function Screen.new(width, height)
|
||||
height = 14
|
||||
end
|
||||
local self = setmetatable({
|
||||
timeout = default_screen_timeout,
|
||||
title = '',
|
||||
icon = '',
|
||||
bell = false,
|
||||
@ -248,7 +249,7 @@ function Screen:wait(check, timeout)
|
||||
|
||||
return true
|
||||
end
|
||||
run(nil, notification_cb, nil, timeout or default_screen_timeout)
|
||||
run(nil, notification_cb, nil, timeout or self.timeout)
|
||||
if not checked then
|
||||
err = check()
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user