aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/provider.txt11
-rw-r--r--src/nvim/api/private/helpers.c31
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--test/functional/api/vim_spec.lua35
-rw-r--r--test/functional/terminal/tui_spec.lua58
-rw-r--r--test/helpers.lua9
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 = {...}