Merge PR #3360 'More fixes for 0.1'

This commit is contained in:
Thiago de Arruda 2015-10-01 15:37:20 -03:00
commit 536c0ba27e
17 changed files with 375 additions and 83 deletions

View File

@ -1246,8 +1246,9 @@ do_shell (
// 1" command to the terminal. // 1" command to the terminal.
ui_cursor_goto(msg_row, msg_col); ui_cursor_goto(msg_row, msg_col);
(void)call_shell(cmd, flags, NULL); (void)call_shell(cmd, flags, NULL);
did_check_timestamps = FALSE; msg_didout = true;
need_check_timestamps = TRUE; did_check_timestamps = false;
need_check_timestamps = true;
// put the message cursor at the end of the screen, avoids wait_return() // put the message cursor at the end of the screen, avoids wait_return()
// to overwrite the text that the external command showed // to overwrite the text that the external command showed

View File

@ -7624,7 +7624,7 @@ void update_topline_cursor(void)
*/ */
static void ex_normal(exarg_T *eap) 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"); EMSG("Can't re-enter normal mode from terminal mode");
return; return;
} }

View File

@ -302,7 +302,7 @@ getcmdline (
input_enable_events(); input_enable_events();
do { do {
c = safe_vgetc(); c = safe_vgetc();
} while (c == K_IGNORE); } while (c == K_IGNORE || c == K_PASTE);
input_disable_events(); input_disable_events();
if (c == K_EVENT) { if (c == K_EVENT) {

View File

@ -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 char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */
static int last_recorded_len = 0; /* number of last recorded chars */ 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 #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "getchar.c.generated.h" # include "getchar.c.generated.h"
@ -1873,14 +1874,15 @@ static int vgetorpeek(int advance)
} }
} }
/* Check for match with 'pastetoggle' */ // Check for a key that can toggle the 'paste' option
if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { if (mp == NULL && (State & (INSERT|NORMAL))) {
for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen]; bool match = typebuf_match_len(ui_toggle, &mlen);
++mlen) if (!match && mlen != typebuf.tb_len && *p_pt != NUL) {
if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off // didn't match ui_toggle_key and didn't try the whole typebuf,
+ mlen]) // check the 'pastetoggle'
break; match = typebuf_match_len(p_pt, &mlen);
if (p_pt[mlen] == NUL) { /* match */ }
if (match) {
/* write chars to script file(s) */ /* write chars to script file(s) */
if (mlen > typebuf.tb_maplen) if (mlen > typebuf.tb_maplen)
gotchars(typebuf.tb_buf + typebuf.tb_off gotchars(typebuf.tb_buf + typebuf.tb_off
@ -4238,3 +4240,14 @@ static char_u * translate_mapping (
ga_append(&ga, NUL); ga_append(&ga, NUL);
return (char_u *)(ga.ga_data); 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
}

View File

@ -284,6 +284,7 @@ static struct key_name_entry {
{K_SNR, (char_u *)"SNR"}, {K_SNR, (char_u *)"SNR"},
{K_PLUG, (char_u *)"Plug"}, {K_PLUG, (char_u *)"Plug"},
{K_CURSORHOLD, (char_u *)"CursorHold"}, {K_CURSORHOLD, (char_u *)"CursorHold"},
{K_PASTE, (char_u *)"Paste"},
{0, NULL} {0, NULL}
}; };

View File

@ -247,6 +247,8 @@ enum key_extra {
, KE_FOCUSGAINED /* focus gained */ , KE_FOCUSGAINED /* focus gained */
, KE_FOCUSLOST /* focus lost */ , KE_FOCUSLOST /* focus lost */
, KE_EVENT // event , 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_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD)
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT) #define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)
/* Bits for modifier mask */ /* Bits for modifier mask */
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */ /* 0x01 cannot be used, because the modifier must be 0x02 or higher */

View File

@ -171,10 +171,17 @@ size_t input_enqueue(String keys)
} }
if (*ptr == '<') { 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 { do {
ptr++; ptr++;
} while (ptr < end && *ptr != '>'); } while (ptr < end && *ptr != '>');
if (*ptr != '>') {
// Incomplete key sequence, return without consuming.
ptr = old_ptr;
break;
}
ptr++; ptr++;
continue; continue;
} }

View File

