aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ops.c
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2015-04-25 18:47:31 +0300
committerZyX <kp-pav@yandex.ru>2015-10-08 21:59:51 +0300
commit244dbe3a77bf548f73d8781da7327f30e818b08a (patch)
tree8777a25447be219fe351106cfef37670e0278ddd /src/nvim/ops.c
parent0fdaab995ed95250b13058a717d5bc562e1834c8 (diff)
downloadrneovim-244dbe3a77bf548f73d8781da7327f30e818b08a.tar.gz
rneovim-244dbe3a77bf548f73d8781da7327f30e818b08a.tar.bz2
rneovim-244dbe3a77bf548f73d8781da7327f30e818b08a.zip
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).
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r--src/nvim/ops.c315
1 files changed, 130 insertions, 185 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 3163132b8a..55c7aa3364 100644
--- a/src/nvim/ops.c
+++ b/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;
+}