diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2019-08-29 23:45:02 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-29 23:45:02 +0200 |
commit | 9f81acc076779f891160423657cc35e6ac37c3e6 (patch) | |
tree | 025dff4b1cfa364061b88d24ec5152725132453c | |
parent | 00d46f63286461eec4d7b2d7cae15fbe0d9cabdb (diff) | |
download | rneovim-9f81acc076779f891160423657cc35e6ac37c3e6.tar.gz rneovim-9f81acc076779f891160423657cc35e6ac37c3e6.tar.bz2 rneovim-9f81acc076779f891160423657cc35e6ac37c3e6.zip |
paste: break lines at CR, CRLF #10877
Some terminals helpfully translate \n to \r.
fix #10872
ref #10223
-rw-r--r-- | runtime/doc/provider.txt | 11 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 31 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 35 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 58 | ||||
-rw-r--r-- | test/helpers.lua | 9 |
6 files changed, 97 insertions, 49 deletions
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 833be8a103..0083bb63a4 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -239,13 +239,14 @@ GUIs can paste by calling |nvim_paste()|. PASTE BEHAVIOR ~ -Paste always inserts text after the cursor. In cmdline-mode only the first -line is pasted, to avoid accidentally executing many commands. Use the -|cmdline-window| if you really want to paste multiple lines to the cmdline. - -When pasting a huge amount of text, screen updates are throttled and the +Paste inserts text after the cursor. Lines break at <NL>, <CR>, and <CR><NL>. +When pasting a huge amount of text, screen-updates are throttled and the message area shows a "..." pulse. +In cmdline-mode only the first line is pasted, to avoid accidentally executing +many commands. Use the |cmdline-window| if you really want to paste multiple +lines to the cmdline. + You can implement a custom paste handler by redefining |vim.paste()|. Example: > diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3443f85e20..2e4874d7c6 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -745,19 +745,32 @@ String ga_take_string(garray_T *ga) return str; } -/// Creates "readfile()-style" ArrayOf(String). +/// Creates "readfile()-style" ArrayOf(String) from a binary string. /// -/// - NUL bytes are replaced with NL (form-feed). -/// - If last line ends with NL an extra empty list item is added. -Array string_to_array(const String input) +/// - Lines break at \n (NL/LF/line-feed). +/// - NUL bytes are replaced with NL. +/// - If the last byte is a linebreak an extra empty list item is added. +/// +/// @param input Binary string +/// @param crlf Also break lines at CR and CRLF. +/// @return [allocated] String array +Array string_to_array(const String input, bool crlf) { Array ret = ARRAY_DICT_INIT; for (size_t i = 0; i < input.size; i++) { const char *start = input.data + i; - const char *end = xmemscan(start, NL, input.size - i); - const size_t line_len = (size_t)(end - start); + const char *end = start; + size_t line_len = 0; + for (; line_len < input.size - i; line_len++) { + end = start + line_len; + if (*end == NL || (crlf && *end == CAR)) { + break; + } + } i += line_len; - + if (crlf && *end == CAR && i + 1 < input.size && *(end + 1) == NL) { + i += 1; // Advance past CRLF. + } String s = { .size = line_len, .data = xmemdupz(start, line_len), @@ -766,8 +779,8 @@ Array string_to_array(const String input) ADD(ret, STRING_OBJ(s)); // If line ends at end-of-buffer, add empty final item. // This is "readfile()-style", see also ":help channel-lines". - if (i + 1 == input.size && end[0] == NL) { - ADD(ret, STRING_OBJ(cchar_to_string(NUL))); + if (i + 1 == input.size && (*end == NL || (crlf && *end == CAR))) { + ADD(ret, STRING_OBJ(STRING_INIT)); } } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1ca0d8789d..8063a6f1fd 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1247,7 +1247,7 @@ Boolean nvim_paste(String data, Integer phase, Error *err) // Skip remaining chunks. Report error only once per "stream". goto theend; } - Array lines = string_to_array(data); + Array lines = string_to_array(data, true); ADD(args, ARRAY_OBJ(lines)); ADD(args, INTEGER_OBJ(phase)); rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args, diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 647fab5c43..cf7e479e15 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -381,8 +381,7 @@ describe('API', function() line 2 line 3 ]]) - -- Cursor follows the paste. - eq({0,4,1,0}, funcs.getpos('.')) + eq({0,4,1,0}, funcs.getpos('.')) -- Cursor follows the paste. eq(false, nvim('get_option', 'paste')) command('%delete _') -- Without final "\n". @@ -391,8 +390,38 @@ describe('API', function() line 1 line 2 line 3]]) - -- Cursor follows the paste. eq({0,3,6,0}, funcs.getpos('.')) + command('%delete _') + -- CRLF #10872 + nvim('paste', 'line 1\r\nline 2\r\nline 3\r\n', -1) + expect([[ + line 1 + line 2 + line 3 + ]]) + eq({0,4,1,0}, funcs.getpos('.')) + command('%delete _') + -- CRLF without final "\n". + nvim('paste', 'line 1\r\nline 2\r\nline 3\r', -1) + expect([[ + line 1 + line 2 + line 3 + ]]) + eq({0,4,1,0}, funcs.getpos('.')) + command('%delete _') + -- CRLF without final "\r\n". + nvim('paste', 'line 1\r\nline 2\r\nline 3', -1) + expect([[ + line 1 + line 2 + line 3]]) + eq({0,3,6,0}, funcs.getpos('.')) + command('%delete _') + -- Various other junk. + nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', -1) + expect('line 1\n\n\nline 2\nline 3\nline 4\n') + eq({0,7,1,0}, funcs.getpos('.')) eq(false, nvim('get_option', 'paste')) end) it('vim.paste() failure', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 5445ff0127..6ec6a41807 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -227,13 +227,24 @@ describe('TUI', function() expect_child_buf_lines({''}) end) - it('paste: normal-mode', function() + it('paste: normal-mode (+CRLF #10872)', function() feed_data(':set ruler') wait_for_mode('c') feed_data('\n') wait_for_mode('n') - local expected = {'line 1', ' line 2', 'ESC:\027 / CR: \013'} + local expected_lf = {'line 1', 'ESC:\027 / CR: \rx'} + local expected_crlf = {'line 1', 'ESC:\027 / CR: ', 'x'} + local expected_grid1 = [[ + line 1 | + ESC:{11:^[} / CR: | + {1:x} | + {4:~ }| + {5:[No Name] [+] 3,1 All}| + | + {3:-- TERMINAL --} | + ]] local expected_attr = { + [1] = {reverse = true}, [3] = {bold = true}, [4] = {foreground = tonumber('0x00000c')}, [5] = {bold = true, reverse = true}, @@ -241,36 +252,30 @@ describe('TUI', function() [12] = {reverse = true, foreground = tonumber('0x000051')}, } -- "bracketed paste" - feed_data('\027[200~'..table.concat(expected,'\n')..'\027[201~') - screen:expect{ - grid=[[ - line 1 | - line 2 | - ESC:{11:^[} / CR: {12:^}{11:M} | - {4:~ }| - {5:[No Name] [+] 3,13-14 All}| - | - {3:-- TERMINAL --} | - ]], - attr_ids=expected_attr} + feed_data('\027[200~'..table.concat(expected_lf,'\n')..'\027[201~') + screen:expect{grid=expected_grid1, attr_ids=expected_attr} -- Dot-repeat/redo. feed_data('.') screen:expect{ grid=[[ - line 2 | - ESC:{11:^[} / CR: {11:^M}line 1 | - line 2 | - ESC:{11:^[} / CR: {12:^}{11:M} | - {5:[No Name] [+] 5,13-14 Bot}| + ESC:{11:^[} / CR: | + xline 1 | + ESC:{11:^[} / CR: | + {1:x} | + {5:[No Name] [+] 5,1 Bot}| | {3:-- TERMINAL --} | ]], attr_ids=expected_attr} -- Undo. feed_data('u') - expect_child_buf_lines(expected) + expect_child_buf_lines(expected_crlf) feed_data('u') expect_child_buf_lines({''}) + -- CRLF input + feed_data('\027[200~'..table.concat(expected_lf,'\r\n')..'\027[201~') + screen:expect{grid=expected_grid1, attr_ids=expected_attr} + expect_child_buf_lines(expected_crlf) end) it('paste: cmdline-mode inserts 1 line', function() @@ -347,7 +352,7 @@ describe('TUI', function() ]]} -- Start pasting... feed_data('\027[200~line 1\nline 2\n') - wait_for_mode('n') + expect_child_buf_lines({'foo',''}) screen:expect{any='paste: Error executing lua'} -- Remaining chunks are discarded after vim.paste() failure. feed_data('line 3\nline 4\n') @@ -413,11 +418,6 @@ describe('TUI', function() ]]} end) - -- TODO - it('paste: other modes', function() - -- Other modes act like CTRL-C + paste. - end) - it('paste: exactly 64 bytes #10311', function() local expected = string.rep('z', 64) feed_data('i') @@ -523,10 +523,6 @@ describe('TUI', function() ]]) end) - -- TODO - it('paste: handles missing "stop paste" code', function() - end) - it('allows termguicolors to be set at runtime', function() screen:set_option('rgb', true) screen:set_default_attr_ids({ @@ -580,7 +576,7 @@ describe('TUI', function() end) it('is included in nvim_list_uis()', function() - feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(filter(v, {k,v -> k[:3] !=# "ext_" })))})\013') + feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(filter(v, {k,v -> k[:3] !=# "ext_" })))})\r') screen:expect([=[ | {4:~ }| diff --git a/test/helpers.lua b/test/helpers.lua index ce5e8b9c04..b571085b4e 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -455,6 +455,12 @@ local SUBTBL = { '\\030', '\\031', } +-- Formats Lua value `v`. +-- +-- TODO(justinmk): redundant with vim.inspect() ? +-- +-- "Nice table formatting similar to screen:snapshot_util()". +-- Commit: 520c0b91a528 function module.format_luav(v, indent, opts) opts = opts or {} local linesep = '\n' @@ -533,6 +539,9 @@ function module.format_luav(v, indent, opts) return ret end +-- Like Python repr(), "{!r}".format(s) +-- +-- Commit: 520c0b91a528 function module.format_string(fmt, ...) local i = 0 local args = {...} |