@ -24,11 +24,13 @@ RBuffer *rbuffer_new(size_t capacity)
rv->size = 0; rv->size = 0;
rv->write_ptr = rv->read_ptr = rv->start_ptr; rv->write_ptr = rv->read_ptr = rv->start_ptr;
rv->end_ptr = rv->start_ptr + capacity; rv->end_ptr = rv->start_ptr + capacity;
rv->temp = NULL;
return rv; return rv;
} }
void rbuffer_free(RBuffer *buf) void rbuffer_free(RBuffer *buf)
{ {
xfree(buf->temp);
xfree(buf); xfree(buf);
} }
@ -69,12 +71,20 @@ char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL
return buf->write_ptr; return buf->write_ptr;
} }
// Set read and write pointer for an empty RBuffer to the beginning of the // Reset an RBuffer so read_ptr is at the beginning of the memory. If
// buffer. // necessary, this moves existing data by allocating temporary memory.
void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
{ {
if (buf->size == 0) { size_t temp_size;
buf->write_ptr = buf->read_ptr = buf->start_ptr; 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);
} }
} }

View File

@ -72,6 +72,8 @@ struct rbuffer {
rbuffer_callback full_cb, nonfull_cb; rbuffer_callback full_cb, nonfull_cb;
void *data; void *data;
size_t size; size_t size;
// helper memory used to by rbuffer_reset if required
char *temp;
char *end_ptr, *read_ptr, *write_ptr; char *end_ptr, *read_ptr, *write_ptr;
char start_ptr[]; char start_ptr[];
}; };

View File

@ -9,7 +9,8 @@
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/event/rstream.h" #include "nvim/event/rstream.h"
#define PASTETOGGLE_KEY "<f37>" #define PASTETOGGLE_KEY "<Paste>"
#define KEY_BUFFER_SIZE 0xfff
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/input.c.generated.h" # include "tui/input.c.generated.h"
@ -20,6 +21,9 @@ void term_input_init(TermInput *input, Loop *loop)
input->loop = loop; input->loop = loop;
input->paste_enabled = false; input->paste_enabled = false;
input->in_fd = 0; 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"); const char *term = os_getenv("TERM");
if (!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); rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff, input);
// initialize a timer handle for handling ESC with libtermkey // initialize a timer handle for handling ESC with libtermkey
time_watcher_init(loop, &input->timer_handle, input); 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) 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); time_watcher_close(&input->timer_handle, NULL);
stream_close(&input->read_stream, NULL); stream_close(&input->read_stream, NULL);
termkey_destroy(input->tk); termkey_destroy(input->tk);
@ -59,25 +61,56 @@ void term_input_stop(TermInput *input)
time_watcher_stop(&input->timer_handle); 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) static void input_done_event(void **argv)
{ {
input_done(); 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, TermInput *input = argv[0];
xstrndup(buf, size))); 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; size_t len = 0;
char buf[64]; char buf[64];
@ -92,11 +125,10 @@ static void forward_simple_utf8(TermKeyKey *key)
ptr++; ptr++;
} }
buf[len] = 0; enqueue_input(input, buf, len);
enqueue_input(buf, len);
} }
static void forward_modified_utf8(TermKey *tk, TermKeyKey *key) static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
{ {
size_t len; size_t len;
char buf[64]; char buf[64];
@ -105,19 +137,19 @@ static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
&& key->code.sym == TERMKEY_SYM_ESCAPE) { && key->code.sym == TERMKEY_SYM_ESCAPE) {
len = (size_t)snprintf(buf, sizeof(buf), "<Esc>"); len = (size_t)snprintf(buf, sizeof(buf), "<Esc>");
} else { } 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]; char buf[64];
size_t len = 0; size_t len = 0;
int button, row, col; int button, row, col;
TermKeyMouseEvent ev; 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) { if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) {
return; 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); 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) 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) { while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
forward_simple_utf8(&key); forward_simple_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_UNICODE || } else if (key.type == TERMKEY_TYPE_UNICODE ||
key.type == TERMKEY_TYPE_FUNCTION || key.type == TERMKEY_TYPE_FUNCTION ||
key.type == TERMKEY_TYPE_KEYSYM) { key.type == TERMKEY_TYPE_KEYSYM) {
forward_modified_utf8(input->tk, &key); forward_modified_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_MOUSE) { } 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; return;
} }
@ -230,21 +262,7 @@ static bool handle_bracketed_paste(TermInput *input)
if (input->paste_enabled == enable) { if (input->paste_enabled == enable) {
return true; return true;
} }
if (enable) { enqueue_input(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
// 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);
input->paste_enabled = enable; input->paste_enabled = enable;
return true; 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)); } while (rbuffer_size(input->read_stream.buffer));
flush_input(input, true);
// Make sure the next input escape sequence fits into the ring buffer // Make sure the next input escape sequence fits into the ring buffer
// without wrap around, otherwise it could be misinterpreted. // without wrap around, otherwise it could be misinterpreted.
rbuffer_reset(input->read_stream.buffer); rbuffer_reset(input->read_stream.buffer);

