paste: break lines at CR, CRLF #10877
Some terminals helpfully translate \n to \r. fix #10872 ref #10223
This commit is contained in:
parent
00d46f6328
commit
9f81acc076
|
@ -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: >
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:~ }|
|
||||
|
|
|
@ -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 = {...}
|
||||
|
|
Loading…
Reference in New Issue