mirror of
https://github.com/neovim/neovim.git
synced 2025-09-12 14:28:18 +00:00
Merge pull request #1182 from bfredl/clipboard
clipboard: support separate +/* clipboards, linewise copy/paste and add tests
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
" The clipboard provider uses shell commands to communicate with the clipboard.
|
" The clipboard provider uses shell commands to communicate with the clipboard.
|
||||||
" The provider function will only be registered if one of the supported
|
" The provider function will only be registered if one of the supported
|
||||||
" commands are available.
|
" commands are available.
|
||||||
let s:copy = ''
|
let s:copy = {}
|
||||||
let s:paste = ''
|
let s:paste = {}
|
||||||
|
|
||||||
function! s:try_cmd(cmd, ...)
|
function! s:try_cmd(cmd, ...)
|
||||||
let out = a:0 ? systemlist(a:cmd, a:1) : systemlist(a:cmd)
|
let out = a:0 ? systemlist(a:cmd, a:1, 1) : systemlist(a:cmd, [''], 1)
|
||||||
if v:shell_error
|
if v:shell_error
|
||||||
echo "clipboard: error: ".(len(out) ? out[0] : '')
|
echo "clipboard: error: ".(len(out) ? out[0] : '')
|
||||||
return ''
|
return ''
|
||||||
@@ -14,14 +14,20 @@ function! s:try_cmd(cmd, ...)
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
if executable('pbcopy')
|
if executable('pbcopy')
|
||||||
let s:copy = 'pbcopy'
|
let s:copy['+'] = 'pbcopy'
|
||||||
let s:paste = 'pbpaste'
|
let s:paste['+'] = 'pbpaste'
|
||||||
elseif executable('xsel')
|
let s:copy['*'] = s:copy['+']
|
||||||
let s:copy = 'xsel -i -b'
|
let s:paste['*'] = s:paste['+']
|
||||||
let s:paste = 'xsel -o -b'
|
|
||||||
elseif executable('xclip')
|
elseif executable('xclip')
|
||||||
let s:copy = 'xclip -i -selection clipboard'
|
let s:copy['+'] = 'xclip -i -selection clipboard'
|
||||||
let s:paste = 'xclip -o -selection clipboard'
|
let s:paste['+'] = 'xclip -o -selection clipboard'
|
||||||
|
let s:copy['*'] = 'xclip -i -selection primary'
|
||||||
|
let s:paste['*'] = 'xclip -o -selection primary'
|
||||||
|
elseif executable('xsel')
|
||||||
|
let s:copy['+'] = 'xsel -i -b'
|
||||||
|
let s:paste['+'] = 'xsel -o -b'
|
||||||
|
let s:copy['*'] = 'xsel -i -p'
|
||||||
|
let s:paste['*'] = 'xsel -o -p'
|
||||||
else
|
else
|
||||||
echom 'clipboard: No shell command for communicating with the clipboard found.'
|
echom 'clipboard: No shell command for communicating with the clipboard found.'
|
||||||
finish
|
finish
|
||||||
@@ -29,14 +35,14 @@ endif
|
|||||||
|
|
||||||
let s:clipboard = {}
|
let s:clipboard = {}
|
||||||
|
|
||||||
function! s:clipboard.get(...)
|
function! s:clipboard.get(reg)
|
||||||
return s:try_cmd(s:paste)
|
return s:try_cmd(s:paste[a:reg])
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:clipboard.set(...)
|
function! s:clipboard.set(lines, regtype, reg)
|
||||||
call s:try_cmd(s:copy, a:1)
|
call s:try_cmd(s:copy[a:reg], a:lines)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! provider#clipboard#Call(method, args)
|
function! provider#clipboard#Call(method, args)
|
||||||
return s:clipboard[a:method](a:args)
|
return call(s:clipboard[a:method],a:args,s:clipboard)
|
||||||
endfunction
|
endfunction
|
||||||
|
@@ -7547,13 +7547,6 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
Note that this causes the whole buffer to be stored in memory. Set
|
Note that this causes the whole buffer to be stored in memory. Set
|
||||||
this option to a lower value if you run out of memory.
|
this option to a lower value if you run out of memory.
|
||||||
|
|
||||||
{Nvim} *'unnamedclip'* *ucp'*
|
|
||||||
'unnamedclip' 'ucp' boolean (default off)
|
|
||||||
global
|
|
||||||
Use the unnamed register to access the clipboard(when available).
|
|
||||||
This option has the same effect of setting 'clipboard' to
|
|
||||||
'unnamed/unnamedplus' in Vim.
|
|
||||||
|
|
||||||
*'updatecount'* *'uc'*
|
*'updatecount'* *'uc'*
|
||||||
'updatecount' 'uc' number (default: 200)
|
'updatecount' 'uc' number (default: 200)
|
||||||
global
|
global
|
||||||
|
@@ -5118,6 +5118,20 @@ void list_append_tv(list_T *l, typval_T *tv)
|
|||||||
list_append(l, li);
|
list_append(l, li);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a list to a list.
|
||||||
|
*/
|
||||||
|
void list_append_list(list_T *list, list_T *itemlist)
|
||||||
|
{
|
||||||
|
listitem_T *li = listitem_alloc();
|
||||||
|
|
||||||
|
li->li_tv.v_type = VAR_LIST;
|
||||||
|
li->li_tv.v_lock = 0;
|
||||||
|
li->li_tv.vval.v_list = itemlist;
|
||||||
|
list_append(list, li);
|
||||||
|
++list->lv_refcount;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a dictionary to a list. Used by getqflist().
|
* Add a dictionary to a list. Used by getqflist().
|
||||||
*/
|
*/
|
||||||
@@ -19815,16 +19829,12 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
|
|||||||
|
|
||||||
bool eval_has_provider(char *name)
|
bool eval_has_provider(char *name)
|
||||||
{
|
{
|
||||||
#define source_provider(name) \
|
|
||||||
do_source((uint8_t *)"$VIMRUNTIME/autoload/provider/" name ".vim", \
|
|
||||||
false, \
|
|
||||||
false)
|
|
||||||
|
|
||||||
#define check_provider(name) \
|
#define check_provider(name) \
|
||||||
if (has_##name == -1) { \
|
if (has_##name == -1) { \
|
||||||
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
||||||
if (!has_##name) { \
|
if (!has_##name) { \
|
||||||
source_provider(#name); \
|
script_autoload((uint8_t *)"provider#" #name "#Call", false); \
|
||||||
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
@@ -915,14 +915,7 @@ getcount:
|
|||||||
&& !oap->op_type
|
&& !oap->op_type
|
||||||
&& (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG))) {
|
&& (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG))) {
|
||||||
clearop(oap);
|
clearop(oap);
|
||||||
{
|
set_reg_var(0);
|
||||||
int regname = 0;
|
|
||||||
|
|
||||||
/* Adjust the register according to 'clipboard', so that when
|
|
||||||
* "unnamed" is present it becomes '*' or '+' instead of '"'. */
|
|
||||||
adjust_clipboard_register(®name);
|
|
||||||
set_reg_var(regname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the length of mapped chars again after typing a count, second
|
/* Get the length of mapped chars again after typing a count, second
|
||||||
@@ -5105,7 +5098,6 @@ static void nv_brackets(cmdarg_T *cap)
|
|||||||
end = equalpos(start, VIsual) ? curwin->w_cursor : VIsual;
|
end = equalpos(start, VIsual) ? curwin->w_cursor : VIsual;
|
||||||
curwin->w_cursor = (dir == BACKWARD ? start : end);
|
curwin->w_cursor = (dir == BACKWARD ? start : end);
|
||||||
}
|
}
|
||||||
adjust_clipboard_register(®name);
|
|
||||||
prep_redo_cmd(cap);
|
prep_redo_cmd(cap);
|
||||||
do_put(regname, dir, cap->count1, PUT_FIXINDENT);
|
do_put(regname, dir, cap->count1, PUT_FIXINDENT);
|
||||||
if (was_visual) {
|
if (was_visual) {
|
||||||
@@ -7272,10 +7264,8 @@ static void nv_put(cmdarg_T *cap)
|
|||||||
*/
|
*/
|
||||||
was_visual = true;
|
was_visual = true;
|
||||||
regname = cap->oap->regname;
|
regname = cap->oap->regname;
|
||||||
bool adjusted = adjust_clipboard_register(®name);
|
|
||||||
if (regname == 0 || regname == '"'
|
if (regname == 0 || regname == '"'
|
||||||
|| VIM_ISDIGIT(regname) || regname == '-'
|
|| VIM_ISDIGIT(regname) || regname == '-'
|
||||||
|| adjusted
|
|
||||||
) {
|
) {
|
||||||
/* The delete is going to overwrite the register we want to
|
/* The delete is going to overwrite the register we want to
|
||||||
* put, save it first. */
|
* put, save it first. */
|
||||||
|
163
src/nvim/ops.c
163
src/nvim/ops.c
@@ -60,6 +60,7 @@
|
|||||||
#define DELETION_REGISTER 36
|
#define DELETION_REGISTER 36
|
||||||
#define CLIP_REGISTER 37
|
#define CLIP_REGISTER 37
|
||||||
|
|
||||||
|
# define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
|
||||||
/*
|
/*
|
||||||
* Each yank register is an array of pointers to lines.
|
* Each yank register is an array of pointers to lines.
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +75,7 @@ static struct yankreg *y_current; /* ptr to current yankreg */
|
|||||||
static int y_append; /* TRUE when appending */
|
static int y_append; /* TRUE when appending */
|
||||||
static struct yankreg *y_previous = NULL; /* ptr to last written yankreg */
|
static struct yankreg *y_previous = NULL; /* ptr to last written yankreg */
|
||||||
|
|
||||||
|
static bool clipboard_didwarn_unnamed = false;
|
||||||
/*
|
/*
|
||||||
* structure used by block_prep, op_delete and op_yank for blockwise operators
|
* structure used by block_prep, op_delete and op_yank for blockwise operators
|
||||||
* also op_change, op_shift, op_insert, op_replace - AKelly
|
* also op_change, op_shift, op_insert, op_replace - AKelly
|
||||||
@@ -751,7 +753,8 @@ void get_yank_register(int regname, int writing)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
y_append = FALSE;
|
y_append = FALSE;
|
||||||
if ((regname == 0 || regname == '"') && !writing && y_previous != NULL) {
|
int unnamedclip = cb_flags & CB_UNNAMEDMASK;
|
||||||
|
if ((regname == 0 || regname == '"') && !unnamedclip && !writing && y_previous != NULL) {
|
||||||
y_current = y_previous;
|
y_current = y_previous;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1302,18 +1305,6 @@ cmdline_paste_reg (
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool adjust_clipboard_register(int *rp)
|
|
||||||
{
|
|
||||||
// If no reg. specified and 'unnamedclip' is set, use the
|
|
||||||
// clipboard register.
|
|
||||||
if (*rp == 0 && p_unc && eval_has_provider("clipboard")) {
|
|
||||||
*rp = '+';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle a delete operation.
|
* Handle a delete operation.
|
||||||
*
|
*
|
||||||
@@ -1328,7 +1319,6 @@ int op_delete(oparg_T *oap)
|
|||||||
struct block_def bd;
|
struct block_def bd;
|
||||||
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
|
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
|
||||||
int did_yank = FALSE;
|
int did_yank = FALSE;
|
||||||
int orig_regname = oap->regname;
|
|
||||||
|
|
||||||
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */
|
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */
|
||||||
return OK;
|
return OK;
|
||||||
@@ -1342,8 +1332,6 @@ int op_delete(oparg_T *oap)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool adjusted = adjust_clipboard_register(&oap->regname);
|
|
||||||
|
|
||||||
if (has_mbyte)
|
if (has_mbyte)
|
||||||
mb_adjust_opend(oap);
|
mb_adjust_opend(oap);
|
||||||
|
|
||||||
@@ -1393,9 +1381,10 @@ int op_delete(oparg_T *oap)
|
|||||||
* register. For the black hole register '_' don't yank anything.
|
* register. For the black hole register '_' don't yank anything.
|
||||||
*/
|
*/
|
||||||
if (oap->regname != '_') {
|
if (oap->regname != '_') {
|
||||||
if (oap->regname != 0) {
|
bool unnamedclip = oap->regname == 0 && (cb_flags & CB_UNNAMEDMASK);
|
||||||
|
if (oap->regname != 0 || unnamedclip) {
|
||||||
/* check for read-only register */
|
/* check for read-only register */
|
||||||
if (!valid_yank_reg(oap->regname, TRUE)) {
|
if (!( valid_yank_reg(oap->regname, TRUE) || unnamedclip )) {
|
||||||
beep_flush();
|
beep_flush();
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -1407,10 +1396,8 @@ int op_delete(oparg_T *oap)
|
|||||||
/*
|
/*
|
||||||
* Put deleted text into register 1 and shift number registers if the
|
* Put deleted text into register 1 and shift number registers if the
|
||||||
* delete contains a line break, or when a regname has been specified.
|
* delete contains a line break, or when a regname has been specified.
|
||||||
* Use the register name from before adjust_clip_reg() may have
|
|
||||||
* changed it.
|
|
||||||
*/
|
*/
|
||||||
if (orig_regname != 0 || oap->motion_type == MLINE
|
if (oap->regname != 0 || oap->motion_type == MLINE
|
||||||
|| oap->line_count > 1 || oap->use_reg_one) {
|
|| oap->line_count > 1 || oap->use_reg_one) {
|
||||||
y_current = &y_regs[9];
|
y_current = &y_regs[9];
|
||||||
free_yank_all(); /* free register nine */
|
free_yank_all(); /* free register nine */
|
||||||
@@ -1424,9 +1411,7 @@ int op_delete(oparg_T *oap)
|
|||||||
|
|
||||||
/* Yank into small delete register when no named register specified
|
/* Yank into small delete register when no named register specified
|
||||||
* and the delete is within one line. */
|
* and the delete is within one line. */
|
||||||
if ((
|
if (oap->regname == 0 && oap->motion_type != MLINE
|
||||||
adjusted ||
|
|
||||||
oap->regname == 0) && oap->motion_type != MLINE
|
|
||||||
&& oap->line_count == 1) {
|
&& oap->line_count == 1) {
|
||||||
oap->regname = '-';
|
oap->regname = '-';
|
||||||
get_yank_register(oap->regname, TRUE);
|
get_yank_register(oap->regname, TRUE);
|
||||||
@@ -2623,7 +2608,6 @@ do_put (
|
|||||||
int allocated = FALSE;
|
int allocated = FALSE;
|
||||||
long cnt;
|
long cnt;
|
||||||
|
|
||||||
adjust_clipboard_register(®name);
|
|
||||||
get_clipboard(regname);
|
get_clipboard(regname);
|
||||||
|
|
||||||
if (flags & PUT_FIXINDENT)
|
if (flags & PUT_FIXINDENT)
|
||||||
@@ -3215,7 +3199,6 @@ void ex_display(exarg_T *eap)
|
|||||||
)
|
)
|
||||||
continue; /* did not ask for this register */
|
continue; /* did not ask for this register */
|
||||||
|
|
||||||
adjust_clipboard_register(&name);
|
|
||||||
get_clipboard(name);
|
get_clipboard(name);
|
||||||
|
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
@@ -4842,6 +4825,7 @@ void write_reg_contents_ex(int name,
|
|||||||
if (!y_append && !must_append)
|
if (!y_append && !must_append)
|
||||||
free_yank_all();
|
free_yank_all();
|
||||||
str_to_reg(y_current, yank_type, str, len, block_len);
|
str_to_reg(y_current, yank_type, str, len, block_len);
|
||||||
|
set_clipboard(name);
|
||||||
|
|
||||||
|
|
||||||
/* ':let @" = "val"' should change the meaning of the "" register */
|
/* ':let @" = "val"' should change the meaning of the "" register */
|
||||||
@@ -5225,33 +5209,82 @@ static void free_register(struct yankreg *reg)
|
|||||||
y_current = curr;
|
y_current = curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copy_register(struct yankreg *dest, struct yankreg *src)
|
// return target register
|
||||||
{
|
static int adjust_clipboard_name(int *name) {
|
||||||
free_register(dest);
|
if (*name == '*' || *name == '+') {
|
||||||
*dest = *src;
|
if(!eval_has_provider("clipboard")) {
|
||||||
dest->y_array = xcalloc(src->y_size, sizeof(uint8_t *));
|
EMSG("clipboard: provider is not available");
|
||||||
for (int j = 0; j < src->y_size; ++j) {
|
return -1;
|
||||||
dest->y_array[j] = (uint8_t *)xstrdup((char *)src->y_array[j]);
|
}
|
||||||
|
return CLIP_REGISTER;
|
||||||
|
} else if (*name == NUL && (cb_flags & (CB_UNNAMED | CB_UNNAMEDPLUS))) {
|
||||||
|
if(!eval_has_provider("clipboard")) {
|
||||||
|
if (!clipboard_didwarn_unnamed) {
|
||||||
|
msg((char_u*)"clipboard: provider not available, ignoring clipboard=unnamed[plus]");
|
||||||
|
clipboard_didwarn_unnamed = true;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (cb_flags & CB_UNNAMEDPLUS) {
|
||||||
|
*name = '+';
|
||||||
|
} else {
|
||||||
|
*name = '*';
|
||||||
|
}
|
||||||
|
return 0; //unnamed
|
||||||
}
|
}
|
||||||
|
// don't do anything for other register names
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_clipboard(int name)
|
static void get_clipboard(int name)
|
||||||
{
|
{
|
||||||
if (!(name == '*' || name == '+'
|
int ireg = adjust_clipboard_name(&name);
|
||||||
|| (p_unc && !name && eval_has_provider("clipboard")))) {
|
if (ireg < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct yankreg *reg = &y_regs[CLIP_REGISTER];
|
struct yankreg *reg = &y_regs[ireg];
|
||||||
free_register(reg);
|
free_register(reg);
|
||||||
|
|
||||||
list_T *args = list_alloc();
|
list_T *args = list_alloc();
|
||||||
|
char_u regname = name;
|
||||||
|
list_append_string(args, ®name, 1);
|
||||||
|
|
||||||
typval_T result = eval_call_provider("clipboard", "get", args);
|
typval_T result = eval_call_provider("clipboard", "get", args);
|
||||||
|
|
||||||
if (result.v_type != VAR_LIST) {
|
if (result.v_type != VAR_LIST) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_T *lines = result.vval.v_list;
|
list_T *res = result.vval.v_list, *lines = NULL;
|
||||||
|
if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) {
|
||||||
|
lines = res->lv_first->li_tv.vval.v_list;
|
||||||
|
if (res->lv_last->li_tv.v_type != VAR_STRING) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
char_u* regtype = res->lv_last->li_tv.vval.v_string;
|
||||||
|
if (regtype == NULL || strlen((char*)regtype) != 1) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
switch (regtype[0]) {
|
||||||
|
case 'v': case 'c':
|
||||||
|
reg->y_type = MCHAR;
|
||||||
|
break;
|
||||||
|
case 'V': case 'l':
|
||||||
|
reg->y_type = MLINE;
|
||||||
|
break;
|
||||||
|
case 'b': case Ctrl_V:
|
||||||
|
reg->y_type = MBLOCK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lines = res;
|
||||||
|
// provider did not specify regtype, calculate it below
|
||||||
|
reg->y_type = MAUTO;
|
||||||
|
}
|
||||||
|
|
||||||
reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *));
|
reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *));
|
||||||
reg->y_size = lines->lv_len;
|
reg->y_size = lines->lv_len;
|
||||||
|
|
||||||
@@ -5263,9 +5296,23 @@ static void get_clipboard(int name)
|
|||||||
reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string);
|
reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name && p_unc) {
|
if (reg->y_type == MAUTO) {
|
||||||
// copy to the unnamed register
|
if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
|
||||||
copy_register(&y_regs[0], reg);
|
reg->y_type = MLINE;
|
||||||
|
free(reg->y_array[reg->y_size-1]);
|
||||||
|
reg->y_size--;
|
||||||
|
} else {
|
||||||
|
reg->y_type = MCHAR;
|
||||||
|
}
|
||||||
|
} else if (reg->y_type == MBLOCK) {
|
||||||
|
int maxlen = 0;
|
||||||
|
for (int i = 0; i < reg->y_size; i++) {
|
||||||
|
int rowlen = STRLEN(reg->y_array[i]);
|
||||||
|
if (rowlen > maxlen) {
|
||||||
|
maxlen = rowlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg->y_width = maxlen-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -5279,22 +5326,17 @@ err:
|
|||||||
}
|
}
|
||||||
reg->y_array = NULL;
|
reg->y_array = NULL;
|
||||||
reg->y_size = 0;
|
reg->y_size = 0;
|
||||||
EMSG("Clipboard provider returned invalid data");
|
EMSG("clipboard: provider returned invalid data");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_clipboard(int name)
|
static void set_clipboard(int name)
|
||||||
{
|
{
|
||||||
if (!(name == '*' || name == '+'
|
int ireg = adjust_clipboard_name(&name);
|
||||||
|| (p_unc && !name && eval_has_provider("clipboard")))) {
|
if (ireg < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct yankreg *reg = &y_regs[CLIP_REGISTER];
|
struct yankreg *reg = &y_regs[ireg];
|
||||||
|
|
||||||
if (!name && p_unc) {
|
|
||||||
// copy from the unnamed register
|
|
||||||
copy_register(reg, &y_regs[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_T *lines = list_alloc();
|
list_T *lines = list_alloc();
|
||||||
|
|
||||||
@@ -5302,5 +5344,26 @@ static void set_clipboard(int name)
|
|||||||
list_append_string(lines, reg->y_array[i], -1);
|
list_append_string(lines, reg->y_array[i], -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)eval_call_provider("clipboard", "set", lines);
|
list_T *args = list_alloc();
|
||||||
|
list_append_list(args, lines);
|
||||||
|
|
||||||
|
char_u regtype;
|
||||||
|
switch (reg->y_type) {
|
||||||
|
case MLINE:
|
||||||
|
regtype = 'V';
|
||||||
|
list_append_string(lines, (char_u*)"", 0);
|
||||||
|
break;
|
||||||
|
case MCHAR:
|
||||||
|
regtype = 'v';
|
||||||
|
break;
|
||||||
|
case MBLOCK:
|
||||||
|
regtype = 'b';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list_append_string(args, ®type, 1);
|
||||||
|
|
||||||
|
char_u regname = name;
|
||||||
|
list_append_string(args, ®name, 1);
|
||||||
|
|
||||||
|
(void)eval_call_provider("clipboard", "set", args);
|
||||||
}
|
}
|
||||||
|
@@ -527,7 +527,7 @@ static struct vimoption
|
|||||||
(char_u *)0L}
|
(char_u *)0L}
|
||||||
SCRIPTID_INIT},
|
SCRIPTID_INIT},
|
||||||
{"clipboard", "cb", P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
|
{"clipboard", "cb", P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
|
||||||
(char_u *)NULL, PV_NONE,
|
(char_u *)&p_cb, PV_NONE,
|
||||||
{(char_u *)"", (char_u *)0L}
|
{(char_u *)"", (char_u *)0L}
|
||||||
SCRIPTID_INIT},
|
SCRIPTID_INIT},
|
||||||
{"cmdheight", "ch", P_NUM|P_VI_DEF|P_RALL,
|
{"cmdheight", "ch", P_NUM|P_VI_DEF|P_RALL,
|
||||||
@@ -1620,9 +1620,6 @@ static struct vimoption
|
|||||||
{"undoreload", "ur", P_NUM|P_VI_DEF,
|
{"undoreload", "ur", P_NUM|P_VI_DEF,
|
||||||
(char_u *)&p_ur, PV_NONE,
|
(char_u *)&p_ur, PV_NONE,
|
||||||
{ (char_u *)10000L, (char_u *)0L} SCRIPTID_INIT},
|
{ (char_u *)10000L, (char_u *)0L} SCRIPTID_INIT},
|
||||||
{"unnamedclip", "ucp", P_BOOL|P_VI_DEF|P_VIM,
|
|
||||||
(char_u *)&p_unc, PV_NONE,
|
|
||||||
{(char_u *)FALSE, (char_u *)FALSE} SCRIPTID_INIT},
|
|
||||||
{"updatecount", "uc", P_NUM|P_VI_DEF,
|
{"updatecount", "uc", P_NUM|P_VI_DEF,
|
||||||
(char_u *)&p_uc, PV_NONE,
|
(char_u *)&p_uc, PV_NONE,
|
||||||
{(char_u *)200L, (char_u *)0L} SCRIPTID_INIT},
|
{(char_u *)200L, (char_u *)0L} SCRIPTID_INIT},
|
||||||
@@ -4279,6 +4276,10 @@ did_set_string_option (
|
|||||||
if (check_opt_strings(p_ead, p_ead_values, FALSE) != OK)
|
if (check_opt_strings(p_ead, p_ead_values, FALSE) != OK)
|
||||||
errmsg = e_invarg;
|
errmsg = e_invarg;
|
||||||
}
|
}
|
||||||
|
else if (varp == &p_cb) {
|
||||||
|
if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, TRUE) != OK)
|
||||||
|
errmsg = e_invarg;
|
||||||
|
}
|
||||||
/* When 'spelllang' or 'spellfile' is set and there is a window for this
|
/* When 'spelllang' or 'spellfile' is set and there is a window for this
|
||||||
* buffer in which 'spell' is set load the wordlists. */
|
* buffer in which 'spell' is set load the wordlists. */
|
||||||
else if (varp == &(curbuf->b_s.b_p_spl) || varp == &(curbuf->b_s.b_p_spf)) {
|
else if (varp == &(curbuf->b_s.b_p_spl) || varp == &(curbuf->b_s.b_p_spf)) {
|
||||||
@@ -4846,7 +4847,6 @@ char_u *check_stl_option(char_u *s)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
|
* Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
|
||||||
* Return error message when failed, NULL when OK.
|
* Return error message when failed, NULL when OK.
|
||||||
|
@@ -317,6 +317,13 @@ EXTERN char_u *p_enc; /* 'encoding' */
|
|||||||
EXTERN int p_deco; /* 'delcombine' */
|
EXTERN int p_deco; /* 'delcombine' */
|
||||||
EXTERN char_u *p_ccv; /* 'charconvert' */
|
EXTERN char_u *p_ccv; /* 'charconvert' */
|
||||||
EXTERN char_u *p_cedit; /* 'cedit' */
|
EXTERN char_u *p_cedit; /* 'cedit' */
|
||||||
|
EXTERN char_u *p_cb; /* 'clipboard' */
|
||||||
|
EXTERN unsigned cb_flags;
|
||||||
|
#ifdef IN_OPTION_C
|
||||||
|
static char *(p_cb_values[]) = {"unnamed", "unnamedplus", NULL};
|
||||||
|
#endif
|
||||||
|
# define CB_UNNAMED 0x001
|
||||||
|
# define CB_UNNAMEDPLUS 0x002
|
||||||
EXTERN long p_cwh; /* 'cmdwinheight' */
|
EXTERN long p_cwh; /* 'cmdwinheight' */
|
||||||
EXTERN long p_ch; /* 'cmdheight' */
|
EXTERN long p_ch; /* 'cmdheight' */
|
||||||
EXTERN int p_confirm; /* 'confirm' */
|
EXTERN int p_confirm; /* 'confirm' */
|
||||||
@@ -582,7 +589,6 @@ static char *(p_ttym_values[]) =
|
|||||||
EXTERN char_u *p_udir; /* 'undodir' */
|
EXTERN char_u *p_udir; /* 'undodir' */
|
||||||
EXTERN long p_ul; /* 'undolevels' */
|
EXTERN long p_ul; /* 'undolevels' */
|
||||||
EXTERN long p_ur; /* 'undoreload' */
|
EXTERN long p_ur; /* 'undoreload' */
|
||||||
EXTERN int p_unc; /* 'unnamedclip' */
|
|
||||||
EXTERN long p_uc; /* 'updatecount' */
|
EXTERN long p_uc; /* 'updatecount' */
|
||||||
EXTERN long p_ut; /* 'updatetime' */
|
EXTERN long p_ut; /* 'updatetime' */
|
||||||
EXTERN char_u *p_fcs; /* 'fillchar' */
|
EXTERN char_u *p_fcs; /* 'fillchar' */
|
||||||
|
16
test/functional/clipboard/autoload/provider/clipboard.vim
Normal file
16
test/functional/clipboard/autoload/provider/clipboard.vim
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
let g:test_clip = { '+': [''], '*': [''], }
|
||||||
|
|
||||||
|
let s:methods = {}
|
||||||
|
|
||||||
|
function! s:methods.get(reg)
|
||||||
|
return g:test_clip[a:reg]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:methods.set(lines, regtype, reg)
|
||||||
|
let g:test_clip[a:reg] = a:lines
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! provider#clipboard#Call(method, args)
|
||||||
|
return call(s:methods[a:method],a:args,s:methods)
|
||||||
|
endfunction
|
141
test/functional/clipboard/clipboard_provider_spec.lua
Normal file
141
test/functional/clipboard/clipboard_provider_spec.lua
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
-- Test clipboard provider support
|
||||||
|
|
||||||
|
local helpers = require('test.functional.helpers')
|
||||||
|
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
|
||||||
|
local execute, expect, eq, eval = helpers.execute, helpers.expect, helpers.eq, helpers.eval
|
||||||
|
local nvim, run, stop, restart = helpers.nvim, helpers.run, helpers.stop, helpers.restart
|
||||||
|
|
||||||
|
local function reset()
|
||||||
|
clear()
|
||||||
|
execute('let &rtp = "test/functional/clipboard,".&rtp')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function basic_register_test()
|
||||||
|
insert("some words")
|
||||||
|
|
||||||
|
feed('^dwP')
|
||||||
|
expect('some words')
|
||||||
|
|
||||||
|
feed('veyP')
|
||||||
|
expect('some words words')
|
||||||
|
|
||||||
|
feed('^dwywe"-p')
|
||||||
|
expect('wordssome words')
|
||||||
|
|
||||||
|
feed('p')
|
||||||
|
expect('wordssome words words')
|
||||||
|
|
||||||
|
feed('yyp')
|
||||||
|
expect([[
|
||||||
|
wordssome words words
|
||||||
|
wordssome words words]])
|
||||||
|
feed('d-')
|
||||||
|
|
||||||
|
insert([[
|
||||||
|
some text, and some more
|
||||||
|
random text stuff]])
|
||||||
|
feed('ggtav+2ed$p')
|
||||||
|
expect([[
|
||||||
|
some text, stuff and some more
|
||||||
|
random text]])
|
||||||
|
reset()
|
||||||
|
end
|
||||||
|
|
||||||
|
describe('clipboard usage', function()
|
||||||
|
setup(reset)
|
||||||
|
it("works", function()
|
||||||
|
basic_register_test()
|
||||||
|
|
||||||
|
-- "* and unnamed should function as independent registers
|
||||||
|
insert("some words")
|
||||||
|
feed('^"*dwdw"*P')
|
||||||
|
expect('some ')
|
||||||
|
eq({'some '}, eval("g:test_clip['*']"))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- "* and "+ should be independent when the provider supports it
|
||||||
|
insert([[
|
||||||
|
text:
|
||||||
|
first line
|
||||||
|
secound line
|
||||||
|
third line]])
|
||||||
|
|
||||||
|
feed('G"+dd"*dddd"+p"*pp')
|
||||||
|
expect([[
|
||||||
|
text:
|
||||||
|
third line
|
||||||
|
secound line
|
||||||
|
first line]])
|
||||||
|
-- linewise selection should be encoded as an extra newline
|
||||||
|
eq({'third line', ''}, eval("g:test_clip['+']"))
|
||||||
|
eq({'secound line', ''}, eval("g:test_clip['*']"))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- handle null bytes
|
||||||
|
insert("some\x16000text\n\x16000very binary\x16000")
|
||||||
|
feed('"*y-+"*p')
|
||||||
|
eq({'some\ntext', '\nvery binary\n',''}, eval("g:test_clip['*']"))
|
||||||
|
expect("some\x00text\n\x00very binary\x00\nsome\x00text\n\x00very binary\x00")
|
||||||
|
|
||||||
|
-- test getreg/getregtype
|
||||||
|
eq('some\ntext\n\nvery binary\n\n', eval("getreg('*', 1)"))
|
||||||
|
eq("V", eval("getregtype('*')"))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- blockwise paste
|
||||||
|
insert([[
|
||||||
|
much
|
||||||
|
text]])
|
||||||
|
feed('"*yy') -- force load of provider
|
||||||
|
execute("let g:test_clip['*'] = [['very','block'],'b']")
|
||||||
|
feed('gg"*P')
|
||||||
|
expect([[
|
||||||
|
very much
|
||||||
|
blocktext]])
|
||||||
|
eq("\x165", eval("getregtype('*')"))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- test setreg
|
||||||
|
execute('call setreg("*", "setted\\ntext", "c")')
|
||||||
|
execute('call setreg("+", "explicitly\\nlines", "l")')
|
||||||
|
feed('"+P"*p')
|
||||||
|
expect([[
|
||||||
|
esetted
|
||||||
|
textxplicitly
|
||||||
|
lines
|
||||||
|
]])
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- test let @+ (issue #1427)
|
||||||
|
execute("let @+ = 'some'")
|
||||||
|
execute("let @* = ' other stuff'")
|
||||||
|
eq({'some'}, eval("g:test_clip['+']"))
|
||||||
|
eq({' other stuff'}, eval("g:test_clip['*']"))
|
||||||
|
feed('"+p"*p')
|
||||||
|
expect('some other stuff')
|
||||||
|
execute("let @+ .= ' more'")
|
||||||
|
feed('dd"+p')
|
||||||
|
expect('some more')
|
||||||
|
reset()
|
||||||
|
|
||||||
|
-- the basic behavior of unnamed register should be the same
|
||||||
|
-- even when handled by clipboard provider
|
||||||
|
execute('set clipboard=unnamed')
|
||||||
|
basic_register_test()
|
||||||
|
|
||||||
|
-- with cb=unnamed, "* and unnamed will be the same register
|
||||||
|
execute('set clipboard=unnamed')
|
||||||
|
insert("some words")
|
||||||
|
feed('^"*dwdw"*P')
|
||||||
|
expect('words')
|
||||||
|
eq({'words'}, eval("g:test_clip['*']"))
|
||||||
|
|
||||||
|
execute("let g:test_clip['*'] = ['linewise stuff','']")
|
||||||
|
feed('p')
|
||||||
|
expect([[
|
||||||
|
words
|
||||||
|
linewise stuff]])
|
||||||
|
reset()
|
||||||
|
|
||||||
|
end)
|
||||||
|
end)
|
Reference in New Issue
Block a user