View File

@ -10,10 +10,14 @@
typedef struct term_input { typedef struct term_input {
int in_fd; int in_fd;
bool paste_enabled; bool paste_enabled;
bool waiting;
TermKey *tk; TermKey *tk;
TimeWatcher timer_handle; TimeWatcher timer_handle;
Loop *loop; Loop *loop;
Stream read_stream; Stream read_stream;
RBuffer *key_buffer;
uv_mutex_t key_buffer_mutex;
uv_cond_t key_buffer_cond;
} TermInput; } TermInput;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@ -372,5 +372,6 @@ return {
set_session = set_session, set_session = set_session,
write_file = write_file, write_file = write_file,
rmdir = rmdir, rmdir = rmdir,
mkdir = lfs.mkdir,
exc_exec = exc_exec, exc_exec = exc_exec,
} }

View 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)

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers') local helpers = require('test.functional.helpers')
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local nvim_dir = helpers.nvim_dir 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) local function feed_data(data)
nvim('set_var', 'term_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 enable_mouse() feed_termcode('[?1002h') end
local function disable_mouse() feed_termcode('[?1002l') 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 TermCursor cterm=reverse')
nvim('command', 'highlight TermCursorNC ctermbg=11') nvim('command', 'highlight TermCursorNC ctermbg=11')
nvim('set_var', 'terminal_scrollback_buffer_size', 10) nvim('set_var', 'terminal_scrollback_buffer_size', 10)
if not extra_height then extra_height = 0 end 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) local screen = Screen.new(50, 7 + extra_height)
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {reverse = true}, -- focused cursor [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 -- 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 -- done by feeding it with terminfo codes to control the display and
-- verifying output with screen:expect. -- verifying output with screen:expect.
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert') execute('enew | call termopen('..command..') | startinsert')
-- wait for "tty ready" to be printed before each test or the terminal may if command == default_command then
-- still be in canonical mode(will echo characters for example) -- 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 = { local empty_line = ' '
'tty ready ', local expected = {
'{1: } ', 'tty ready ',
empty_line, '{1: } ',
empty_line, empty_line,
empty_line, empty_line,
empty_line, empty_line,
} empty_line,
for i = 1, extra_height do }
table.insert(expected, empty_line) for i = 1, extra_height do
end table.insert(expected, empty_line)
end
table.insert(expected, '-- TERMINAL -- ') table.insert(expected, '-- TERMINAL -- ')
screen:expect(table.concat(expected, '\n')) screen:expect(table.concat(expected, '\n'))
else
wait()
end
return screen return screen
end end

View 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)

View File

@ -2,6 +2,7 @@ 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 local expect = helpers.expect
local Screen = require('test.functional.ui.screen')
describe('mappings', function() describe('mappings', function()
local cid local cid
@ -40,7 +41,76 @@ describe('mappings', function()
end) end)
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() describe('input utf sequences that contain CSI/K_SPECIAL', function()
before_each(clear)
it('ok', function() it('ok', function()
feed('i…<esc>') feed('i…<esc>')
expect('') expect('')

View File

@ -163,6 +163,7 @@ function Screen.new(width, height)
height = 14 height = 14
end end
local self = setmetatable({ local self = setmetatable({
timeout = default_screen_timeout,
title = '', title = '',
icon = '', icon = '',
bell = false, bell = false,
@ -248,7 +249,7 @@ function Screen:wait(check, timeout)
return true return true
end end
run(nil, notification_cb, nil, timeout or default_screen_timeout) run(nil, notification_cb, nil, timeout or self.timeout)
if not checked then if not checked then
err = check() err = check()
end end