mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 21:25:04 -07:00
paste: make vim.paste() "public"
This commit is contained in:
parent
ed60015266
commit
87389c6a57
@ -533,6 +533,25 @@ inspect({object}, {options}) *vim.inspect()*
|
|||||||
See also: ~
|
See also: ~
|
||||||
https://github.com/kikito/inspect.lua
|
https://github.com/kikito/inspect.lua
|
||||||
|
|
||||||
|
paste({lines}, {phase}) *vim.paste()*
|
||||||
|
Paste handler, invoked by |nvim_paste()| when a conforming UI
|
||||||
|
(such as the |TUI|) pastes text into the editor.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{lines} |readfile()|-style list of lines to paste.
|
||||||
|
|channel-lines|
|
||||||
|
{phase} -1: "non-streaming" paste: the call contains all
|
||||||
|
lines. If paste is "streamed", `phase` indicates the stream state:
|
||||||
|
• 1: starts the paste (exactly once)
|
||||||
|
• 2: continues the paste (zero or more times)
|
||||||
|
• 3: ends the paste (exactly once)
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
false if client should cancel the paste.
|
||||||
|
|
||||||
|
See also: ~
|
||||||
|
|paste|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,11 +222,11 @@ a list of lines and `regtype` is a register type conforming to |setreg()|.
|
|||||||
Paste *provider-paste* *paste*
|
Paste *provider-paste* *paste*
|
||||||
|
|
||||||
"Paste" is a separate concept from |clipboard|: paste means "dump a bunch of
|
"Paste" is a separate concept from |clipboard|: paste means "dump a bunch of
|
||||||
text to the editor", whereas clipboard adds features like |quote-+| to get and
|
text to the editor", whereas clipboard provides features like |quote-+| to get
|
||||||
set the OS clipboard buffer directly. When you middle-click or CTRL-SHIFT-v
|
and set the OS clipboard directly. For example, middle-click or CTRL-SHIFT-v
|
||||||
(macOS: CMD-v) to paste text into your terminal, this is "paste", not
|
(macOS: CMD-v) in your terminal is "paste", not "clipboard": the terminal
|
||||||
"clipboard": the terminal application (Nvim) just gets a stream of text, it
|
application (Nvim) just gets a stream of text, it does not interact with the
|
||||||
does not interact with the clipboard directly.
|
clipboard directly.
|
||||||
|
|
||||||
*bracketed-paste-mode*
|
*bracketed-paste-mode*
|
||||||
Pasting in the |TUI| depends on the "bracketed paste" terminal capability,
|
Pasting in the |TUI| depends on the "bracketed paste" terminal capability,
|
||||||
@ -235,19 +235,21 @@ pasted text. https://cirw.in/blog/bracketed-paste
|
|||||||
This works automatically if your terminal supports it.
|
This works automatically if your terminal supports it.
|
||||||
|
|
||||||
*ui-paste*
|
*ui-paste*
|
||||||
GUIs can opt-into Nvim's amazing paste-handling by calling |nvim_paste()|.
|
GUIs can paste by calling |nvim_paste()|.
|
||||||
|
|
||||||
PASTE BEHAVIOR ~
|
PASTE BEHAVIOR ~
|
||||||
|
|
||||||
Paste always inserts text after the cursor. In cmdline-mode only the first
|
Paste always inserts text after the cursor. In cmdline-mode only the first
|
||||||
line is pasted, to avoid accidentally executing many commands.
|
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
|
When pasting a huge amount of text, screen updates are throttled and the
|
||||||
message area shows a "..." pulse.
|
message area shows a "..." pulse.
|
||||||
|
|
||||||
You can implement a custom paste handler. Example: >
|
You can implement a custom paste handler by redefining |vim.paste()|.
|
||||||
|
Example: >
|
||||||
|
|
||||||
vim._paste = (function(lines, phase)
|
vim.paste = (function(lines, phase)
|
||||||
vim.api.nvim_put(lines, 'c', true, true)
|
vim.api.nvim_put(lines, 'c', true, true)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -1243,7 +1243,7 @@ Boolean nvim_paste(String data, Integer phase, Error *err)
|
|||||||
Array lines = string_to_array(data);
|
Array lines = string_to_array(data);
|
||||||
ADD(args, ARRAY_OBJ(lines));
|
ADD(args, ARRAY_OBJ(lines));
|
||||||
ADD(args, INTEGER_OBJ(phase));
|
ADD(args, INTEGER_OBJ(phase));
|
||||||
rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim._paste(...)"), args,
|
rv = nvim_execute_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
|
||||||
err);
|
err);
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
draining = true;
|
draining = true;
|
||||||
|
@ -93,47 +93,6 @@ local function _os_proc_children(ppid)
|
|||||||
return children
|
return children
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Default nvim_paste() handler.
|
|
||||||
local _paste = (function()
|
|
||||||
local tdots, tredraw, tick, got_line1 = 0, 0, 0, false
|
|
||||||
return function(lines, phase)
|
|
||||||
local call = vim.api.nvim_call_function
|
|
||||||
local now = vim.loop.now()
|
|
||||||
local mode = call('mode', {}):sub(1,1)
|
|
||||||
if phase < 2 then -- Reset flags.
|
|
||||||
tdots, tredraw, tick, got_line1 = now, now, 0, false
|
|
||||||
end
|
|
||||||
if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line.
|
|
||||||
got_line1 = (#lines > 1)
|
|
||||||
vim.api.nvim_set_option('paste', true) -- For nvim_input().
|
|
||||||
local line1, _ = string.gsub(lines[1], '[\r\n\012\027]', ' ')
|
|
||||||
vim.api.nvim_input(line1) -- Scrub "\r".
|
|
||||||
elseif mode == 'i' or mode == 'R' then
|
|
||||||
vim.api.nvim_put(lines, 'c', false, true)
|
|
||||||
else
|
|
||||||
vim.api.nvim_put(lines, 'c', true, true)
|
|
||||||
end
|
|
||||||
if (now - tredraw >= 1000) or phase == -1 or phase > 2 then
|
|
||||||
tredraw = now
|
|
||||||
vim.api.nvim_command('redraw')
|
|
||||||
vim.api.nvim_command('redrawstatus')
|
|
||||||
end
|
|
||||||
if phase ~= -1 and (now - tdots >= 100) then
|
|
||||||
local dots = ('.'):rep(tick % 4)
|
|
||||||
tdots = now
|
|
||||||
tick = tick + 1
|
|
||||||
-- Use :echo because Lua print('') is a no-op, and we want to clear the
|
|
||||||
-- message when there are zero dots.
|
|
||||||
vim.api.nvim_command(('echo "%s"'):format(dots))
|
|
||||||
end
|
|
||||||
if phase == -1 or phase == 3 then
|
|
||||||
vim.api.nvim_command('echo ""')
|
|
||||||
vim.api.nvim_set_option('paste', false)
|
|
||||||
end
|
|
||||||
return true -- Paste will not continue if not returning `true`.
|
|
||||||
end
|
|
||||||
end)()
|
|
||||||
|
|
||||||
-- TODO(ZyX-I): Create compatibility layer.
|
-- TODO(ZyX-I): Create compatibility layer.
|
||||||
--{{{1 package.path updater function
|
--{{{1 package.path updater function
|
||||||
-- Last inserted paths. Used to clear out items from package.[c]path when they
|
-- Last inserted paths. Used to clear out items from package.[c]path when they
|
||||||
@ -202,6 +161,59 @@ local function inspect(object, options) -- luacheck: no unused
|
|||||||
error(object, options) -- Stub for gen_vimdoc.py
|
error(object, options) -- Stub for gen_vimdoc.py
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Paste handler, invoked by |nvim_paste()| when a conforming UI
|
||||||
|
--- (such as the |TUI|) pastes text into the editor.
|
||||||
|
---
|
||||||
|
--@see |paste|
|
||||||
|
---
|
||||||
|
--@param lines |readfile()|-style list of lines to paste. |channel-lines|
|
||||||
|
--@param phase -1: "non-streaming" paste: the call contains all lines.
|
||||||
|
--- If paste is "streamed", `phase` indicates the stream state:
|
||||||
|
--- - 1: starts the paste (exactly once)
|
||||||
|
--- - 2: continues the paste (zero or more times)
|
||||||
|
--- - 3: ends the paste (exactly once)
|
||||||
|
--@returns false if client should cancel the paste.
|
||||||
|
local function paste(lines, phase) end -- luacheck: no unused
|
||||||
|
paste = (function()
|
||||||
|
local tdots, tredraw, tick, got_line1 = 0, 0, 0, false
|
||||||
|
return function(lines, phase)
|
||||||
|
local call = vim.api.nvim_call_function
|
||||||
|
local now = vim.loop.now()
|
||||||
|
local mode = call('mode', {}):sub(1,1)
|
||||||
|
if phase < 2 then -- Reset flags.
|
||||||
|
tdots, tredraw, tick, got_line1 = now, now, 0, false
|
||||||
|
end
|
||||||
|
if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line.
|
||||||
|
got_line1 = (#lines > 1)
|
||||||
|
vim.api.nvim_set_option('paste', true) -- For nvim_input().
|
||||||
|
local line1, _ = string.gsub(lines[1], '[\r\n\012\027]', ' ')
|
||||||
|
vim.api.nvim_input(line1) -- Scrub "\r".
|
||||||
|
elseif mode == 'i' or mode == 'R' then
|
||||||
|
vim.api.nvim_put(lines, 'c', false, true)
|
||||||
|
else
|
||||||
|
vim.api.nvim_put(lines, 'c', true, true)
|
||||||
|
end
|
||||||
|
if (now - tredraw >= 1000) or phase == -1 or phase > 2 then
|
||||||
|
tredraw = now
|
||||||
|
vim.api.nvim_command('redraw')
|
||||||
|
vim.api.nvim_command('redrawstatus')
|
||||||
|
end
|
||||||
|
if phase ~= -1 and (now - tdots >= 100) then
|
||||||
|
local dots = ('.'):rep(tick % 4)
|
||||||
|
tdots = now
|
||||||
|
tick = tick + 1
|
||||||
|
-- Use :echo because Lua print('') is a no-op, and we want to clear the
|
||||||
|
-- message when there are zero dots.
|
||||||
|
vim.api.nvim_command(('echo "%s"'):format(dots))
|
||||||
|
end
|
||||||
|
if phase == -1 or phase == 3 then
|
||||||
|
vim.api.nvim_command('echo ""')
|
||||||
|
vim.api.nvim_set_option('paste', false)
|
||||||
|
end
|
||||||
|
return true -- Paste will not continue if not returning `true`.
|
||||||
|
end
|
||||||
|
end)()
|
||||||
|
|
||||||
--- Defers the wrapped callback until the Nvim API is safe to call.
|
--- Defers the wrapped callback until the Nvim API is safe to call.
|
||||||
---
|
---
|
||||||
--@see |vim-loop-callbacks|
|
--@see |vim-loop-callbacks|
|
||||||
@ -227,8 +239,8 @@ local module = {
|
|||||||
_update_package_paths = _update_package_paths,
|
_update_package_paths = _update_package_paths,
|
||||||
_os_proc_children = _os_proc_children,
|
_os_proc_children = _os_proc_children,
|
||||||
_os_proc_info = _os_proc_info,
|
_os_proc_info = _os_proc_info,
|
||||||
_paste = _paste,
|
|
||||||
_system = _system,
|
_system = _system,
|
||||||
|
paste = paste,
|
||||||
schedule_wrap = schedule_wrap,
|
schedule_wrap = schedule_wrap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ describe('API', function()
|
|||||||
eq(false, nvim('get_option', 'paste'))
|
eq(false, nvim('get_option', 'paste'))
|
||||||
end)
|
end)
|
||||||
it('vim.paste() failure', function()
|
it('vim.paste() failure', function()
|
||||||
nvim('execute_lua', 'vim._paste = (function(lines, phase) error("fake fail") end)', {})
|
nvim('execute_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {})
|
||||||
expect_err([[Error executing lua: %[string "%<nvim>"]:1: fake fail]],
|
expect_err([[Error executing lua: %[string "%<nvim>"]:1: fake fail]],
|
||||||
request, 'nvim_paste', 'line 1\nline 2\nline 3', 1)
|
request, 'nvim_paste', 'line 1\nline 2\nline 3', 1)
|
||||||
end)
|
end)
|
||||||
|
@ -326,8 +326,8 @@ describe('TUI', function()
|
|||||||
|
|
||||||
it('paste: recovers from vim.paste() failure', function()
|
it('paste: recovers from vim.paste() failure', function()
|
||||||
child_session:request('nvim_execute_lua', [[
|
child_session:request('nvim_execute_lua', [[
|
||||||
_G.save_paste_fn = vim._paste
|
_G.save_paste_fn = vim.paste
|
||||||
vim._paste = function(lines, phase) error("fake fail") end
|
vim.paste = function(lines, phase) error("fake fail") end
|
||||||
]], {})
|
]], {})
|
||||||
-- Start pasting...
|
-- Start pasting...
|
||||||
feed_data('\027[200~line 1\nline 2\n')
|
feed_data('\027[200~line 1\nline 2\n')
|
||||||
@ -360,7 +360,7 @@ describe('TUI', function()
|
|||||||
]]}
|
]]}
|
||||||
-- Paste works if vim.paste() succeeds.
|
-- Paste works if vim.paste() succeeds.
|
||||||
child_session:request('nvim_execute_lua', [[
|
child_session:request('nvim_execute_lua', [[
|
||||||
vim._paste = _G.save_paste_fn
|
vim.paste = _G.save_paste_fn
|
||||||
]], {})
|
]], {})
|
||||||
feed_data('\027[200~line A\nline B\n\027[201~')
|
feed_data('\027[200~line A\nline B\n\027[201~')
|
||||||
feed_data('\n') -- <Enter>
|
feed_data('\n') -- <Enter>
|
||||||
|
Loading…
Reference in New Issue
Block a user