Skip to content

Commit

Permalink
provider: Add support for clipboard registers.
Browse files Browse the repository at this point in the history
This reimplements the '+'/'*' clipboard registers(both are aliases to the same
register, no dedicated storage for the X11 selection) on top of the provider
infrastructure.

This adds two new 'unnamedclip' option, has the same effect of setting
'clipboard' to 'unnamed/unnamedplus' in vim

The 'clipboard' option was not reused because all values(except 'unnamedplus')
seem to be useless for Neovim, and the code to parse the option was relatively
big. The option remains for vim compatibility but it's silently ignored.
  • Loading branch information
tarruda committed Jul 17, 2014
1 parent 486c8e3 commit fba1d3b
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 5 deletions.
5 changes: 4 additions & 1 deletion src/nvim/normal.c
Expand Up @@ -923,6 +923,7 @@ normal_cmd (


/* Adjust the register according to 'clipboard', so that when /* Adjust the register according to 'clipboard', so that when
* "unnamed" is present it becomes '*' or '+' instead of '"'. */ * "unnamed" is present it becomes '*' or '+' instead of '"'. */
adjust_clipboard_register(&regname);
set_reg_var(regname); set_reg_var(regname);
} }
} }
Expand Down Expand Up @@ -5101,6 +5102,7 @@ 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(&regname);
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) {
Expand Down Expand Up @@ -7267,9 +7269,10 @@ 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(&regname);
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. */
Expand Down
125 changes: 122 additions & 3 deletions src/nvim/ops.c
Expand Up @@ -47,6 +47,9 @@
#include "nvim/ui.h" #include "nvim/ui.h"
#include "nvim/undo.h" #include "nvim/undo.h"
#include "nvim/window.h" #include "nvim/window.h"
#include "nvim/os/provider.h"
#include "nvim/os/msgpack_rpc_helpers.h"
#include "nvim/api/private/helpers.h"


/* /*
* Registers: * Registers:
Expand All @@ -55,8 +58,9 @@
* 10..35 = registers 'a' to 'z' * 10..35 = registers 'a' to 'z'
* 36 = delete register '-' * 36 = delete register '-'
*/ */
#define NUM_REGISTERS 37 #define NUM_REGISTERS 38
#define DELETION_REGISTER 36 #define DELETION_REGISTER 36
#define CLIP_REGISTER 37


/* /*
* Each yank register is an array of pointers to lines. * Each yank register is an array of pointers to lines.
Expand Down Expand Up @@ -711,6 +715,8 @@ valid_yank_reg (
|| regname == '"' || regname == '"'
|| regname == '-' || regname == '-'
|| regname == '_' || regname == '_'
|| regname == '*'
|| regname == '+'
) )
return TRUE; return TRUE;
return FALSE; return FALSE;
Expand Down Expand Up @@ -743,6 +749,8 @@ void get_yank_register(int regname, int writing)
y_append = TRUE; y_append = TRUE;
} else if (regname == '-') } else if (regname == '-')
i = DELETION_REGISTER; i = DELETION_REGISTER;
else if (regname == '*' || regname == '+')
i = CLIP_REGISTER;
else /* not 0-9, a-z, A-Z or '-': use register 0 */ else /* not 0-9, a-z, A-Z or '-': use register 0 */
i = 0; i = 0;
y_current = &(y_regs[i]); y_current = &(y_regs[i]);
Expand All @@ -762,6 +770,7 @@ get_register (
) FUNC_ATTR_NONNULL_RET ) FUNC_ATTR_NONNULL_RET
{ {
get_yank_register(name, 0); get_yank_register(name, 0);
get_clipboard(name);


struct yankreg *reg = xmalloc(sizeof(struct yankreg)); struct yankreg *reg = xmalloc(sizeof(struct yankreg));
*reg = *y_current; *reg = *y_current;
Expand Down Expand Up @@ -789,7 +798,7 @@ void put_register(int name, void *reg)
free_yank_all(); free_yank_all();
*y_current = *(struct yankreg *)reg; *y_current = *(struct yankreg *)reg;
free(reg); free(reg);

set_clipboard(name);
} }


