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.
|
// 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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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[];
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
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 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
|
||||||
|
|
||||||
|
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 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('…')
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user