paste: phases, dots

- Send `phase` parameter to the paste handler.
- Redraw at intervals and when paste terminates.
- Show "..." throbber during paste to indicate activity.
This commit is contained in:
Justin M. Keyes
2019-08-21 01:55:12 +02:00
parent 93e5f0235b
commit 5ae6849517
3 changed files with 63 additions and 33 deletions

View File

@@ -94,19 +94,39 @@ local function _os_proc_children(ppid)
end end
-- Default paste function. -- Default paste function.
local function _paste(lines) local _paste = (function()
-- local eof = (lines == {''}) local tdots = 0
local tredraw = 0
local tick = 0
return function(lines, phase)
local call = vim.api.nvim_call_function local call = vim.api.nvim_call_function
local mode = call('mode', {}) local now = vim.loop.now()
local curline = call('line', {'.'}) if phase == 1 then
-- vim.api.nvim_set_option('paste', true) tdots = now
tredraw = now
tick = 0
if (call('mode', {})):find('[vV]') then
vim.api.nvim_feedkeys('', 'n', false)
end
end
vim.api.nvim_put(lines, 'c', true, true) vim.api.nvim_put(lines, 'c', true, true)
-- vim.api.nvim_set_option('paste', false) if (now - tredraw >= 1000) or phase == 1 or phase == 3 then
-- TODO: do not redraw (slow!) until paste is finished. tredraw = now
-- if eof then
vim.api.nvim_command('redraw') vim.api.nvim_command('redraw')
vim.api.nvim_command('redrawstatus')
end
if (now - tdots >= 100) then
local dots = ('.'):rep(tick % 4)
tdots = now
tick = tick + 1
vim.api.nvim_command(('echo "%s"'):format(dots))
end
if phase == 3 then
vim.api.nvim_command('echo ""')
end
return true -- Paste will not continue if not returning `true`. return true -- Paste will not continue if not returning `true`.
end end
end)()
-- TODO(ZyX-I): Create compatibility layer. -- TODO(ZyX-I): Create compatibility layer.
--{{{1 package.path updater function --{{{1 package.path updater function

View File

@@ -25,7 +25,7 @@
void tinput_init(TermInput *input, Loop *loop) void tinput_init(TermInput *input, Loop *loop)
{ {
input->loop = loop; input->loop = loop;
input->paste_enabled = false; input->paste = 0;
input->in_fd = 0; input->in_fd = 0;
input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE); input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE);
uv_mutex_init(&input->key_buffer_mutex); uv_mutex_init(&input->key_buffer_mutex);
@@ -130,19 +130,20 @@ static void tinput_wait_enqueue(void **argv)
TermInput *input = argv[0]; TermInput *input = argv[0];
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
const String keys = { .data = buf, .size = len }; const String keys = { .data = buf, .size = len };
if (input->paste_enabled) { if (input->paste) {
Object keys_array = ARRAY_OBJ(string_to_array(keys));
Array args = { .capacity = 1, .size = 1, .items = &keys_array };
Error err = ERROR_INIT; Error err = ERROR_INIT;
Object fret Array args = ARRAY_DICT_INIT;
ADD(args, ARRAY_OBJ(string_to_array(keys)));
ADD(args, INTEGER_OBJ(input->paste));
Object rv
= nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"), = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"),
args, &err); args, &err);
if (fret.type != kObjectTypeBoolean || !fret.data.boolean) { input->paste = (rv.type == kObjectTypeBoolean && rv.data.boolean)
// Abort paste if handler does not return true. ? 2 // Paste phase: "continue".
input->paste_enabled = false; : 0; // Abort paste if handler does not return true.
}
api_free_object(fret); api_free_object(rv);
api_free_object(keys_array); api_free_array(args);
rbuffer_consumed(input->key_buffer, len); rbuffer_consumed(input->key_buffer, len);
rbuffer_reset(input->key_buffer); rbuffer_reset(input->key_buffer);
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
@@ -392,18 +393,27 @@ static bool handle_bracketed_paste(TermInput *input)
&& (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6)
|| !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { || !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) {
bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0';
if (input->paste_enabled && enable) { if (input->paste && enable) {
// Pasting "enable paste" code literally. return false; // Pasting "start paste" code literally.
return false;
} }
// Advance past the sequence // Advance past the sequence
rbuffer_consumed(input->read_stream.buffer, 6); rbuffer_consumed(input->read_stream.buffer, 6);
if (input->paste_enabled == enable) { if (!!input->paste == enable) {
return true; return true; // Spurious "disable paste" code.
} }
if (enable) {
// Flush before starting paste.
tinput_flush(input, true); tinput_flush(input, true);
input->paste_enabled = enable; // Paste phase: "first-chunk".
input->paste = 1;
} else {
// Paste phase: "last-chunk".
input->paste = 3;
tinput_flush(input, true);
// Paste phase: "disabled".
input->paste = 0;
}
return true; return true;
} }
return false; return false;
@@ -548,7 +558,7 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_,
} }
} }
// Push bytes directly (paste). // Push bytes directly (paste).
if (input->paste_enabled) { if (input->paste) {
RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) {
size_t consumed = MIN(count, len); size_t consumed = MIN(count, len);
assert(consumed <= input->read_stream.buffer->size); assert(consumed <= input->read_stream.buffer->size);

View File

@@ -9,7 +9,7 @@
typedef struct term_input { typedef struct term_input {
int in_fd; int in_fd;
bool paste_enabled; uint8_t paste; // Phases: 0=disabled 1=first-chunk 2=continue 3=last-chunk
bool waiting; bool waiting;
TermKey *tk; TermKey *tk;
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 #if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18