/* /*
Expand Down Expand Up @@ -929,6 +938,7 @@ do_execreg (
} }
execreg_lastc = regname; execreg_lastc = regname;


get_clipboard(regname);


if (regname == '_') /* black hole: don't stuff anything */ if (regname == '_') /* black hole: don't stuff anything */
return OK; return OK;
Expand Down Expand Up @@ -1093,6 +1103,7 @@ insert_reg (
if (regname != NUL && !valid_yank_reg(regname, FALSE)) if (regname != NUL && !valid_yank_reg(regname, FALSE))
return FAIL; return FAIL;


get_clipboard(regname);


if (regname == '.') /* insert last inserted text */ if (regname == '.') /* insert last inserted text */
retval = stuff_inserted(NUL, 1L, TRUE); retval = stuff_inserted(NUL, 1L, TRUE);
Expand Down Expand Up @@ -1278,6 +1289,17 @@ 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 && provider_has_feature("clipboard")) {
*rp = '+';
return true;
}

return false;
}


/* /*
* Handle a delete operation. * Handle a delete operation.
Expand Down Expand Up @@ -1307,6 +1329,7 @@ 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);
Expand Down Expand Up @@ -1389,6 +1412,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 ((
adjusted ||
oap->regname == 0) && oap->motion_type != MLINE oap->regname == 0) && oap->motion_type != MLINE
&& oap->line_count == 1) { && oap->line_count == 1) {
oap->regname = '-'; oap->regname = '-';
Expand Down Expand Up @@ -2336,7 +2360,6 @@ int op_yank(oparg_T *oap, int deleting, int mess)
if (oap->regname == '_') /* black hole: nothing to do */ if (oap->regname == '_') /* black hole: nothing to do */
return OK; return OK;



if (!deleting) /* op_delete() already set y_current */ if (!deleting) /* op_delete() already set y_current */
get_yank_register(oap->regname, TRUE); get_yank_register(oap->regname, TRUE);


Expand Down Expand Up @@ -2519,6 +2542,8 @@ int op_yank(oparg_T *oap, int deleting, int mess)
curbuf->b_op_end.col = MAXCOL; curbuf->b_op_end.col = MAXCOL;
} }


set_clipboard(oap->regname);

return OK; return OK;
} }


Expand Down Expand Up @@ -2581,6 +2606,8 @@ do_put (
int allocated = FALSE; int allocated = FALSE;
long cnt; long cnt;


adjust_clipboard_register(&regname);
get_clipboard(regname);


if (flags & PUT_FIXINDENT) if (flags & PUT_FIXINDENT)
orig_indent = get_indent(); orig_indent = get_indent();
Expand Down Expand Up @@ -3171,6 +3198,8 @@ 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);


