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

Commit: 
https://github.com/vim/vim/commit/fcd3958dcb62011e865204f9c82da648f3635eae
Author: Foxe Chen <[email protected]>
Date:   Thu Dec 11 21:11:03 2025 +0100

    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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 4f1b0b42f..ebb6d5ba7 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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:
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 001786456..a67864475 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
diff --git a/runtime/doc/tags b/runtime/doc/tags
index bc900fdf2..79c3a6fe0 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -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*
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 42b0b6657..89fce0d93 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -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
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index a736370f0..ff6de1dc5 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -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.
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 050880215..17161fad4 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <[email protected]>
-" 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 ---
 
diff --git a/src/clipboard.c b/src/clipboard.c
index af3fee308..51f705430 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -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
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 222fa4017..49b7c72b2 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -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;
diff --git a/src/evalvars.c b/src/evalvars.c
index ca71562d4..af14c8b0d 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -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
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 8e861d5a2..e8343cae4 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -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
        }
 
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index 6b8fd2627..6e9749f5d 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -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
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 3f31460a0..bc3c91252 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -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
 
diff --git a/src/globals.h b/src/globals.h
index f412ab5fc..180e3f44f 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -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
 
diff --git a/src/gui.c b/src/gui.c
index 9487fd193..dd81d4c99 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -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
diff --git a/src/main.c b/src/main.c
index 14ae3eaf3..e42aafaa9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -704,7 +704,7 @@ vim_main2(void)
     }
 #endif
 
-#ifdef FEAT_CLIPBOARD
+#ifdef HAVE_CLIPMETHOD
     choose_clipmethod();
 #endif
 
diff --git a/src/option.h b/src/option.h
index df073b2b0..87096fa60 100644
--- a/src/option.h
+++ b/src/option.h
@@ -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'
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 59d363ffa..81d9ff76e 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -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}
diff --git a/src/optionstr.c b/src/optionstr.c
index 106e9003d..42e09b3c9 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -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
 
diff --git a/src/proto/clipboard.pro b/src/proto/clipboard.pro
index 5727aa8b7..6ada18c9e 100644
--- a/src/proto/clipboard.pro
+++ b/src/proto/clipboard.pro
@@ -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 : */
diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro
index 627af17a8..dad8c6771 100644
--- a/src/proto/evalfunc.pro
+++ b/src/proto/evalfunc.pro
@@ -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 : */
diff --git a/src/register.c b/src/register.c
index 52afe793c..589d07f71 100644
--- a/src/register.c
+++ b/src/register.c
@@ -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 (clip_isautosel_star())
-           clip_update_selection(&clip_star);
-       may_get_selection(name);
-    }
-    if (name == '+' && clip_plus.available)
+    if (clipmethod != CLIPMETHOD_PROVIDER)
     {
-       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_PROVIDER
+    if (curr == &y_regs[REAL_PLUS_REGISTER])
+       call_clip_provider_set('+');
+    else if (curr == &y_regs[STAR_REGISTER])
+       call_clip_provider_set('*');
+#endif
+
 #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]));
+    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);
-    }
+           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,12 +1585,19 @@ do_put(
     pos_T      orig_end = curbuf->b_op_end;
     unsigned int cur_ve_flags = get_ve_flags();
 
+#ifdef FEAT_CLIPBOARD_PROVIDER
+    call_clip_provider_request(regname);
+#endif
 #ifdef FEAT_CLIPBOARD
-    // Adjust register name for "unnamed" in 'clipboard'.
-    adjust_clip_reg(&regname);
-    (void)may_get_selection(regname);
+    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(_("
Type 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
 
diff --git a/src/structs.h b/src/structs.h
index 3462aeca9..37c8ac6c0 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -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
diff --git a/src/testdir/test_clipmethod.vim b/src/testdir/test_clipmethod.vim
index 9b3465842..3e98828bf 100644
--- a/src/testdir/test_clipmethod.vim
+++ b/src/testdir/test_clipmethod.vim
@@ -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
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index 39bb4ba9f..49fa9509e 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
@@ -727,4 +727,360 @@ func Test_eval_string_in_special_key()
   silent! echo 0{1-$"\<S--{> n|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
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 3abc067b3..2e4e9f727 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -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])
diff --git a/src/version.c b/src/version.c
index 922242d91..e754b6021 100644
--- a/src/version.c
+++ b/src/version.c
@@ -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,
 /**/
diff --git a/src/vim.h b/src/vim.h
index bc1a1d47f..fccb31b3d 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -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
 {
diff --git a/src/viminfo.c b/src/viminfo.c
index 7f6313ba5..c87f54a1f 100644
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -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

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vTn47-00AGLt-QX%40256bit.org.

Raspunde prin e-mail lui