mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge pull request #25872 from gpanders/osc52
feat(clipboard): add OSC 52 clipboard support
This commit is contained in:
		| @@ -208,6 +208,9 @@ The following new APIs and features were added. | |||||||
| • The |TermResponse| autocommand event can be used with |v:termresponse| to | • The |TermResponse| autocommand event can be used with |v:termresponse| to | ||||||
|   read escape sequence responses from the terminal. |   read escape sequence responses from the terminal. | ||||||
|  |  | ||||||
|  | • A clipboard provider which uses OSC 52 to copy the selection to the system | ||||||
|  |   clipboard is now bundled by default. |clipboard-osc52| | ||||||
|  |  | ||||||
| ============================================================================== | ============================================================================== | ||||||
| CHANGED FEATURES                                                 *news-changed* | CHANGED FEATURES                                                 *news-changed* | ||||||
|  |  | ||||||
|   | |||||||
| @@ -253,7 +253,35 @@ For Windows WSL, try this g:clipboard definition: | |||||||
|                 \   }, |                 \   }, | ||||||
|                 \   'cache_enabled': 0, |                 \   'cache_enabled': 0, | ||||||
|                 \ } |                 \ } | ||||||
|  | < | ||||||
|  | 							    *clipboard-osc52* | ||||||
|  | Nvim bundles a clipboard provider that allows copying to the system clipboard | ||||||
|  | using OSC 52. OSC 52 is an Operating System Command control sequence that | ||||||
|  | writes the copied text to the terminal emulator. If the terminal emulator | ||||||
|  | supports OSC 52 then it will write the copied text into the system clipboard. | ||||||
|  |  | ||||||
|  | This is most useful when using Nvim remotely (e.g. via ssh) as Nvim does not | ||||||
|  | have direct access to the system clipboard in that case. | ||||||
|  |  | ||||||
|  | Because not all terminal emulators support OSC 52, this provider must be opted | ||||||
|  | into explicitly by setting the following |g:clipboard| definition: >lua | ||||||
|  |  | ||||||
|  |     vim.g.clipboard = { | ||||||
|  |       name = 'OSC 52', | ||||||
|  |       copy = { | ||||||
|  |         ['+'] = require('vim.clipboard.osc52').copy, | ||||||
|  |         ['*'] = require('vim.clipboard.osc52').copy, | ||||||
|  |       }, | ||||||
|  |       paste = { | ||||||
|  |         ['+'] = require('vim.clipboard.osc52').paste, | ||||||
|  |         ['*'] = require('vim.clipboard.osc52').paste, | ||||||
|  |       }, | ||||||
|  |     } | ||||||
|  | < | ||||||
|  | Note that not all terminal emulators support reading from the system clipboard | ||||||
|  | (and even for those that do, users should be aware of the security | ||||||
|  | implications), so using OSC 52 for pasting may not be possible. | ||||||
|  | < | ||||||
| ============================================================================== | ============================================================================== | ||||||
| Paste							*provider-paste* *paste* | Paste							*provider-paste* *paste* | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								runtime/lua/vim/clipboard/osc52.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								runtime/lua/vim/clipboard/osc52.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | local M = {} | ||||||
|  |  | ||||||
|  | function M.copy(lines) | ||||||
|  |   local s = table.concat(lines, '\n') | ||||||
|  |   io.stdout:write(string.format('\x1b]52;;%s\x1b\\', vim.base64.encode(s))) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function M.paste() | ||||||
|  |   local contents = nil | ||||||
|  |   local id = vim.api.nvim_create_autocmd('TermResponse', { | ||||||
|  |     callback = function(args) | ||||||
|  |       local resp = args.data ---@type string | ||||||
|  |       local encoded = resp:match('\x1b%]52;%w?;([A-Za-z0-9+/=]*)') | ||||||
|  |       if encoded then | ||||||
|  |         contents = vim.base64.decode(encoded) | ||||||
|  |         return true | ||||||
|  |       end | ||||||
|  |     end, | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   io.stdout:write('\x1b]52;;?\x1b\\') | ||||||
|  |  | ||||||
|  |   vim.wait(1000, function() | ||||||
|  |     return contents ~= nil | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   -- Delete the autocommand if it didn't already delete itself | ||||||
|  |   pcall(vim.api.nvim_del_autocmd, id) | ||||||
|  |  | ||||||
|  |   if contents then | ||||||
|  |     return vim.split(contents, '\n') | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   vim.notify('Timed out waiting for a clipboard response from the terminal', vim.log.levels.WARN) | ||||||
|  |   return 0 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | return M | ||||||
| @@ -11,7 +11,6 @@ | |||||||
| #include "nvim/api/private/helpers.h" | #include "nvim/api/private/helpers.h" | ||||||
| #include "nvim/ascii.h" | #include "nvim/ascii.h" | ||||||
| #include "nvim/charset.h" | #include "nvim/charset.h" | ||||||
| #include "nvim/eval.h" |  | ||||||
| #include "nvim/event/defs.h" | #include "nvim/event/defs.h" | ||||||
| #include "nvim/log.h" | #include "nvim/log.h" | ||||||
| #include "nvim/macros.h" | #include "nvim/macros.h" | ||||||
| @@ -31,6 +30,7 @@ | |||||||
| #include "nvim/event/rstream.h" | #include "nvim/event/rstream.h" | ||||||
| #include "nvim/msgpack_rpc/channel.h" | #include "nvim/msgpack_rpc/channel.h" | ||||||
|  |  | ||||||
|  | #define READ_STREAM_SIZE 0xfff | ||||||
| #define KEY_BUFFER_SIZE 0xfff | #define KEY_BUFFER_SIZE 0xfff | ||||||
|  |  | ||||||
| static const struct kitty_key_map_entry { | static const struct kitty_key_map_entry { | ||||||
| @@ -153,7 +153,9 @@ void tinput_init(TermInput *input, Loop *loop) | |||||||
|   termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); |   termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); | ||||||
|  |  | ||||||
|   // setup input handle |   // setup input handle | ||||||
|   rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); |   rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE); | ||||||
|  |   termkey_set_buffer_size(input->tk, rbuffer_capacity(input->read_stream.buffer)); | ||||||
|  |  | ||||||
|   // initialize a timer handle for handling ESC with libtermkey |   // initialize a timer handle for handling ESC with libtermkey | ||||||
|   time_watcher_init(loop, &input->timer_handle, input); |   time_watcher_init(loop, &input->timer_handle, input); | ||||||
| } | } | ||||||
| @@ -758,9 +760,9 @@ static void handle_raw_buffer(TermInput *input, bool force) | |||||||
|     RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { |     RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { | ||||||
|       size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len)); |       size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len)); | ||||||
|       // termkey_push_bytes can return (size_t)-1, so it is possible that |       // termkey_push_bytes can return (size_t)-1, so it is possible that | ||||||
|       // `consumed > input->read_stream.buffer->size`, but since tk_getkeys is |       // `consumed > rbuffer_size(input->read_stream.buffer)`, but since tk_getkeys is | ||||||
|       // called soon, it shouldn't happen. |       // called soon, it shouldn't happen. | ||||||
|       assert(consumed <= input->read_stream.buffer->size); |       assert(consumed <= rbuffer_size(input->read_stream.buffer)); | ||||||
|       rbuffer_consumed(input->read_stream.buffer, consumed); |       rbuffer_consumed(input->read_stream.buffer, consumed); | ||||||
|       // Process the keys now: there is no guarantee `count` will |       // Process the keys now: there is no guarantee `count` will | ||||||
|       // fit into libtermkey's input buffer. |       // fit into libtermkey's input buffer. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Gregory Anders
					Gregory Anders