patch 9.1.1972: No way to access the clipboard without X11/Wayland

Problem:  No way to access the clipboard without X11/Wayland.
Solution: Add the clipboard provider feature (Foxe Chen).

closes: #18781

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Foxe Chen
2025-12-11 21:11:03 +01:00
committed by Christian Brabandt
parent 98a0cbf05b
commit fcd3958dcb
28 changed files with 1349 additions and 130 deletions
+116 -2
View File
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 9.1. Last change: 2025 Dec 01
*eval.txt* For Vim version 9.1. Last change: 2025 Dec 11
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -38,6 +38,7 @@ a remark is given.
12. The sandbox |eval-sandbox|
13. Textlock |textlock|
14. Vim script library |vim-script-library|
15. Clipboard providers |clipboard-providers|
Testing support is documented in |testing.txt|.
Profiling is documented at |profiling|.
@@ -2247,7 +2248,14 @@ v:clipmethod The current method of accessing the clipboard that is being
x11 X11 selections are being used.
none The above methods are unavailable or
cannot be used.
See 'clipmethod' for more details.
If it is set to a value not in the above list, then a
clipboard provider with the given name is being used for the
clipboard functionality. See 'clipmethod' for more details.
*v:clipproviders*
v:clipproviders
A dictionary containing clipboard providers, see
|clipboard-providers| for more information.
*v:cmdarg* *cmdarg-variable*
v:cmdarg This variable is used for two purposes:
@@ -5266,5 +5274,111 @@ Usage: >vim
:call dist#vim9#Launch(<args>)
:Launch <app> <args>.
<
==============================================================================
15. Clipboard providers *clipboard-providers*
The clipboard provider feature allows the "+" |quoteplus| and "*" |quotestar|
registers to be overridden by custom Vimscript functions. There can be
multiple providers, and Vim chooses which one to use based on 'clipmethod'.
Despite the name, it does not use the 'clipboard' option and should be treated
separate from the clipboard functionality. It essentially overrides the
existing behaviour of the clipboard registers.
*clipboard-providers-no-clipboard*
If the |+clipboard| feature is not enabled, then the "+" and "*" registers
will not be enabled/available unless |v:clipmethod| is set to a provider. If
it is set to a provider, then the clipboard registers will be exposed despite
not having the |+clipboard| feature.
*clipboard-providers-plus*
If on a platform that only has the "*" register, then the "+" register will
only be available when |v:clipmethod| is set to a provider. If you want to
check if the "+" is available for use, it can be checked with: >
if has('unnamedplus')
<
*clipboard-providers-clipmethod*
To integrate the providers with Vim's clipboard functionality, the
'clipmethod' option is used on all platforms. The names of clipboard
providers should be put inside the option, and if Vim chooses it, then it
overrides the "+" and "*" registers. Note that the "+" and "*" will not be
saved in the viminfo at all.
*clipboard-providers-define*
To define a clipboard provider, the |v:clipproviders| vim variable is used. It
is a |dict| where each key is the clipboard provider name, and the value is
another |dict| declaring the "available", "copy", and "paste" callbacks: >vim
let v:clipproviders["myprovider"] = {
\ "available": function("Available"),
\ "paste": {
\ "+": function("Paste"),
\ "*": function("Paste")
\ },
\ "copy": {
\ "+": function("Copy"),
\ "*": function("Copy")
\ }
\ }
set clipmethod^=myprovider
<
Each callback can either be a name of a function in a string, a |Funcref|, or
a |lambda| expression.
With the exception of the "available" callback if a callback is not provided,
Vim will not invoke anything, and this is not an error.
*clipboard-providers-textlock*
In both the "paste" and "copy" callbacks, it is not allowed to change the
buffer text, see |textlock|.
*clipboard-providers-available*
The "available" callback is optional, does not take any arguments and should
return a |boolean| or non-zero number, which tells Vim if it is available
for use. If it is not, then Vim skips over it and tries the next 'clipmethod'
value. If the "available" callback is not provided, Vim assumes the provider
is always available for use (true).
*clipboard-providers-paste*
The "paste" callback takes the following arguments in the following order:
1. Name of the register being accessed, either "+" or "*".
It should return a |list| or |tuple| containing the following elements in
order:
1. Register type (and optional width) conforming to |setreg()|
2. A |list| of strings to return to Vim, each representing a line.
*clipboard-providers-copy*
The "copy" callback returns nothing and takes the following arguments in the
following order:
1. Name of the register being accessed, either "+" or "*".
2. Register type conforming to |getregtype()|
3. List of strings to use, each representing a line.
Below is a sample script that makes use of the clipboard provider feature: >vim
func Available()
return v:true
endfunc
func Copy(reg, type, str)
echom "Register: " .. a:reg
echom "Register type: " .. a:type
echom "Contents: " .. string(a:str)
endfunc
func Paste(reg)
return ("b40", ["this", "is", "the", a:reg, "register!"])
endfunc
let v:clipproviders["test"] = {
\ "available": function("Available"),
\ "copy": {
\ "+": function("Copy"),
\ "*": function("Copy")
\ },
\ "paste": {
\ "+": function("Paste"),
\ "*": function("Paste")
\ }
\ }
set clipmethod^=test
<
vim:tw=78:ts=8:noet:ft=help:norl:
+12 -8
View File
@@ -1,4 +1,4 @@
*options.txt* For Vim version 9.1. Last change: 2025 Dec 09
*options.txt* For Vim version 9.1. Last change: 2025 Dec 11
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1912,18 +1912,22 @@ A jump table for the options with a short description can be found at |Q_op|.
for VMS: "x11",
otherwise: "")
global
{only when the |+xterm_clipboard| or
|+wayland_clipboard| features are included}
Specifies which method of accessing the system clipboard is used,
depending on which method works first or is available. Supported
methods are:
{only when the |+xterm_clipboard|, |+wayland_clipboard|,
or |+eval| features are included}
Specifies which method of accessing the system clipboard (or clipboard
provider) is used. Methods are tried in the order given; the first
working method is used. Supported methods are:
wayland Wayland selections
x11 X11 selections
<name> Use a clipboard provider with the given name
Note: This option is ignored when either the GUI is running or if Vim
is run on a system without Wayland or X11 support, such as Windows or
macOS. The GUI or system way of accessing the clipboard is always
used instead.
macOS. The GUI or system way of accessing the clipboard is used
instead, meaning |v:clipmethod| will be set to "none". The
exception to this is the |clipboard-providers| feature, in which if
a clipboard provider is being used, then it will override the existing
clipboard functionality.
The option value is a list of comma separated items. The list is
parsed left to right in order, and the first method that Vim
+11
View File
@@ -1410,6 +1410,7 @@ $quote eval.txt /*$quote*
+cindent various.txt /*+cindent*
+clientserver various.txt /*+clientserver*
+clipboard various.txt /*+clipboard*
+clipboard_provider various.txt /*+clipboard_provider*
+clipboard_working various.txt /*+clipboard_working*
+cmd editing.txt /*+cmd*
+cmdline_compl various.txt /*+cmdline_compl*
@@ -6692,6 +6693,15 @@ clipboard-autoselectml options.txt /*clipboard-autoselectml*
clipboard-autoselectplus options.txt /*clipboard-autoselectplus*
clipboard-exclude options.txt /*clipboard-exclude*
clipboard-html options.txt /*clipboard-html*
clipboard-providers eval.txt /*clipboard-providers*
clipboard-providers-available eval.txt /*clipboard-providers-available*
clipboard-providers-clipmethod eval.txt /*clipboard-providers-clipmethod*
clipboard-providers-copy eval.txt /*clipboard-providers-copy*
clipboard-providers-define eval.txt /*clipboard-providers-define*
clipboard-providers-no-clipboard eval.txt /*clipboard-providers-no-clipboard*
clipboard-providers-paste eval.txt /*clipboard-providers-paste*
clipboard-providers-plus eval.txt /*clipboard-providers-plus*
clipboard-providers-textlock eval.txt /*clipboard-providers-textlock*
clipboard-unnamed options.txt /*clipboard-unnamed*
clipboard-unnamedplus options.txt /*clipboard-unnamedplus*
clojure-indent indent.txt /*clojure-indent*
@@ -11258,6 +11268,7 @@ v:char eval.txt /*v:char*
v:charconvert_from eval.txt /*v:charconvert_from*
v:charconvert_to eval.txt /*v:charconvert_to*
v:clipmethod eval.txt /*v:clipmethod*
v:clipproviders eval.txt /*v:clipproviders*
v:cmdarg eval.txt /*v:cmdarg*
v:cmdbang eval.txt /*v:cmdbang*
v:collate eval.txt /*v:collate*
+2 -1
View File
@@ -1,4 +1,4 @@
*various.txt* For Vim version 9.1. Last change: 2025 Nov 09
*various.txt* For Vim version 9.1. Last change: 2025 Dec 11
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -379,6 +379,7 @@ T *+cindent* 'cindent', C indenting; Always enabled
N *+clientserver* Unix and Win32: Remote invocation |clientserver|
*+clipboard* |clipboard| support compiled-in
*+clipboard_working* |clipboard| support compiled-in and working
*+clipboard_provider* |clipboard-providers| support compiled-in
T *+cmdline_compl* command line completion |cmdline-completion|
T *+cmdline_hist* command line history |cmdline-history|
T *+cmdline_info* 'showcmd' and 'ruler'; Always enabled since
+5 -1
View File
@@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Dec 10
*version9.txt* For Vim version 9.1. Last change: 2025 Dec 11
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -41653,6 +41653,8 @@ Other new features ~
- |items()| function now supports Blob.
- The clipboard provider feature has been added |clipboard-providers|.
*changed-9.2*
Changed~
-------
@@ -41907,6 +41909,8 @@ Options: ~
Vim Variables: ~
|v:clipmethod| The current 'clipmethod'.
|v:clipproviders| A dictionary containing clipboard providers
configuration |clipboard-providers|.
|v:stacktrace| The most recent caught exception.
|v:t_enumvalue| Value of |enumvalue|.
|v:t_enum| Value of |enum| type.
+2 -2
View File
@@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" Last Change: 2025 Dec 04
" Last Change: 2025 Dec 11
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.
@@ -166,7 +166,7 @@ syn keyword vimFuncName contained win_findbuf win_getid win_gettype win_gotoid w
" Predefined variable names {{{2
" GEN_SYN_VIM: vimVarName, START_STR='syn keyword vimVimVarName contained', END_STR=''
syn keyword vimVimVarName contained count count1 prevcount errmsg warningmsg statusmsg shell_error this_session version lnum termresponse fname lang lc_time ctype charconvert_from charconvert_to fname_in fname_out fname_new fname_diff cmdarg foldstart foldend folddashes foldlevel progname servername dying exception throwpoint register cmdbang insertmode val key profiling fcs_reason fcs_choice beval_bufnr beval_winnr beval_winid beval_lnum beval_col beval_text scrollstart swapname swapchoice swapcommand char mouse_win mouse_winid mouse_lnum mouse_col operator searchforward hlsearch oldfiles windowid progpath completed_item option_new option_old option_oldlocal option_oldglobal option_command option_type errors false true none null numbermax numbermin numbersize
syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace t_tuple wayland_display clipmethod termda1 termosc vim_did_init
syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace t_tuple wayland_display clipmethod termda1 termosc vim_did_init clipproviders
"--- syntax here and above generated by runtime/syntax/generator/gen_syntax_vim.vim ---
+458 -14
View File
@@ -8,7 +8,9 @@
*/
/*
* clipboard.c: Functions to handle the clipboard
* clipboard.c: Functions to handle the clipboard. Additionally contains the
* clipboard provider code, which is separate from the main
* clipboard code.
*/
#include "vim.h"
@@ -29,6 +31,11 @@
// versions of these for the 'clipboard' selection, as Visual mode has no use
// for them.
#ifdef FEAT_CLIPBOARD_PROVIDER
static int clip_provider_is_available(char_u *provider);
#endif
#if defined(FEAT_CLIPBOARD)
#if defined(FEAT_WAYLAND_CLIPBOARD)
@@ -3415,6 +3422,9 @@ clip_wl_owner_exists(Clipboard_T *cbd)
#endif // FEAT_WAYLAND_CLIPBOARD
#endif // FEAT_CLIPBOARD
#ifdef HAVE_CLIPMETHOD
/*
* Returns the first method for accessing the clipboard that is available/works,
@@ -3473,8 +3483,30 @@ get_clipmethod(char_u *str)
}
else
{
ret = CLIPMETHOD_FAIL;
goto exit;
#ifdef FEAT_EVAL
// Check if name matches a clipboard provider
int r = clip_provider_is_available(buf);
if (r == 1)
{
method = CLIPMETHOD_PROVIDER;
if (ret == CLIPMETHOD_FAIL)
{
vim_free(clip_provider);
clip_provider = vim_strsave(buf);
if (clip_provider == NULL)
goto fail;
}
}
else if (r == -1)
#endif
{
#ifdef FEAT_EVAL
fail:
#endif
ret = CLIPMETHOD_FAIL;
goto exit;
}
}
// Keep on going in order to catch errors
@@ -3494,17 +3526,21 @@ exit:
/*
* Returns name of clipmethod in a statically allocated string.
*/
static char *
static char_u *
clipmethod_to_str(clipmethod_T method)
{
switch(method)
{
case CLIPMETHOD_WAYLAND:
return "wayland";
return (char_u *)"wayland";
case CLIPMETHOD_X11:
return "x11";
return (char_u *)"x11";
case CLIPMETHOD_PROVIDER:
#ifdef FEAT_EVAL
return clip_provider;
#endif
default:
return "none";
return (char_u *)"none";
}
}
@@ -3522,9 +3558,10 @@ choose_clipmethod(void)
// If GUI is running or we are not on a system with Wayland or X11, then always
// return CLIPMETHOD_NONE. System or GUI clipboard handling always overrides.
// This is unless a provider is being used.
#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
# if defined(FEAT_GUI)
if (gui.in_use)
if (method != CLIPMETHOD_PROVIDER && gui.in_use)
{
# ifdef FEAT_WAYLAND
// We only interact with Wayland for the clipboard, we can just deinit
@@ -3538,19 +3575,26 @@ choose_clipmethod(void)
# endif
#else
// If on a system like windows or macos, then clipmethod is irrelevant, we
// use their way of accessing the clipboard.
method = CLIPMETHOD_NONE;
goto exit;
// use their way of accessing the clipboard. This is unless we are using the
// clipboard provider
#ifdef FEAT_CLIPBOARD_PROVIDER
if (method != CLIPMETHOD_PROVIDER)
#endif
{
method = CLIPMETHOD_NONE;
goto exit;
}
#endif
#ifdef FEAT_CLIPBOARD
// Deinitialize clipboard if there is no way to access clipboard
if (method == CLIPMETHOD_NONE)
clip_init(FALSE);
// If we have a clipmethod that works now, then initialize clipboard
else if (clipmethod == CLIPMETHOD_NONE && method != CLIPMETHOD_NONE)
{
clip_init(TRUE);
did_warn_clipboard = false;
clip_init(TRUE);
did_warn_clipboard = false;
}
// Disown clipboard if we are switching to a new method
else if (clipmethod != CLIPMETHOD_NONE && method != clipmethod)
@@ -3572,6 +3616,7 @@ lose_sel_exit:
did_warn_clipboard = false;
}
}
#endif // FEAT_CLIPBOARD
#if !defined(FEAT_XCLIPBOARD) && !defined(FEAT_WAYLAND_CLIPBOARD)
exit:
@@ -3603,4 +3648,403 @@ ex_clipreset(exarg_T *eap UNUSED)
clipmethod_to_str(clipmethod));
}
#endif // FEAT_CLIPBOARD
#endif // HAVE_CLIPMETHOD
#ifdef FEAT_CLIPBOARD_PROVIDER
/*
* Check if a clipboard provider with given name is available. Returns 1 if available,
* 0 if not available, and -1 on error
*/
static int
clip_provider_is_available(char_u *provider)
{
dict_T *providers = get_vim_var_dict(VV_CLIPPROVIDERS);
typval_T provider_tv = {0};
callback_T callback = {0};
typval_T rettv = {0};
typval_T func_tv = {0};
int res = 0;
if (dict_get_tv(providers, (char *)provider, &provider_tv) == FAIL
|| provider_tv.v_type != VAR_DICT)
// clipboard provider not defined
return -1;
if (dict_get_tv(provider_tv.vval.v_dict, "available", &func_tv) == FAIL)
{
clear_tv(&provider_tv);
// If "available" function not specified assume always TRUE
return 1;
}
if ((callback = get_callback(&func_tv)).cb_name == NULL)
goto fail;
if (call_callback(&callback, -1, &rettv, 0, NULL) == FAIL ||
(rettv.v_type != VAR_BOOL && rettv.v_type != VAR_NUMBER))
goto fail;
if (rettv.vval.v_number)
res = 1;
if (FALSE)
fail:
res = -1;
free_callback(&callback);
clear_tv(&func_tv);
clear_tv(&rettv);
clear_tv(&provider_tv);
return res;
}
/*
* Get the specified callback "function" from the provider dictionary for
* register "reg".
*/
static int
clip_provider_get_callback(
char_u *reg,
char_u *provider,
char_u *function,
callback_T *callback)
{
dict_T *providers = get_vim_var_dict(VV_CLIPPROVIDERS);
typval_T provider_tv;
typval_T action_tv;
typval_T func_tv;
callback_T cb;
if (dict_get_tv(providers, (char *)provider, &provider_tv) == FAIL)
return FAIL;
else if (provider_tv.v_type != VAR_DICT)
{
clear_tv(&provider_tv);
return FAIL;
}
else if (dict_get_tv(
provider_tv.vval.v_dict,
(char *)function,
&action_tv) == FAIL)
{
clear_tv(&provider_tv);
return FAIL;
}
else if (action_tv.v_type != VAR_DICT)
{
clear_tv(&provider_tv);
clear_tv(&action_tv);
return FAIL;
}
else if (dict_get_tv(action_tv.vval.v_dict, (char *)reg, &func_tv) == FAIL)
{
clear_tv(&provider_tv);
clear_tv(&action_tv);
return FAIL;
}
else if ((cb = get_callback(&func_tv)).cb_name == NULL)
{
clear_tv(&provider_tv);
clear_tv(&action_tv);
clear_tv(&func_tv);
return FAIL;
}
clear_tv(&provider_tv);
clear_tv(&action_tv);
// func_tv owns the function name, so we must make a copy for the callback
set_callback(callback, &cb);
free_callback(&cb);
clear_tv(&func_tv);
return OK;
}
static void
clip_provider_copy(char_u *reg, char_u *provider)
{
callback_T callback;
typval_T rettv;
typval_T argvars[4];
yankreg_T *y_ptr;
char_u type[2 + NUMBUFLEN] = {0};
list_T *list = NULL;
if (clip_provider_get_callback(
reg,
provider,
(char_u *)"copy",
&callback) == FAIL)
return;
// Convert register type into a string
if (*reg == '+')
y_ptr = get_y_register(REAL_PLUS_REGISTER);
else
y_ptr = get_y_register(STAR_REGISTER);
switch (y_ptr->y_type)
{
case MCHAR:
type[0] = 'v';
break;
case MLINE:
type[0] = 'V';
break;
case MBLOCK:
sprintf((char *)type, "%c%d", Ctrl_V, y_ptr->y_width + 1);
break;
default:
type[0] = 0;
break;
}
argvars[0].v_type = VAR_STRING;
argvars[0].vval.v_string = reg;
argvars[1].v_type = VAR_STRING;
argvars[1].vval.v_string = type;
// Get register contents by creating a list of lines
list = list_alloc();
if (list == NULL)
{
free_callback(&callback);
return;
}
for (int i = 0; i < y_ptr->y_size; i++)
if (list_append_string(list, y_ptr->y_array[i].string, -1) == FAIL)
{
free_callback(&callback);
list_unref(list);
return;
}
list->lv_refcount++;
argvars[2].v_type = VAR_LIST;
argvars[2].v_lock = VAR_FIXED;
argvars[2].vval.v_list = list;
argvars[3].v_type = VAR_UNKNOWN;
textlock++;
call_callback(&callback, -1, &rettv, 3, argvars);
clear_tv(&rettv);
textlock--;
free_callback(&callback);
list_unref(list);
}
static void
clip_provider_paste(char_u *reg, char_u *provider)
{
callback_T callback;
typval_T argvars[2];
typval_T rettv;
int ret;
char_u *reg_type;
list_T *lines;
if (clip_provider_get_callback(
reg,
provider,
(char_u *)"paste",
&callback) == FAIL)
return;
argvars[0].v_type = VAR_STRING;
argvars[0].vval.v_string = reg;
argvars[1].v_type = VAR_UNKNOWN;
textlock++;
ret = call_callback(&callback, -1, &rettv, 1, argvars);
textlock--;
if (ret == FAIL)
goto exit;
else if (rettv.v_type == VAR_TUPLE
&& TUPLE_LEN(rettv.vval.v_tuple) == 2
&& TUPLE_ITEM(rettv.vval.v_tuple, 0)->v_type == VAR_STRING
&& TUPLE_ITEM(rettv.vval.v_tuple, 1)->v_type == VAR_LIST)
{
reg_type = TUPLE_ITEM(rettv.vval.v_tuple, 0)->vval.v_string;
lines = TUPLE_ITEM(rettv.vval.v_tuple, 1)->vval.v_list;
}
else if (rettv.v_type == VAR_LIST
&& rettv.vval.v_list->lv_len == 2
&& rettv.vval.v_list->lv_first->li_tv.v_type == VAR_STRING
&& rettv.vval.v_list->lv_first->li_next->li_tv.v_type == VAR_LIST)
{
reg_type = rettv.vval.v_list->lv_first->li_tv.vval.v_string;
lines = rettv.vval.v_list->lv_first->li_next->li_tv.vval.v_list;
}
else
goto exit;
{
char_u yank_type = MAUTO;
long block_len = -1;
yankreg_T *y_ptr, *cur_y_ptr;
char_u **lstval;
char_u **allocval;
char_u buf[NUMBUFLEN];
char_u **curval;
char_u **curallocval;
char_u *strval;
listitem_T *li;
int len;
// If the list is NULL handle like an empty list.
len = lines == NULL ? 0 : lines->lv_len;
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
lstval = ALLOC_MULT(char_u *, (len + 1) * 2);
if (lstval == NULL)
goto exit;
curval = lstval;
allocval = lstval + len + 2;
curallocval = allocval;
if (lines != NULL)
{
CHECK_LIST_MATERIALIZE(lines);
FOR_ALL_LIST_ITEMS(lines, li)
{
strval = tv_get_string_buf_chk(&li->li_tv, buf);
if (strval == NULL)
goto free_lstval;
if (strval == buf)
{
// Need to make a copy, next tv_get_string_buf_chk() will
// overwrite the string.
strval = vim_strsave(buf);
if (strval == NULL)
goto free_lstval;
*curallocval++ = strval;
}
*curval++ = strval;
}
}
*curval++ = NULL;
if (STRLEN(reg_type) <= 0
|| get_yank_type(&reg_type, &yank_type, &block_len) == FAIL)
{
emsg(e_invalid_argument);
goto free_lstval;
}
if (*reg == '+')
y_ptr = get_y_register(REAL_PLUS_REGISTER);
else
y_ptr = get_y_register(STAR_REGISTER);
// Free previous register contents
cur_y_ptr = get_y_current();
set_y_current(y_ptr);
free_yank_all();
get_y_current()->y_size = 0;
set_y_current(cur_y_ptr);
str_to_reg(y_ptr,
yank_type,
(char_u *)lstval,
-1,
block_len,
TRUE);
free_lstval:
while (curallocval > allocval)
vim_free(*--curallocval);
vim_free(lstval);
}
exit:
free_callback(&callback);
clear_tv(&rettv);
}
// Used to stop calling the provider callback every time there is an update.
// This prevents unnecessary calls when accessing the provider often in an
// interval.
//
// If -1 then allow provider callback to be called then set to zero. Default
// value (is allowed) is -2.
static int star_pause_count = -2, plus_pause_count = -2;
void
call_clip_provider_request(int reg)
{
if (clipmethod != CLIPMETHOD_PROVIDER)
return;
if (reg == '+' && plus_pause_count < 0)
{
if (plus_pause_count == -1)
plus_pause_count = 1;
clip_provider_paste((char_u *)"+", clip_provider);
}
else if (reg == '*' && star_pause_count < 0)
{
if (star_pause_count == -1)
star_pause_count = 1;
clip_provider_paste((char_u *)"*", clip_provider);
}
else
return;
}
void
call_clip_provider_set(int reg)
{
if (clipmethod != CLIPMETHOD_PROVIDER)
return;
if (reg == '+' && plus_pause_count < 0)
{
if (plus_pause_count == -1)
plus_pause_count = 1;
clip_provider_copy((char_u *)"+", clip_provider);
}
else if (reg == '*' && star_pause_count < 0)
{
if (star_pause_count == -1)
star_pause_count = 1;
clip_provider_copy((char_u *)"*", clip_provider);
}
}
/*
* Makes it so that the next provider call is only done once any calls after are
* ignored, until dec_clip_provider is called the same number of times after
* again. Note that this is per clipboard register ("+", "*")
*/
void
inc_clip_provider(void)
{
plus_pause_count = plus_pause_count == -2 ? -1 : plus_pause_count + 1;
star_pause_count = star_pause_count == -2 ? -1 : star_pause_count + 1;
}
void
dec_clip_provider(void)
{
plus_pause_count = plus_pause_count == -1 ? -1 : plus_pause_count - 1;
star_pause_count = star_pause_count == -1 ? -1 : star_pause_count - 1;
if (plus_pause_count == 0 || plus_pause_count == -1)
plus_pause_count = -2;
if (star_pause_count == 0 || star_pause_count == -1)
star_pause_count = -2;
}
#endif // FEAT_CLIPBOARD_PROVIDER
+29 -10
View File
@@ -6866,6 +6866,13 @@ f_has(typval_T *argvars, typval_T *rettv)
1
#else
0
#endif
},
{"clipboard_provider",
#ifdef FEAT_CLIPBOARD_PROVIDER
1
#else
0
#endif
},
{"cmdline_compl", 1},
@@ -7563,14 +7570,6 @@ f_has(typval_T *argvars, typval_T *rettv)
1
#else
0
#endif
},
{"unnamedplus",
#if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
1
#else
0
#endif
},
{"user-commands", 1}, // was accidentally included in 5.4
@@ -7918,7 +7917,27 @@ f_has(typval_T *argvars, typval_T *rettv)
{
x = TRUE;
#ifdef FEAT_CLIPBOARD
n = clip_star.available;
n = clipmethod == CLIPMETHOD_PROVIDER ? TRUE : clip_star.available;
#endif
}
else if (STRICMP(name, "unnamedplus") == 0)
{
x = TRUE;
#ifdef FEAT_CLIPBOARD
// The + register is available when clipmethod is set to a provider,
// but becomes unavailable if on a platform that doesn't support it
// and clipmethod is "none".
// (Windows, MacOS).
# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
n = TRUE;
# elif defined(FEAT_EVAL)
if (clipmethod == CLIPMETHOD_PROVIDER)
n = TRUE;
else
n = FALSE;
# else
n = FALSE;
# endif
#endif
}
}
@@ -11493,7 +11512,7 @@ f_setpos(typval_T *argvars, typval_T *rettv)
/*
* Translate a register type string to the yank type and block length
*/
static int
int
get_yank_type(char_u **pp, char_u *yank_type, long *block_len)
{
char_u *stropt = *pp;
+1
View File
@@ -168,6 +168,7 @@ static struct vimvar
{VV_NAME("termda1", VAR_STRING), NULL, VV_RO},
{VV_NAME("termosc", VAR_STRING), NULL, VV_RO},
{VV_NAME("vim_did_init", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("clipproviders", VAR_DICT), NULL, VV_RO},
};
// shorthand
+6
View File
@@ -5489,12 +5489,18 @@ ex_global(exarg_T *eap)
}
else
{
#ifdef FEAT_CLIPBOARD_PROVIDER
inc_clip_provider();
#endif
#ifdef FEAT_CLIPBOARD
start_global_changes();
#endif
global_exe(cmd);
#ifdef FEAT_CLIPBOARD
end_global_changes();
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
dec_clip_provider();
#endif
}
+6
View File
@@ -536,6 +536,9 @@ ex_listdo(exarg_T *eap)
#ifdef FEAT_CLIPBOARD
start_global_changes();
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
inc_clip_provider();
#endif
if (eap->cmdidx == CMD_windo
|| eap->cmdidx == CMD_tabdo
@@ -760,6 +763,9 @@ ex_listdo(exarg_T *eap)
#ifdef FEAT_CLIPBOARD
end_global_changes();
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
dec_clip_provider();
#endif
}
#ifdef FEAT_EVAL
+8 -2
View File
@@ -375,7 +375,7 @@ static void ex_folddo(exarg_T *eap);
#if !defined(FEAT_WAYLAND)
# define ex_wlrestore ex_ni
#endif
#if !defined(FEAT_CLIPBOARD)
#if !defined(HAVE_CLIPMETHOD)
# define ex_clipreset ex_ni
#endif
#if !defined(FEAT_PROP_POPUP)
@@ -2372,7 +2372,7 @@ do_one_cmd(
&& (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=')
&& !((ea.argt & EX_COUNT) && VIM_ISDIGIT(*ea.arg)))
{
#ifndef FEAT_CLIPBOARD
#if !defined(FEAT_CLIPBOARD) && !defined(FEAT_CLIPBOARD_PROVIDER)
// check these explicitly for a more specific error message
if (*ea.arg == '*' || *ea.arg == '+')
{
@@ -10404,6 +10404,9 @@ ex_folddo(exarg_T *eap)
# ifdef FEAT_CLIPBOARD
start_global_changes();
# endif
#ifdef FEAT_CLIPBOARD_PROVIDER
inc_clip_provider();
#endif
// First set the marks for all lines closed/open.
for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
@@ -10416,6 +10419,9 @@ ex_folddo(exarg_T *eap)
# ifdef FEAT_CLIPBOARD
end_global_changes();
# endif
#ifdef FEAT_CLIPBOARD_PROVIDER
dec_clip_provider();
#endif
}
#endif
+6 -1
View File
@@ -993,6 +993,11 @@ EXTERN regprog_T *clip_exclude_prog INIT(= NULL);
EXTERN int clip_unnamed_saved INIT(= 0);
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
EXTERN char_u *clip_provider INIT(= NULL);
#endif
/*
* All regular windows are linked in a list. "firstwin" points to the first
* entry, "lastwin" to the last entry (can be the same as firstwin) and
@@ -2070,7 +2075,7 @@ EXTERN int p_tgc_set INIT(= FALSE);
// If we've already warned about missing/unavailable clipboard
EXTERN bool did_warn_clipboard INIT(= FALSE);
#ifdef FEAT_CLIPBOARD
#ifdef HAVE_CLIPMETHOD
EXTERN clipmethod_T clipmethod INIT(= CLIPMETHOD_NONE);
#endif
+2
View File
@@ -146,9 +146,11 @@ gui_start(char_u *arg UNUSED)
emsg(msg);
#endif
}
#ifdef HAVE_CLIPMETHOD
else
// Reset clipmethod to CLIPMETHOD_NONE
choose_clipmethod();
#endif
#ifdef FEAT_SOCKETSERVER
// Install socket server listening socket if we are running it
+1 -1
View File
@@ -704,7 +704,7 @@ vim_main2(void)
}
#endif
#ifdef FEAT_CLIPBOARD
#ifdef HAVE_CLIPMETHOD
choose_clipmethod();
#endif
+2
View File
@@ -507,6 +507,8 @@ EXTERN char_u *p_cedit; // 'cedit'
EXTERN long p_cwh; // 'cmdwinheight'
#ifdef FEAT_CLIPBOARD
EXTERN char_u *p_cb; // 'clipboard'
#endif
#ifdef HAVE_CLIPMETHOD
EXTERN char_u *p_cpm; // 'clipmethod'
#endif
EXTERN long p_ch; // 'cmdheight'
+1 -1
View File
@@ -632,7 +632,7 @@ static struct vimoption options[] =
#endif
SCTX_INIT},
{"clipmethod", "cpm", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
#ifdef FEAT_CLIPBOARD
#ifdef HAVE_CLIPMETHOD
(char_u *)&p_cpm, PV_NONE, did_set_clipmethod, expand_set_clipmethod,
# ifdef UNIX
{(char_u *)"wayland,x11", (char_u *)0L}
+54 -5
View File
@@ -44,8 +44,6 @@ static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
#ifdef FEAT_CLIPBOARD
// Note: Keep this in sync with did_set_clipboard()
static char *(p_cb_values[]) = {"unnamed", "unnamedplus", "autoselect", "autoselectplus", "autoselectml", "html", "exclude:", NULL};
// Note: Keep this in sync with get_clipmethod()
static char *(p_cpm_values[]) = {"wayland", "x11", NULL};
#endif
#ifdef FEAT_CRYPT
static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2",
@@ -1402,7 +1400,9 @@ expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches)
numMatches,
matches);
}
#endif
#ifdef HAVE_CLIPMETHOD
char *
did_set_clipmethod(optset_T *args UNUSED)
{
@@ -1412,12 +1412,61 @@ did_set_clipmethod(optset_T *args UNUSED)
int
expand_set_clipmethod(optexpand_T *args, int *numMatches, char_u ***matches)
{
return expand_set_opt_string(
// We want to expand using the predefined clipmethod values + clipboard
// provider names.
int result;
char **values;
int count, pos = 0, start = 0;
#ifdef FEAT_EVAL
dict_T *providers = get_vim_var_dict(VV_CLIPPROVIDERS);
#else
dict_T *providers = NULL;
#endif
hashtab_T *ht = providers == NULL ? NULL : &providers->dv_hashtab;
count = (ht == NULL ? 0 : ht->ht_used);
#ifdef FEAT_WAYLAND_CLIPBOARD
count++;
start++;
#endif
#ifdef FEAT_XCLIPBOARD
count++;
start++;
#endif
values = ALLOC_MULT(char *, count + 1); // Add NULL terminator too
if (values == NULL)
return FAIL;
#ifdef FEAT_WAYLAND_CLIPBOARD
values[pos++] = "wayland";
#endif
#ifdef FEAT_XCLIPBOARD
values[pos++] = "x11";
#endif
if (ht != NULL)
for (long_u i = 0; i < ht->ht_mask + 1; i++)
{
hashitem_T *hi = ht->ht_array + i;
if (!HASHITEM_EMPTY(hi))
values[pos++] = (char *)vim_strsave(hi->hi_key);
}
values[pos++] = NULL;
result = expand_set_opt_string(
args,
p_cpm_values,
ARRAY_LENGTH(p_cpm_values) - 1,
values,
count,
numMatches,
matches);
for (int i = start; i < count; i++)
vim_free(values[i]);
vim_free(values);
return result;
}
#endif
+4
View File
@@ -40,4 +40,8 @@ void clip_uninit_wayland(void);
int clip_reset_wayland(void);
char *choose_clipmethod(void);
void ex_clipreset(exarg_T *eap);
void call_clip_provider_request(int reg);
void call_clip_provider_set(int reg);
void inc_clip_provider(void);
void dec_clip_provider(void);
/* vim: set ft=c : */
+1
View File
@@ -28,4 +28,5 @@ void f_len(typval_T *argvars, typval_T *rettv);
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
void range_list_materialize(list_T *list);
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
int get_yank_type(char_u **pp, char_u *yank_type, long *block_len);
/* vim: set ft=c : */
+152 -59
View File
@@ -46,7 +46,7 @@ get_y_regs(void)
}
#endif
#if defined(FEAT_CLIPBOARD)
#if defined(FEAT_CLIPBOARD) || defined(FEAT_CLIPBOARD_PROVIDER)
yankreg_T *
get_y_register(int reg)
{
@@ -190,9 +190,17 @@ valid_yank_reg(
|| regname == '"'
|| regname == '-'
|| regname == '_'
#ifdef FEAT_CLIPBOARD
#if defined(FEAT_CLIPBOARD) // If +clipboard is enabled, then these registers
// always exist.
|| regname == '*'
|| regname == '+'
#elif defined(FEAT_CLIPBOARD_PROVIDER)
|| ( // If -clipboard, then these registers only exist when
// clipmethod is set to provider.
clipmethod == CLIPMETHOD_PROVIDER && (
regname == '*'
|| regname == '+'
))
#endif
#ifdef FEAT_DND
|| (!writing && regname == '~')
@@ -256,7 +264,25 @@ get_yank_register(int regname, int writing)
// When clipboard is not available, use register 0 instead of '+'
else if (clip_plus.available && regname == '+')
{
i = PLUS_REGISTER;
#ifdef FEAT_CLIPBOARD_PROVIDER
// We want to use the actual + register, since PLUS_REGISTER may be
// pointing to STAR_REGISTER.
if (clipmethod == CLIPMETHOD_PROVIDER)
i = REAL_PLUS_REGISTER;
else
#endif
i = PLUS_REGISTER;
ret = TRUE;
}
#elif defined(FEAT_CLIPBOARD_PROVIDER)
else if (regname == '*')
{
i = STAR_REGISTER;
ret = TRUE;
}
else if (regname == '+')
{
i = REAL_PLUS_REGISTER;
ret = TRUE;
}
#endif
@@ -284,20 +310,26 @@ get_register(
yankreg_T *reg;
int i;
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(name);
#endif
#ifdef FEAT_CLIPBOARD
// When Visual area changed, may have to update selection. Obtain the
// selection too.
if (name == '*' && clip_star.available)
if (clipmethod != CLIPMETHOD_PROVIDER)
{
if (clip_isautosel_star())
clip_update_selection(&clip_star);
may_get_selection(name);
}
if (name == '+' && clip_plus.available)
{
if (clip_isautosel_plus())
clip_update_selection(&clip_plus);
may_get_selection(name);
// When Visual area changed, may have to update selection. Obtain the
// selection too.
if (name == '*' && clip_star.available)
{
if (clip_isautosel_star())
clip_update_selection(&clip_star);
may_get_selection(name);
}
if (name == '+' && clip_plus.available)
{
if (clip_isautosel_plus())
clip_update_selection(&clip_plus);
may_get_selection(name);
}
}
#endif
@@ -615,8 +647,12 @@ do_execreg(
}
execreg_lastc = regname;
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(regname);
#endif
#ifdef FEAT_CLIPBOARD
regname = may_get_selection(regname);
if (clipmethod != CLIPMETHOD_PROVIDER)
regname = may_get_selection(regname);
#endif
// black hole: don't stuff anything
@@ -823,8 +859,12 @@ insert_reg(
if (regname != NUL && !valid_yank_reg(regname, FALSE))
return FAIL;
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(regname);
#endif
#ifdef FEAT_CLIPBOARD
regname = may_get_selection(regname);
if (clipmethod != CLIPMETHOD_PROVIDER)
regname = may_get_selection(regname);
#endif
if (regname == '.') // insert last inserted text
@@ -1379,39 +1419,49 @@ op_yank(oparg_T *oap, int deleting, int mess)
decl(&curbuf->b_op_end);
}
#ifdef FEAT_CLIPBOARD
// If we were yanking to the '*' register, send result to clipboard.
// If no register was specified, and "unnamed" in 'clipboard', make a copy
// to the '*' register.
if (clip_star.available
&& (curr == &(y_regs[STAR_REGISTER])
|| (!deleting && oap->regname == 0
&& ((clip_unnamed | clip_unnamed_saved) & CLIP_UNNAMED))))
{
if (curr != &(y_regs[STAR_REGISTER]))
// Copy the text from register 0 to the clipboard register.
copy_yank_reg(&(y_regs[STAR_REGISTER]));
#ifdef FEAT_CLIPBOARD_PROVIDER
if (curr == &y_regs[REAL_PLUS_REGISTER])
call_clip_provider_set('+');
else if (curr == &y_regs[STAR_REGISTER])
call_clip_provider_set('*');
#endif
clip_own_selection(&clip_star);
clip_gen_set_selection(&clip_star);
}
#ifdef FEAT_CLIPBOARD
if (clipmethod != CLIPMETHOD_PROVIDER)
{
// If we were yanking to the '*' register, send result to clipboard. If
// no register was specified, and "unnamed" in 'clipboard', make a copy
// to the '*' register.
if (clip_star.available
&& (curr == &(y_regs[STAR_REGISTER])
|| (!deleting && oap->regname == 0
&& ((clip_unnamed | clip_unnamed_saved) & CLIP_UNNAMED))))
{
if (curr != &(y_regs[STAR_REGISTER]))
// Copy the text from register 0 to the clipboard register.
copy_yank_reg(&(y_regs[STAR_REGISTER]));
clip_own_selection(&clip_star);
clip_gen_set_selection(&clip_star);
}
# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
// If we were yanking to the '+' register, send result to selection.
if (clip_plus.available
&& (curr == &(y_regs[PLUS_REGISTER])
|| (!deleting && oap->regname == 0
&& ((clip_unnamed | clip_unnamed_saved) &
CLIP_UNNAMED_PLUS))))
{
if (curr != &(y_regs[PLUS_REGISTER]))
// Copy the text from register 0 to the clipboard register.
copy_yank_reg(&(y_regs[PLUS_REGISTER]));
// If we were yanking to the '+' register, send result to selection.
if (clip_plus.available
&& (curr == &(y_regs[PLUS_REGISTER])
|| (!deleting && oap->regname == 0
&& ((clip_unnamed | clip_unnamed_saved) &
CLIP_UNNAMED_PLUS))))
{
if (curr != &(y_regs[PLUS_REGISTER]))
// Copy the text from register 0 to the clipboard register.
copy_yank_reg(&(y_regs[PLUS_REGISTER]));
clip_own_selection(&clip_plus);
clip_gen_set_selection(&clip_plus);
}
clip_own_selection(&clip_plus);
clip_gen_set_selection(&clip_plus);
}
# endif
}
#endif
#if defined(FEAT_EVAL)
@@ -1535,11 +1585,18 @@ do_put(
pos_T orig_end = curbuf->b_op_end;
unsigned int cur_ve_flags = get_ve_flags();
#ifdef FEAT_CLIPBOARD
// Adjust register name for "unnamed" in 'clipboard'.
adjust_clip_reg(&regname);
(void)may_get_selection(regname);
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(regname);
#endif
#ifdef FEAT_CLIPBOARD
if (clipmethod != CLIPMETHOD_PROVIDER)
{
// Adjust register name for "unnamed" in 'clipboard'.
adjust_clip_reg(&regname);
(void)may_get_selection(regname);
}
#endif
curbuf->b_op_start = curwin->w_cursor; // default for '[ mark
curbuf->b_op_end = curwin->w_cursor; // default for '] mark
@@ -2326,10 +2383,16 @@ get_register_name(int num)
return num + '0';
else if (num == DELETION_REGISTER)
return '-';
#ifdef FEAT_CLIPBOARD
#if defined(FEAT_CLIPBOARD) || defined(FEAT_CLIPBOARD_PROVIDER)
else if (num == STAR_REGISTER)
return '*';
else if (num == PLUS_REGISTER)
// If there is only one clipboard, we only want the plus register to point
// to the star register if the clipboard provider is not being used. If the
// clipboard provider is being used, then both registers should be available
// no matter the platform
else if (clipmethod == CLIPMETHOD_PROVIDER && num == REAL_PLUS_REGISTER)
return '+';
else if (clipmethod != CLIPMETHOD_PROVIDER && num == PLUS_REGISTER)
return '+';
#endif
else
@@ -2368,6 +2431,10 @@ ex_display(exarg_T *eap)
arg = NULL;
attr = HL_ATTR(HLF_8);
#ifdef FEAT_CLIPBOARD_PROVIDER
inc_clip_provider();
#endif
// Highlight title
msg_puts_title(_("\nType Name Content"));
for (i = -1; i < NUM_REGISTERS && !got_int; ++i)
@@ -2381,18 +2448,24 @@ ex_display(exarg_T *eap)
}
if (arg != NULL && vim_strchr(arg, name) == NULL
#ifdef ONE_CLIPBOARD
// Star register and plus register contain the same thing.
// Star register and plus register contain the same thing.
&& (name != '*' || vim_strchr(arg, '+') == NULL)
#endif
)
continue; // did not ask for this register
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(name);
#endif
#ifdef FEAT_CLIPBOARD
// Adjust register name for "unnamed" in 'clipboard'.
// When it's a clipboard register, fill it with the current contents
// of the clipboard.
adjust_clip_reg(&name);
(void)may_get_selection(name);
if (clipmethod != CLIPMETHOD_PROVIDER)
{
// Adjust register name for "unnamed" in 'clipboard'.
// When it's a clipboard register, fill it with the current contents
// of the clipboard.
adjust_clip_reg(&name);
(void)may_get_selection(name);
}
#endif
if (i == -1)
@@ -2513,6 +2586,10 @@ ex_display(exarg_T *eap)
dis_msg(expr_line, FALSE);
}
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
dec_clip_provider();
#endif
}
/*
@@ -2585,8 +2662,12 @@ get_reg_type(int regname, long *reglen)
return MCHAR;
}
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(regname);
#endif
# ifdef FEAT_CLIPBOARD
regname = may_get_selection(regname);
if (clipmethod != CLIPMETHOD_PROVIDER)
regname = may_get_selection(regname);
# endif
if (regname != NUL && !valid_yank_reg(regname, FALSE))
@@ -2664,8 +2745,12 @@ get_reg_contents(int regname, int flags)
if (regname != NUL && !valid_yank_reg(regname, FALSE))
return NULL;
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_request(regname);
#endif
# ifdef FEAT_CLIPBOARD
regname = may_get_selection(regname);
if (clipmethod != CLIPMETHOD_PROVIDER)
regname = may_get_selection(regname);
# endif
if (get_spec_reg(regname, &retval, &allocated, FALSE))
@@ -2830,6 +2915,10 @@ write_reg_contents_lst(
str_to_reg(y_current, yank_type, (char_u *)strings, -1, block_len, TRUE);
finish_write_reg(name, old_y_previous, old_y_current);
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_set(name);
#endif
}
void
@@ -2904,6 +2993,10 @@ write_reg_contents_ex(
str_to_reg(y_current, yank_type, str, len, block_len, FALSE);
finish_write_reg(name, old_y_previous, old_y_current);
#ifdef FEAT_CLIPBOARD_PROVIDER
call_clip_provider_set(name);
#endif
}
#endif // FEAT_EVAL
+15 -3
View File
@@ -4874,12 +4874,20 @@ typedef enum {
// Symbolic names for some registers.
#define DELETION_REGISTER 36
#ifdef FEAT_CLIPBOARD
#if defined(FEAT_CLIPBOARD) || defined(HAVE_CLIPMETHOD)
# define STAR_REGISTER 37
# if defined(FEAT_X11) || defined(FEAT_WAYLAND)
# define PLUS_REGISTER 38
# define REAL_PLUS_REGISTER PLUS_REGISTER
# else
# define PLUS_REGISTER STAR_REGISTER // there is only one
# ifdef FEAT_EVAL
// Make it so that if clipmethod is "none", the plus register is not available,
// but if clipmethod is a provider, then expose the plus register for use.
# define REAL_PLUS_REGISTER 38
# else
# define REAL_PLUS_REGISTER STAR_REGISTER
# endif
# endif
#endif
#ifdef FEAT_DND
@@ -4890,10 +4898,14 @@ typedef enum {
# ifdef FEAT_DND
# define NUM_REGISTERS (TILDE_REGISTER + 1)
# else
# define NUM_REGISTERS (PLUS_REGISTER + 1)
# define NUM_REGISTERS (REAL_PLUS_REGISTER + 1)
# endif
#else
# define NUM_REGISTERS 37
# ifdef HAVE_CLIPMETHOD
# define NUM_REGISTERS (REAL_PLUS_REGISTER + 1)
# else
# define NUM_REGISTERS 37
# endif
#endif
// structure used by block_prep, op_delete and op_yank for blockwise operators
+55 -3
View File
@@ -3,21 +3,26 @@
source util/window_manager.vim
CheckFeature clipboard_working
CheckFeature xterm_clipboard
CheckFeature wayland_clipboard
CheckUnix
" Test if no available clipmethod sets v:clipmethod to none and deinits clipboard
func Test_no_clipmethod_sets_v_clipmethod_none()
CheckFeature xterm_clipboard
CheckFeature wayland_clipboard
CheckUnix
CheckNotGui
set clipmethod=
call assert_equal("none", v:clipmethod)
call assert_equal(0, has('clipboard_working'))
set clipmethod&
endfunc
" Test if method chosen is in line with clipmethod order
func Test_clipmethod_order()
CheckFeature xterm_clipboard
CheckFeature wayland_clipboard
CheckUnix
CheckNotGui
set cpm=wayland,x11
@@ -60,6 +65,8 @@ func Test_clipmethod_order()
call assert_equal("wayland", v:clipmethod)
call EndWaylandCompositor(l:wayland_display)
set clipmethod&
endfunc
" Test if clipmethod is set to 'none' when gui is started
@@ -83,6 +90,9 @@ endfunc
" Test if :clipreset switches methods when current one doesn't work
func Test_clipreset_switches()
CheckFeature xterm_clipboard
CheckFeature wayland_clipboard
CheckUnix
CheckNotGui
CheckFeature clientserver
CheckXServer
@@ -171,6 +181,48 @@ func Test_clipreset_switches()
" existing, this why WaitForAssert() is used.
call WaitForAssert({-> assert_equal(['SUCCESS'], readfile('Xtest'))}, 1000)
endif
set clipmethod&
endfunc
func s:AAvailable()
return g:a_available
endfunc
func s:BAvailable()
return g:b_available
endfunc
" Test clipmethod when using provider
func Test_clipmethod_provider()
CheckFeature clipboard_provider
let v:clipproviders["a"] = {
\ "available": function("s:AAvailable"),
\ }
let v:clipproviders["b"] = {
\ "available": function("s:BAvailable"),
\ }
let g:a_available = 1
let g:b_available = 1
set clipmethod=a,b
call assert_equal("a", v:clipmethod)
let g:a_available = 0
clipreset
call assert_equal("b", v:clipmethod)
let g:b_available = 0
clipreset
call assert_equal("none", v:clipmethod)
let g:a_available = 1
let g:b_available = 1
clipreset
call assert_equal("a", v:clipmethod)
set clipmethod&
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+356
View File
@@ -727,4 +727,360 @@ func Test_eval_string_in_special_key()
silent! echo 0{1-$"\<S--{>n|%
endfunc
func s:Available()
return get(g:, "vim_cp_available", v:true)
endfunc
func s:Paste(reg)
let l:t = g:vim_paste
if l:t == "tuple"
return ("c", ["a", "tuple", a:reg])
elseif l:t == "list"
return ["c", ["a", "list", a:reg]]
elseif l:t == "block"
return ("b40", ["a", "block"])
elseif l:t == "empty"
return ("c", [])
elseif l:t == "invalid"
return ("INVALID", [])
elseif l:t == "invalid2"
return ("c", ["test", [1, 2]])
elseif l:t == "count"
let g:vim_paste_count[a:reg] += 1
return ("c", ["hello"])
elseif l:t == "store"
let l:s = get(g:, "vim_reg_store", [])
if (len(l:s) > 0)
return (l:s[0], l:s[1])
else
return ("c", [])
endif
endif
endfunc
func s:Copy(reg, type, lines)
if exists("g:vim_copy_count")
let g:vim_copy_count[a:reg] += 1
endif
let g:vim_copy = {
\ "reg": a:reg,
\ "type": a:type,
\ "lines": a:lines
\ }
let g:vim_reg_store = (a:type, a:lines)
endfunc
func Test_clipboard_provider_available()
let g:vim_cp_available = v:true
let v:clipproviders["test"] = {
\ "available": function("s:Available"),
\ }
set clipmethod=test
call assert_equal("test", v:clipmethod)
let g:vim_cp_available = v:false
clipreset
call assert_notequal("test", v:clipmethod)
" Invalid return value
let g:vim_cp_available = "invalid"
call assert_fails('set clipmethod=test', "E474:")
call assert_notequal("test", v:clipmethod)
let v:clipproviders["test"] = {}
clipreset
" Should default to TRUE
call assert_equal("test", v:clipmethod)
set clipmethod&
endfunc
func Test_clipboard_provider_paste()
let v:clipproviders["test"] = {
\ "paste": {
\ '+': function("s:Paste"),
\ '*': function("s:Paste")
\ }
\ }
set clipmethod=test
let g:vim_paste = "tuple"
call assert_equal(["a", "tuple", "+"], getreg("+", 1, 1))
call assert_equal(["a", "tuple", "*"], getreg("*", 1, 1))
let g:vim_paste = "list"
call assert_equal(["a", "list", "+"], getreg("+", 1, 1))
call assert_equal(["a", "list", "*"], getreg("*", 1, 1))
let g:vim_paste = "block"
call assert_equal("40", getregtype("+"))
call assert_equal("40", getregtype("*"))
let g:vim_paste = "empty"
call assert_equal([], getreg("+", 1, 1))
call assert_equal([], getreg("*", 1, 1))
let g:vim_paste = "invalid"
call assert_fails('call getreg("+", 1, 1)', "E474:")
call assert_fails('call getreg("*", 1, 1)', "E474:")
let g:vim_paste = "invalid2"
call assert_fails('call getreg("+", 1, 1)', "E730:")
call assert_fails('call getreg("*", 1, 1)', "E730:")
" Test put
new
let g:vim_paste = "tuple"
put! *
call assert_equal(["a", "tuple", "*"], getline(1, 3))
put +
call assert_equal(["a", "tuple", "+"], getline(4, 6))
bw!
set clipmethod&
endfunc
func Test_clipboard_provider_copy()
let v:clipproviders["test"] = {
\ "copy": {
\ '+': function("s:Copy"),
\ '*': function("s:Copy")
\ }
\ }
set clipmethod=test
" Test charwise
call setreg("+", ["hello", "world!"], "c")
call assert_equal("+",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("v", g:vim_copy.type)
call setreg("*", ["hello", "world!"], "c")
call assert_equal("*",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("v", g:vim_copy.type)
" Test linewise
call setreg("+", ["hello", "world!"], "l")
call assert_equal("+",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("V", g:vim_copy.type)
call setreg("*", ["hello", "world!"], "l")
call assert_equal("*",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("V", g:vim_copy.type)
" Test blockwise
call setreg("+", ["hello", "world!"], "b40")
call assert_equal("+",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("40", g:vim_copy.type)
call setreg("*", ["hello", "world!"], "b40")
call assert_equal("*",g:vim_copy.reg)
call assert_equal(["hello", "world!"], g:vim_copy.lines)
call assert_equal("40", g:vim_copy.type)
" Test yanking
new
call setline(1, "TESTING")
yank *
call assert_equal("*",g:vim_copy.reg)
call assert_equal(["TESTING"], g:vim_copy.lines)
call assert_equal("V", g:vim_copy.type)
call setline(1, "TESTING2")
yank +
call assert_equal("+",g:vim_copy.reg)
call assert_equal(["TESTING2"], g:vim_copy.lines)
call assert_equal("V", g:vim_copy.type)
bw!
set clipmethod&
endfunc
" Test on platforms where only the * register is available (+ points to *),
" and that the clipboard provider feature exposes the + register only when
" clipmethod is set to a provider. If not, then the plus register points to the
" star register like normal.
func Test_clipboard_provider_no_unamedplus()
CheckNotFeature unnamedplus
CheckFeature clipboard_working
let g:vim_paste = "store"
let v:clipproviders["test"] = {}
set clipmethod=test
call assert_equal(1, has('unnamedplus'))
call setreg("+", ["plus"], "c")
call setreg("*", ["star"], "c")
call assert_equal("plus", getreg("+"))
call assert_equal("star", getreg("*"))
set clipmethod=
call assert_equal(0, has('unnamedplus'))
call setreg("+", ["plus2"], "c")
call setreg("*", ["star2"], "c")
call assert_equal("star2", getreg("+"))
call assert_equal("star2", getreg("*"))
set clipmethod&
endfunc
" Same as Test_clipboard_provider_registers() but do it when +clipboard isnt
" enabled.
func Test_clipboard_provider_no_clipboard()
CheckNotFeature clipboard
let v:clipproviders["test"] = {
\ "paste": {
\ '+': function("s:Paste"),
\ '*': function("s:Paste")
\ },
\ "copy": {
\ '+': function("s:Copy"),
\ '*': function("s:Copy")
\ }
\ }
call assert_fails('call setreg("+", "")', 'E354:')
call assert_fails('call setreg("*", "")', 'E354:')
let g:vim_paste = "tuple"
set clipmethod=test
call assert_equal(["a", "tuple", "+"], getreg("+", 1, 1))
call assert_equal(["a", "tuple", "*"], getreg("*", 1, 1))
set clipmethod&
endfunc
" Test if clipboard provider feature doesn't break existing clipboard
" functionality.
func Test_clipboard_provider_sys_clipboard()
CheckFeature clipboard_working
let v:clipproviders["test"] = {
\ "paste": {
\ '+': function("s:Paste"),
\ '*': function("s:Paste")
\ },
\ "copy": {
\ '+': function("s:Copy"),
\ '*': function("s:Copy")
\ }
\ }
call setreg("*", "hello world from the * register!", "c")
call assert_equal("hello world from the * register!", getreg("*"))
call setreg("+", "hello world from the + register!", "c")
call assert_equal("hello world from the + register!", getreg("+"))
let g:vim_paste = "tuple"
set clipmethod=test
call assert_equal(1, has('clipboard_working'))
call setreg("*", "hello world!", "c")
call assert_equal(["a", "tuple", "*"], getreg("*", 1, 1))
call assert_equal(["a", "tuple", "+"], getreg("+", 1, 1))
new
call setline(1, "TESTING")
yank *
call assert_equal("*",g:vim_copy.reg)
call assert_equal(["TESTING"], g:vim_copy.lines)
call assert_equal("V", g:vim_copy.type)
put *
call assert_equal(["TESTING", "a", "tuple", "*"], getline(1, 4))
bw!
set clipmethod&
call setreg("*", "testing 1 2 3", "c")
call assert_equal("testing 1 2 3", getreg("*"))
call setreg("+", "testing 1 2 3 4 5", "c")
call assert_equal("testing 1 2 3 4 5", getreg("+"))
set clipmethod&
endfunc
" Test if the provider callback are only called once per register on operations
" that may try calling them multiple times.
func Test_clipboard_provider_accessed_once()
let v:clipproviders["test"] = {
\ "paste": {
\ '+': function("s:Paste"),
\ '*': function("s:Paste")
\ },
\ "copy": {
\ '+': function("s:Copy"),
\ '*': function("s:Copy")
\ }
\ }
set clipmethod=test
let g:vim_paste = "count"
let g:vim_paste_count = {'*': 0, '+': 0}
let g:vim_copy_count = {'*': 0, '+': 0}
" Test if the paste callback is only called once per register when the
" :registers/:display cmd is run.
for i in range(1, 10)
registers
call assert_equal(i, g:vim_paste_count['*'])
call assert_equal(i, g:vim_paste_count['+'])
endfor
let g:vim_paste_count = {'*': 0, '+': 0}
let g:vim_copy_count = {'*': 0, '+': 0}
" Test same for :global
new
call setline(1, "The quick brown fox jumps over the lazy dog")
call execute(':global/quick/:put +')
call execute(':global/quick/:put *')
call assert_equal(1, g:vim_paste_count['+'])
call assert_equal(1, g:vim_paste_count['*'])
call execute(':global/quick/:yank +')
call execute(':global/quick/:yank *')
call assert_equal(1, g:vim_copy_count['+'])
call assert_equal(1, g:vim_copy_count['*'])
bw!
set clipmethod&
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+13 -7
View File
@@ -522,15 +522,21 @@ func Test_set_completion_string_values()
call assert_equal('unload', getcompletion('set bufhidden=', 'cmdline')[1])
call assert_equal('nowrite', getcompletion('set buftype=', 'cmdline')[1])
call assert_equal('internal', getcompletion('set casemap=', 'cmdline')[1])
if exists('+clipboard')
if has('clipboard')
call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
endif
if exists('+clipmethod')
if has('unix') || has('vms')
call assert_match('wayland', getcompletion('set clipmethod=', 'cmdline')[1])
else
call assert_match('wayland', getcompletion('set clipmethod=', 'cmdline')[0])
endif
if has('wayland_clipboard')
call assert_match('wayland', getcompletion('set clipmethod=w', 'cmdline')[0])
endif
if has('xterm_clipboard')
call assert_match('x11', getcompletion('set clipmethod=x', 'cmdline')[0])
endif
if has('eval')
let v:clipproviders["first"] = {}
let v:clipproviders["second"] = {}
call assert_match('first', getcompletion('set clipmethod=f', 'cmdline')[0])
call assert_match('second', getcompletion('set clipmethod=s', 'cmdline')[0])
endif
call assert_equal('.', getcompletion('set complete=', 'cmdline')[1])
call assert_equal('menu', getcompletion('set completeopt=', 'cmdline')[1])
+7
View File
@@ -155,6 +155,11 @@ static char *(features[]) =
"+clipboard",
#else
"-clipboard",
#endif
#ifdef FEAT_CLIPBOARD_PROVIDER
"+clipboard_provider",
#else
"-clipboard_provider",
#endif
"+cmdline_compl",
"+cmdline_hist",
@@ -729,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1972,
/**/
1971,
/**/
+22 -8
View File
@@ -2042,6 +2042,16 @@ typedef __int64 sock_T;
typedef int sock_T;
#endif
// The clipboard provider feature uses clipmethod as well but should be separate
// from the clipboard code.
#if defined(FEAT_CLIPBOARD) || defined(FEAT_EVAL)
# define HAVE_CLIPMETHOD
#endif
#if defined(HAVE_CLIPMETHOD) && defined(FEAT_EVAL)
# define FEAT_CLIPBOARD_PROVIDER
#endif
// Include option.h before structs.h, because the number of window-local and
// buffer-local options is used there.
#include "option.h" // options and default values
@@ -2260,7 +2270,8 @@ typedef int sock_T;
#define VV_TERMDA1 114
#define VV_TERMOSC 115
#define VV_VIM_DID_INIT 116
#define VV_LEN 117 // number of v: vars
#define VV_CLIPPROVIDERS 117
#define VV_LEN 118 // number of v: vars
// used for v_number in VAR_BOOL and VAR_SPECIAL
#define VVAL_FALSE 0L // VAR_BOOL
@@ -2292,6 +2303,16 @@ typedef int sock_T;
#define TABSTOP_MAX 9999
#ifdef HAVE_CLIPMETHOD
typedef enum {
CLIPMETHOD_FAIL,
CLIPMETHOD_NONE,
CLIPMETHOD_WAYLAND,
CLIPMETHOD_X11,
CLIPMETHOD_PROVIDER
} clipmethod_T;
#endif
#ifdef FEAT_CLIPBOARD
// VIM_ATOM_NAME is the older Vim-specific selection type for X11. Still
@@ -2315,13 +2336,6 @@ typedef int sock_T;
# endif
# endif
typedef enum {
CLIPMETHOD_FAIL,
CLIPMETHOD_NONE,
CLIPMETHOD_WAYLAND,
CLIPMETHOD_X11,
} clipmethod_T;
// Info about selected text
typedef struct
{
+2 -2
View File
@@ -1907,9 +1907,9 @@ write_viminfo_registers(FILE *fp)
for (i = 0; i < NUM_REGISTERS; i++)
{
#ifdef FEAT_CLIPBOARD
#if defined(FEAT_CLIPBOARD) || defined(FEAT_CLIPBOARD_PROVIDER)
// Skip '*'/'+' register, we don't want them back next time
if (i == STAR_REGISTER || i == PLUS_REGISTER)
if (i == STAR_REGISTER || i == PLUS_REGISTER || i == REAL_PLUS_REGISTER)
continue;
#endif
#ifdef FEAT_DND