if (i == -1) { if (i == -1) {
if (y_previous != NULL) if (y_previous != NULL)
Expand Down Expand Up @@ -4528,6 +4557,9 @@ void write_viminfo_registers(FILE *fp)
for (i = 0; i < NUM_REGISTERS; i++) { for (i = 0; i < NUM_REGISTERS; i++) {
if (y_regs[i].y_array == NULL) if (y_regs[i].y_array == NULL)
continue; continue;
// Skip '*'/'+' register, we don't want them back next time
if (i == CLIP_REGISTER)
continue;
/* Skip empty registers. */ /* Skip empty registers. */
num_lines = y_regs[i].y_size; num_lines = y_regs[i].y_size;
if (num_lines == 0 if (num_lines == 0
Expand Down Expand Up @@ -4607,6 +4639,7 @@ char_u get_reg_type(int regname, long *reglen)
return MCHAR; return MCHAR;
} }


get_clipboard(regname);


if (regname != NUL && !valid_yank_reg(regname, FALSE)) if (regname != NUL && !valid_yank_reg(regname, FALSE))
return MAUTO; return MAUTO;
Expand Down Expand Up @@ -4654,6 +4687,7 @@ get_reg_contents (
if (regname != NUL && !valid_yank_reg(regname, FALSE)) if (regname != NUL && !valid_yank_reg(regname, FALSE))
return NULL; return NULL;


get_clipboard(regname);


if (get_spec_reg(regname, &retval, &allocated, FALSE)) { if (get_spec_reg(regname, &retval, &allocated, FALSE)) {
if (retval == NULL) if (retval == NULL)
Expand Down Expand Up @@ -5162,3 +5196,88 @@ void cursor_pos_info(void)
} }
} }


static void free_register(struct yankreg *reg)
{
// Save 'y_current' into 'curr'
struct yankreg *curr = y_current;
// Set it to 'y_current' since 'free_yank_all' operates on it
y_current = reg;
free_yank_all();
// Restore 'y_current'
y_current = curr;
}

static void copy_register(struct yankreg *dest, struct yankreg *src)
{
free_register(dest);
*dest = *src;
dest->y_array = xcalloc(src->y_size, sizeof(uint8_t *));
for (int j = 0; j < src->y_size; ++j) {
dest->y_array[j] = (uint8_t *)xstrdup((char *)src->y_array[j]);
}
}

static void get_clipboard(int name)
{
if (!(name == '*' || name == '+'
|| (p_unc && !name && provider_has_feature("clipboard")))) {
return;
}

struct yankreg *reg = &y_regs[CLIP_REGISTER];
free_register(reg);
Object result = provider_call("clipboard_get", NIL);

if (result.type != kObjectTypeArray) {
goto err;
}

Array lines = result.data.array;
reg->y_array = xcalloc(lines.size, sizeof(uint8_t *));
reg->y_size = lines.size;

for (size_t i = 0; i < lines.size; i++) {
if (lines.items[i].type != kObjectTypeString) {
goto err;
}
reg->y_array[i] = (uint8_t *)lines.items[i].data.string.data;
}

if (!name && p_unc) {
// copy to the unnamed register
copy_register(&y_regs[0], reg);
}

return;

err:
msgpack_rpc_free_object(result);
free(reg->y_array);
reg->y_array = NULL;
reg->y_size = 0;
EMSG("Clipboard provider returned invalid data");
}

static void set_clipboard(int name)
{
if (!(name == '*' || name == '+'
|| (p_unc && !name && provider_has_feature("clipboard")))) {
return;
}

struct yankreg *reg = &y_regs[CLIP_REGISTER];

if (!name && p_unc) {
// copy from the unnamed register
copy_register(reg, &y_regs[0]);
}

Array lines = {0, 0, 0};

for (int i = 0; i < reg->y_size; i++) {
ADD(lines, STRING_OBJ(cstr_to_string((char *)reg->y_array[i])));
}

Object result = provider_call("clipboard_set", ARRAY_OBJ(lines));
msgpack_rpc_free_object(result);
}
6 changes: 6 additions & 0 deletions src/nvim/option.c
Expand Up @@ -963,6 +963,9 @@ static struct vimoption
{"infercase", "inf", P_BOOL|P_VI_DEF, {"infercase", "inf", P_BOOL|P_VI_DEF,
(char_u *)&p_inf, PV_INF, (char_u *)&p_inf, PV_INF,
{(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
{"initclipboard","icpb",P_STRING|P_VI_DEF|P_SECURE,
(char_u *)&p_icpb, PV_NONE,
{(char_u *)NULL, (char_u *)0L} SCRIPTID_INIT},
{"initpython","ipy",P_STRING|P_VI_DEF|P_SECURE, {"initpython","ipy",P_STRING|P_VI_DEF|P_SECURE,
(char_u *)&p_ipy, PV_NONE, (char_u *)&p_ipy, PV_NONE,
{(char_u *)NULL, (char_u *)0L} SCRIPTID_INIT}, {(char_u *)NULL, (char_u *)0L} SCRIPTID_INIT},
Expand Down Expand Up @@ -1628,6 +1631,9 @@ 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},
Expand Down
2 changes: 2 additions & 0 deletions src/nvim/option_defs.h
Expand Up @@ -586,6 +586,7 @@ 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' */
Expand Down Expand Up @@ -631,6 +632,7 @@ EXTERN int p_wa; /* 'writeany' */
EXTERN int p_wb; /* 'writebackup' */ EXTERN int p_wb; /* 'writebackup' */
EXTERN long p_wd; /* 'writedelay' */ EXTERN long p_wd; /* 'writedelay' */
EXTERN char *p_ipy; // 'initpython' EXTERN char *p_ipy; // 'initpython'
EXTERN char *p_icpb; // 'initclipboard'


/* /*
* "indir" values for buffer-local opions. * "indir" values for buffer-local opions.
Expand Down
7 changes: 6 additions & 1 deletion src/nvim/os/provider.c
Expand Up @@ -36,7 +36,12 @@ static struct feature {
"python_execute", "python_execute",
"python_execute_file", "python_execute_file",
"python_do_range", "python_do_range",
"python_eval") "python_eval"),

FEATURE("clipboard",
&p_icpb,
"clipboard_get",
"clipboard_set")
}; };


static Map(cstr_t, uint64_t) *registered_providers = NULL; static Map(cstr_t, uint64_t) *registered_providers = NULL;
Expand Down

0 comments on commit fba1d3b

Please sign in to comment.