mirror of
https://github.com/neovim/neovim.git
synced 2025-09-15 15:58:17 +00:00
viminfo: First version of ShaDa file dumping
What works: 1. ShaDa file dumping: header, registers, jump list, history, search patterns, substitute strings, variables. 2. ShaDa file reading: registers, global marks, variables. Most was not tested. TODO: 1. Merging. 2. Reading history, local marks, jump and buffer lists. 3. Documentation update. 4. Converting some data from &encoding. 5. Safer variant of dumping viminfo (dump to temporary file then rename). 6. Removing old viminfo code (currently masked with `#if 0` in a ShaDa file for reference).
This commit is contained in:
315
src/nvim/ops.c
315
src/nvim/ops.c
@@ -50,6 +50,9 @@
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
|
||||
/*
|
||||
* Registers:
|
||||
@@ -62,14 +65,14 @@
|
||||
*/
|
||||
#define DELETION_REGISTER 36
|
||||
#define NUM_SAVED_REGISTERS 37
|
||||
// The following registers should not be saved in viminfo:
|
||||
// The following registers should not be saved in ShaDa file:
|
||||
#define STAR_REGISTER 37
|
||||
#define PLUS_REGISTER 38
|
||||
#define NUM_REGISTERS 39
|
||||
|
||||
static yankreg_T y_regs[NUM_REGISTERS];
|
||||
|
||||
static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
|
||||
static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
|
||||
|
||||
static bool clipboard_didwarn_unnamed = false;
|
||||
|
||||
@@ -746,6 +749,31 @@ typedef enum {
|
||||
YREG_PUT,
|
||||
} yreg_mode_t;
|
||||
|
||||
/// Convert register name into register index
|
||||
///
|
||||
/// @param[in] regname Register name.
|
||||
///
|
||||
/// @return Index in y_regs array or -1 if register name was not recognized.
|
||||
static inline int reg_index(const int regname)
|
||||
FUNC_ATTR_CONST
|
||||
{
|
||||
if (ascii_isdigit(regname)) {
|
||||
return regname - '0';
|
||||
} else if (ASCII_ISLOWER(regname)) {
|
||||
return CharOrdLow(regname) + 10;
|
||||
} else if (ASCII_ISUPPER(regname)) {
|
||||
return CharOrdUp(regname) + 10;
|
||||
} else if (regname == '-') {
|
||||
return DELETION_REGISTER;
|
||||
} else if (regname == '*') {
|
||||
return STAR_REGISTER;
|
||||
} else if (regname == '+') {
|
||||
return PLUS_REGISTER;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return yankreg_T to use, according to the value of `regname`.
|
||||
/// Cannot handle the '_' (black hole) register.
|
||||
/// Must only be called with a valid register name!
|
||||
@@ -778,19 +806,11 @@ yankreg_T *get_yank_register(int regname, int mode)
|
||||
return y_previous;
|
||||
}
|
||||
|
||||
int i = 0; // when not 0-9, a-z, A-Z or '-'/'+'/'*': use register 0
|
||||
if (ascii_isdigit(regname))
|
||||
i = regname - '0';
|
||||
else if (ASCII_ISLOWER(regname))
|
||||
i = CharOrdLow(regname) + 10;
|
||||
else if (ASCII_ISUPPER(regname)) {
|
||||
i = CharOrdUp(regname) + 10;
|
||||
} else if (regname == '-')
|
||||
i = DELETION_REGISTER;
|
||||
else if (regname == '*')
|
||||
i = STAR_REGISTER;
|
||||
else if (regname == '+')
|
||||
i = PLUS_REGISTER;
|
||||
int i = reg_index(regname);
|
||||
// when not 0-9, a-z, A-Z or '-'/'+'/'*': use register 0
|
||||
if (i == -1) {
|
||||
i = 0;
|
||||
}
|
||||
reg = &y_regs[i];
|
||||
|
||||
if (mode == YREG_YANK) {
|
||||
@@ -890,6 +910,20 @@ int do_record(int c)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void set_yreg_additional_data(yankreg_T *reg,
|
||||
Dictionary *additional_data)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
if (reg->additional_data == additional_data) {
|
||||
return;
|
||||
}
|
||||
if (reg->additional_data != NULL) {
|
||||
api_free_dictionary(*reg->additional_data);
|
||||
free(reg->additional_data);
|
||||
}
|
||||
reg->additional_data = additional_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff string "p" into yank register "regname" as a single line (append if
|
||||
* uppercase). "p" must have been alloced.
|
||||
@@ -919,11 +953,13 @@ static int stuff_yank(int regname, char_u *p)
|
||||
*pp = lp;
|
||||
} else {
|
||||
free_register(reg);
|
||||
set_yreg_additional_data(reg, NULL);
|
||||
reg->y_array = (char_u **)xmalloc(sizeof(char_u *));
|
||||
reg->y_array[0] = p;
|
||||
reg->y_size = 1;
|
||||
reg->y_type = MCHAR; /* used to be MLINE, why? */
|
||||
}
|
||||
reg->timestamp = os_time();
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -2266,10 +2302,7 @@ int op_change(oparg_T *oap)
|
||||
*/
|
||||
void init_yank(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_REGISTERS; i++)
|
||||
y_regs[i].y_array = NULL;
|
||||
memset(&(y_regs[0]), 0, sizeof(y_regs));
|
||||
}
|
||||
|
||||
#if defined(EXITFREE)
|
||||
@@ -2291,6 +2324,7 @@ void clear_registers(void)
|
||||
void free_register(yankreg_T *reg)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
set_yreg_additional_data(reg, NULL);
|
||||
if (reg->y_array != NULL) {
|
||||
long i;
|
||||
|
||||
@@ -2369,6 +2403,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
|
||||
reg->y_type = yanktype; /* set the yank register type */
|
||||
reg->y_width = 0;
|
||||
reg->y_array = xcalloc(yanklines, sizeof(char_u *));
|
||||
set_yreg_additional_data(reg, NULL);
|
||||
reg->timestamp = os_time();
|
||||
|
||||
y_idx = 0;
|
||||
lnum = oap->start.lnum;
|
||||
@@ -4433,171 +4469,6 @@ int do_addsub(int command, linenr_T Prenum1)
|
||||
return OK;
|
||||
}
|
||||
|
||||
int read_viminfo_register(vir_T *virp, int force)
|
||||
{
|
||||
int eof;
|
||||
int do_it = TRUE;
|
||||
int size;
|
||||
int limit;
|
||||
int set_prev = FALSE;
|
||||
char_u *str;
|
||||
char_u **array = NULL;
|
||||
|
||||
/* We only get here (hopefully) if line[0] == '"' */
|
||||
str = virp->vir_line + 1;
|
||||
|
||||
/* If the line starts with "" this is the y_previous register. */
|
||||
if (*str == '"') {
|
||||
set_prev = TRUE;
|
||||
str++;
|
||||
}
|
||||
|
||||
if (!ASCII_ISALNUM(*str) && *str != '-') {
|
||||
if (viminfo_error("E577: ", _("Illegal register name"), virp->vir_line))
|
||||
return TRUE; /* too many errors, pretend end-of-file */
|
||||
do_it = FALSE;
|
||||
}
|
||||
yankreg_T *reg = get_yank_register(*str++, YREG_PUT);
|
||||
if (!force && reg->y_array != NULL)
|
||||
do_it = FALSE;
|
||||
|
||||
if (*str == '@') {
|
||||
/* "x@: register x used for @@ */
|
||||
if (force || execreg_lastc == NUL)
|
||||
execreg_lastc = str[-1];
|
||||
}
|
||||
|
||||
size = 0;
|
||||
limit = 100; /* Optimized for registers containing <= 100 lines */
|
||||
if (do_it) {
|
||||
if (set_prev) {
|
||||
y_previous = reg;
|
||||
}
|
||||
|
||||
free_register(reg);
|
||||
array = xmalloc(limit * sizeof(char_u *));
|
||||
|
||||
str = skipwhite(skiptowhite(str));
|
||||
if (STRNCMP(str, "CHAR", 4) == 0) {
|
||||
reg->y_type = MCHAR;
|
||||
} else if (STRNCMP(str, "BLOCK", 5) == 0) {
|
||||
reg->y_type = MBLOCK;
|
||||
} else {
|
||||
reg->y_type = MLINE;
|
||||
}
|
||||
/* get the block width; if it's missing we get a zero, which is OK */
|
||||
str = skipwhite(skiptowhite(str));
|
||||
reg->y_width = getdigits_int(&str);
|
||||
}
|
||||
|
||||
while (!(eof = viminfo_readline(virp))
|
||||
&& (virp->vir_line[0] == TAB || virp->vir_line[0] == '<')) {
|
||||
if (do_it) {
|
||||
if (size >= limit) {
|
||||
limit *= 2;
|
||||
array = xrealloc(array, limit * sizeof(char_u *));
|
||||
}
|
||||
array[size++] = viminfo_readstring(virp, 1, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_it) {
|
||||
if (size == 0) {
|
||||
xfree(array);
|
||||
} else if (size < limit) {
|
||||
reg->y_array = xrealloc(array, size * sizeof(char_u *));
|
||||
} else {
|
||||
reg->y_array = array;
|
||||
}
|
||||
reg->y_size = size;
|
||||
}
|
||||
return eof;
|
||||
}
|
||||
|
||||
void write_viminfo_registers(FILE *fp)
|
||||
{
|
||||
int i, j;
|
||||
char_u *type;
|
||||
char_u c;
|
||||
int num_lines;
|
||||
int max_num_lines;
|
||||
int max_kbyte;
|
||||
long len;
|
||||
|
||||
fputs(_("\n# Registers:\n"), fp);
|
||||
|
||||
/* Get '<' value, use old '"' value if '<' is not found. */
|
||||
max_num_lines = get_viminfo_parameter('<');
|
||||
if (max_num_lines < 0)
|
||||
max_num_lines = get_viminfo_parameter('"');
|
||||
if (max_num_lines == 0)
|
||||
return;
|
||||
max_kbyte = get_viminfo_parameter('s');
|
||||
if (max_kbyte == 0)
|
||||
return;
|
||||
|
||||
// don't include clipboard registers '*'/'+'
|
||||
for (i = 0; i < NUM_SAVED_REGISTERS; i++) {
|
||||
if (y_regs[i].y_array == NULL)
|
||||
continue;
|
||||
|
||||
/* Skip empty registers. */
|
||||
num_lines = y_regs[i].y_size;
|
||||
if (num_lines == 0
|
||||
|| (num_lines == 1 && y_regs[i].y_type == MCHAR
|
||||
&& *y_regs[i].y_array[0] == NUL))
|
||||
continue;
|
||||
|
||||
if (max_kbyte > 0) {
|
||||
/* Skip register if there is more text than the maximum size. */
|
||||
len = 0;
|
||||
for (j = 0; j < num_lines; j++)
|
||||
len += (long)STRLEN(y_regs[i].y_array[j]) + 1L;
|
||||
if (len > (long)max_kbyte * 1024L)
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (y_regs[i].y_type) {
|
||||
case MLINE:
|
||||
type = (char_u *)"LINE";
|
||||
break;
|
||||
case MCHAR:
|
||||
type = (char_u *)"CHAR";
|
||||
break;
|
||||
case MBLOCK:
|
||||
type = (char_u *)"BLOCK";
|
||||
break;
|
||||
default:
|
||||
sprintf((char *)IObuff, _("E574: Unknown register type %d"),
|
||||
y_regs[i].y_type);
|
||||
emsg(IObuff);
|
||||
type = (char_u *)"LINE";
|
||||
break;
|
||||
}
|
||||
if (y_previous == &y_regs[i])
|
||||
fprintf(fp, "\"");
|
||||
c = get_register_name(i);
|
||||
fprintf(fp, "\"%c", c);
|
||||
if (c == execreg_lastc)
|
||||
fprintf(fp, "@");
|
||||
fprintf(fp, "\t%s\t%d\n", type,
|
||||
(int)y_regs[i].y_width
|
||||
);
|
||||
|
||||
/* If max_num_lines < 0, then we save ALL the lines in the register */
|
||||
if (max_num_lines > 0 && num_lines > max_num_lines)
|
||||
num_lines = max_num_lines;
|
||||
for (j = 0; j < num_lines; j++) {
|
||||
putc('\t', fp);
|
||||
viminfo_writestring(fp, y_regs[i].y_array[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Return the type of a register.
|
||||
* Used for getregtype()
|
||||
@@ -4739,7 +4610,6 @@ void *get_reg_contents(int regname, int flags)
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must_append)
|
||||
{
|
||||
if (!valid_yank_reg(name, true)) { // check for valid reg name
|
||||
@@ -4973,6 +4843,8 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str,
|
||||
}
|
||||
y_ptr->y_type = type;
|
||||
y_ptr->y_size = lnum;
|
||||
set_yreg_additional_data(y_ptr, NULL);
|
||||
y_ptr->timestamp = os_time();
|
||||
if (type == MBLOCK) {
|
||||
y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen);
|
||||
} else {
|
||||
@@ -5363,6 +5235,10 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
|
||||
|
||||
reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *));
|
||||
reg->y_size = lines->lv_len;
|
||||
reg->additional_data = NULL;
|
||||
reg->timestamp = 0;
|
||||
// Timestamp is not saved for clipboard registers because clipboard registers
|
||||
// are not saved in the viminfo.
|
||||
|
||||
int i = 0;
|
||||
for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) {
|
||||
@@ -5411,6 +5287,8 @@ err:
|
||||
}
|
||||
reg->y_array = NULL;
|
||||
reg->y_size = 0;
|
||||
reg->additional_data = NULL;
|
||||
reg->timestamp = 0;
|
||||
if (errmsg) {
|
||||
EMSG("clipboard: provider returned invalid data");
|
||||
}
|
||||
@@ -5478,3 +5356,70 @@ void end_global_changes(void)
|
||||
clipboard_needs_update = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether register is empty
|
||||
static inline bool reg_empty(const yankreg_T *const reg)
|
||||
FUNC_ATTR_CONST
|
||||
{
|
||||
return (reg->y_array == NULL
|
||||
|| reg->y_size == 0
|
||||
|| (reg->y_size == 1
|
||||
&& reg->y_type == MCHAR
|
||||
&& *(reg->y_array[0]) == NUL));
|
||||
}
|
||||
|
||||
/// Iterate over registerrs
|
||||
///
|
||||
/// @param[in] iter Iterator. Pass NULL to start iteration.
|
||||
/// @param[out] name Register name.
|
||||
/// @param[out] reg Register contents.
|
||||
///
|
||||
/// @return Pointer that needs to be passed to next `op_register_iter` call or
|
||||
/// NULL if iteration is over.
|
||||
const void *op_register_iter(const void *const iter, char *const name,
|
||||
yankreg_T *const reg)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
const yankreg_T *iter_reg = (iter == NULL
|
||||
? &(y_regs[0])
|
||||
: (const yankreg_T *const) iter);
|
||||
while (reg_empty(iter_reg) && iter_reg - &(y_regs[0]) < NUM_SAVED_REGISTERS) {
|
||||
iter_reg++;
|
||||
}
|
||||
if (reg_empty(iter_reg)) {
|
||||
*reg = (yankreg_T) {.y_array = NULL};
|
||||
return NULL;
|
||||
}
|
||||
size_t iter_off = iter_reg - &(y_regs[0]);
|
||||
*name = (char) get_register_name(iter_off);
|
||||
*reg = *iter_reg;
|
||||
while (++iter_reg - &(y_regs[0]) < NUM_SAVED_REGISTERS) {
|
||||
if (!reg_empty(iter_reg)) {
|
||||
return (void *) iter_reg;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Get a number of non-empty registers
|
||||
size_t op_register_amount(void)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (size_t i = 0; i < NUM_SAVED_REGISTERS; i++) {
|
||||
if (!reg_empty(y_regs + i)) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Set register to a given value
|
||||
void register_set(const char name, const yankreg_T reg)
|
||||
{
|
||||
int i = reg_index(name);
|
||||
if (i == -1) {
|
||||
return;
|
||||
}
|
||||
y_regs[i] = reg;
|
||||
}
|
||||
|
Reference in New Issue
Block a user