aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2015-07-19 21:23:15 +0300
committerZyX <kp-pav@yandex.ru>2015-10-08 22:00:13 +0300
commit43fe98c9fb350b428d05021995c8892e080054b2 (patch)
treebeb6614b0c334de02f40ce5555eac57b691a2d93
parent4bc053facda4aee6cec2c6cbc9fbbe978e66503d (diff)
downloadrneovim-43fe98c9fb350b428d05021995c8892e080054b2.tar.gz
rneovim-43fe98c9fb350b428d05021995c8892e080054b2.tar.bz2
rneovim-43fe98c9fb350b428d05021995c8892e080054b2.zip
shada: Add support for merging everything like described in the doc
-rw-r--r--src/nvim/lib/khash.h20
-rw-r--r--src/nvim/mark.c8
-rw-r--r--src/nvim/mark.h28
-rw-r--r--src/nvim/mark_defs.h6
-rw-r--r--src/nvim/ops.c45
-rw-r--r--src/nvim/ops.h43
-rw-r--r--src/nvim/shada.c1141
7 files changed, 862 insertions, 429 deletions
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index fd4f910dac..17f653f45e 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -196,6 +196,7 @@ static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
+ extern void kh_dealloc_##name(kh_##name##_t *h); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
@@ -204,16 +205,24 @@ static const double __ac_HASH_UPPER = 0.77;
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ SCOPE kh_##name##_t *kh_init_##name(void) \
+ REAL_FATTR_UNUSED; \
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
+ SCOPE void kh_dealloc_##name(kh_##name##_t *h) \
+ REAL_FATTR_UNUSED; \
+ SCOPE void kh_dealloc_##name(kh_##name##_t *h) \
+ { \
+ kfree((void *)h->keys); kfree(h->flags); \
+ kfree((void *)h->vals); \
+ } \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
REAL_FATTR_UNUSED; \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
- kfree((void *)h->keys); kfree(h->flags); \
- kfree((void *)h->vals); \
+ kh_dealloc_##name(h); \
kfree(h); \
} \
} \
@@ -447,6 +456,13 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key)
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
+ @abstract Free memory referenced directly inside a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_dealloc(name, h) kh_dealloc_##name(h)
+
+/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index b7746f192f..2072483e1d 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -1350,12 +1350,8 @@ size_t mark_buffer_amount(const buf_T *const buf)
/// later then existing one.
void mark_set_global(const char name, const xfmark_T fm, const bool update)
{
- xfmark_T *fm_tgt = NULL;
- if (ASCII_ISUPPER(name)) {
- fm_tgt = &(namedfm[name - 'A']);
- } else if (ascii_isdigit(name)) {
- fm_tgt = &(namedfm[NMARKS + (name - '0')]);
- } else {
+ xfmark_T *fm_tgt = &(namedfm[mark_global_index(name)]);
+ if (fm_tgt == &namedfm[-1]) {
return;
}
if (update && fm.fmark.timestamp < fm_tgt->fmark.timestamp) {
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index 3f63e274bb..aff6e7273a 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -1,6 +1,8 @@
#ifndef NVIM_MARK_H
#define NVIM_MARK_H
+#include "nvim/macros.h"
+#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/memory.h"
@@ -46,6 +48,32 @@
SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
} while (0)
+/// Convert mark name to the offset
+static inline int mark_global_index(const char name)
+ FUNC_ATTR_CONST
+{
+ return (ASCII_ISUPPER(name)
+ ? (name - 'A')
+ : (ascii_isdigit(name)
+ ? (NMARKS + (name - '0'))
+ : -1));
+}
+
+/// Convert local mark name to the offset
+static inline int mark_local_index(const char name)
+ FUNC_ATTR_CONST
+{
+ return (ASCII_ISLOWER(name)
+ ? (name - 'a')
+ : (name == '"'
+ ? NMARKS
+ : (name == '^'
+ ? NMARKS + 1
+ : (name == '.'
+ ? NMARKS + 2
+ : -1))));
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mark.h.generated.h"
#endif
diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h
index 2f7c9b4ed4..af4727d634 100644
--- a/src/nvim/mark_defs.h
+++ b/src/nvim/mark_defs.h
@@ -19,6 +19,12 @@
/// Total possible number of global marks
#define NGLOBALMARKS (NMARKS + EXTRA_MARKS)
+/// Total possible number of local marks
+///
+/// That are uppercase marks plus '"', '^' and '.'. There are other local marks,
+/// but they are not saved in ShaDa files.
+#define NLOCALMARKS (NMARKS + 3)
+
/// Maximum number of marks in jump list
#define JUMPLISTSIZE 100
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 52064fae06..0d7c319fba 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -54,22 +54,6 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-/*
- * Registers:
- * 0 = register for latest (unnamed) yank
- * 1..9 = registers '1' to '9', for deletes
- * 10..35 = registers 'a' to 'z'
- * 36 = delete register '-'
- * 37 = selection register '*'
- * 38 = clipboard register '+'
- */
-#define DELETION_REGISTER 36
-#define NUM_SAVED_REGISTERS 37
-// 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 */
@@ -749,31 +733,6 @@ 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!
@@ -806,7 +765,7 @@ yankreg_T *get_yank_register(int regname, int mode)
return y_previous;
}
- int i = reg_index(regname);
+ int i = op_reg_index(regname);
// when not 0-9, a-z, A-Z or '-'/'+'/'*': use register 0
if (i == -1) {
i = 0;
@@ -5417,7 +5376,7 @@ size_t op_register_amount(void)
/// Set register to a given value
void register_set(const char name, const yankreg_T reg)
{
- int i = reg_index(name);
+ int i = op_reg_index(name);
if (i == -1) {
return;
}
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 4da5cfc93d..5565f1631f 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -3,6 +3,8 @@
#include <stdbool.h>
+#include "nvim/macros.h"
+#include "nvim/ascii.h"
#include "nvim/types.h"
#include "nvim/api/private/defs.h"
#include "nvim/os/time.h"
@@ -18,6 +20,22 @@ typedef int (*Indenter)(void);
#define PUT_LINE_FORWARD 32 /* put linewise register below Visual sel. */
/*
+ * Registers:
+ * 0 = register for latest (unnamed) yank
+ * 1..9 = registers '1' to '9', for deletes
+ * 10..35 = registers 'a' to 'z'
+ * 36 = delete register '-'
+ * 37 = selection register '*'
+ * 38 = clipboard register '+'
+ */
+#define DELETION_REGISTER 36
+#define NUM_SAVED_REGISTERS 37
+// The following registers should not be saved in ShaDa file:
+#define STAR_REGISTER 37
+#define PLUS_REGISTER 38
+#define NUM_REGISTERS 39
+
+/*
* Operator IDs; The order must correspond to opchars[] in ops.c!
*/
#define OP_NOP 0 /* no pending operation */
@@ -66,6 +84,31 @@ typedef struct yankreg {
Dictionary *additional_data; ///< Additional data from ShaDa file.
} yankreg_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 op_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;
+ }
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ops.h.generated.h"
#endif
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 6920115735..a7bde92032 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
@@ -154,7 +155,6 @@ enum SRNIFlags {
kSDReadUndisableableData = (
(1 << kSDItemSearchPattern)
| (1 << kSDItemSubString)
- | (1 << kSDItemGlobalMark)
| (1 << kSDItemJump)
), ///< Data reading which cannot be disabled by &shada or other options
///< except for disabling reading ShaDa as a whole.
@@ -175,6 +175,11 @@ enum SRNIFlags {
kSDReadUnknown = (1 << (SHADA_LAST_ENTRY + 1)), ///< Determines whether
///< unknown items should be
///< read (usually disabled).
+ kSDReadGlobalMarks = (1 << kSDItemGlobalMark), ///< Determines whether global
+ ///< marks should be read. Can
+ ///< only be disabled by
+ ///< having f0 in &shada when
+ ///< writing.
kSDReadLocalMarks = (1 << kSDItemLocalMark), ///< Determines whether local
///< marks should be read. Can
///< only be disabled by
@@ -286,6 +291,41 @@ typedef struct {
uint8_t history_type;
} HistoryMergerState;
+/// ShadaEntry structure that knows whether it should be freed
+typedef struct {
+ ShadaEntry data; ///< ShadaEntry data.
+ bool can_free_entry; ///< True if entry can be freed.
+} PossiblyFreedShadaEntry;
+
+/// Structure that holds one file marks.
+typedef struct {
+ PossiblyFreedShadaEntry marks[NLOCALMARKS]; ///< All file marks.
+ PossiblyFreedShadaEntry changes[JUMPLISTSIZE]; ///< All file changes.
+ size_t changes_size; ///< Number of changes occupied.
+ ShadaEntry *additional_marks; ///< All marks with unknown names.
+ size_t additional_marks_size; ///< Size of the additional_marks array.
+ Timestamp greatest_timestamp; ///< Greatest timestamp among marks.
+ bool is_local_entry; ///< True if structure comes from the current session.
+} FileMarks;
+
+KHASH_MAP_INIT_STR(file_marks, FileMarks)
+
+/// State structure used by shada_write
+///
+/// Before actually writing most of the data is read to this structure.
+typedef struct {
+ HistoryMergerState hms[HIST_COUNT]; ///< Structures for history merging.
+ PossiblyFreedShadaEntry global_marks[NGLOBALMARKS]; ///< All global marks.
+ PossiblyFreedShadaEntry registers[NUM_SAVED_REGISTERS]; ///< All registers.
+ PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; ///< All dumped jumps.
+ size_t jumps_size; ///< Number of jumps occupied.
+ PossiblyFreedShadaEntry search_pattern; ///< Last search pattern.
+ PossiblyFreedShadaEntry sub_search_pattern; ///< Last s/ search pattern.
+ PossiblyFreedShadaEntry replacement; ///< Last s// replacement string.
+ khash_t(strset) dumped_variables; ///< Names of already dumped variables.
+ khash_t(file_marks) file_marks; ///< All file marks.
+} WriteMergerState;
+
struct sd_read_def;
/// Function used to read ShaDa files
@@ -457,18 +497,6 @@ static inline void hmll_dealloc(HMLList *const hmll)
xfree(hmll->free_entries);
}
-/// Free linked list and all entries
-///
-/// @param[in] hmll List to free.
-static inline void hmll_free(HMLList *const hmll)
- FUNC_ATTR_NONNULL_ALL
-{
- HMLL_FORALL(hmll, cur_entry) {
- shada_free_shada_entry(&cur_entry->data);
- }
- hmll_dealloc(hmll);
-}
-
/// Wrapper for reading from file descriptors
///
/// @return true if read was successfull, false otherwise.
@@ -897,14 +925,7 @@ static inline void hms_to_he_array(const HistoryMergerState *const hms_p,
static inline void hms_dealloc(HistoryMergerState *const hms_p)
FUNC_ATTR_NONNULL_ALL
{
- if (hms_p->reading) {
- // Free only the linked list if reading because all of the allocated memory
- // was either already freed or saved in internal NeoVim history.
- hmll_dealloc(&hms_p->hmll);
- } else {
- // Free everything because when writing data is only used once to write it.
- hmll_free(&hms_p->hmll);
- }
+ hmll_dealloc(&hms_p->hmll);
}
/// Iterate over all history entries in history merger, in order
@@ -957,30 +978,28 @@ static inline bool marks_equal(const pos_T a, const pos_T b)
static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
FUNC_ATTR_NONNULL_ALL
{
- unsigned srni_flags = 0;
const bool force = flags & kShaDaForceit;
const bool get_old_files = flags & (kShaDaGetOldfiles | kShaDaForceit);
const bool want_marks = flags & kShaDaWantMarks;
- if (flags & kShaDaWantInfo) {
- srni_flags |= kSDReadUndisableableData | kSDReadRegisters;
- if (p_hi) {
- srni_flags |= kSDReadHistory;
- }
- if (find_shada_parameter('!') != NULL) {
- srni_flags |= kSDReadVariables;
- }
- if (find_shada_parameter('%') != NULL && ARGCOUNT == 0) {
- srni_flags |= kSDReadBufferList;
- }
- }
- if (want_marks) {
- if (get_shada_parameter('\'') > 0) {
- srni_flags |= kSDReadLocalMarks | kSDReadChanges;
- }
- }
- if (get_old_files) {
- srni_flags |= kSDReadLocalMarks;
- }
+ const unsigned srni_flags = ((flags & kShaDaWantInfo
+ ? (kSDReadUndisableableData
+ | kSDReadRegisters
+ | kSDReadGlobalMarks
+ | (p_hi ? kSDReadHistory : 0)
+ | (find_shada_parameter('!') != NULL
+ ? kSDReadVariables
+ : 0)
+ | (find_shada_parameter('%') != NULL
+ && ARGCOUNT == 0
+ ? kSDReadBufferList
+ : 0))
+ : 0)
+ | (want_marks && get_shada_parameter('\'') > 0
+ ? kSDReadLocalMarks | kSDReadChanges
+ : 0)
+ | (get_old_files
+ ? kSDReadLocalMarks
+ : 0));
if (srni_flags == 0) {
// Nothing to do.
return;
@@ -1009,7 +1028,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
oldfiles_list = list_alloc();
set_vim_var_list(VV_OLDFILES, oldfiles_list);
}
- while (shada_read_next_item(sd_reader, &cur_entry, srni_flags) == NOTDONE) {
+ while (shada_read_next_item(sd_reader, &cur_entry, srni_flags, 0)
+ == NOTDONE) {
switch (cur_entry.type) {
case kSDItemMissing: {
assert(false);
@@ -1689,6 +1709,140 @@ static void shada_pack_entry(msgpack_packer *const packer,
msgpack_sbuffer_destroy(&sbuf);
}
+/// Write single ShaDa entry, converting it if needed
+///
+/// @warning Frees entry after packing.
+///
+/// @param[in] packer Packer used to write entry.
+/// @param[in] sd_conv Conversion definitions.
+/// @param[in] entry Entry written. If entry.can_free_entry is false then
+/// it assumes that entry was not converted, otherwise it
+/// is assumed that entry was already converted.
+/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
+/// restrictions.
+static void shada_pack_encoded_entry(msgpack_packer *const packer,
+ const vimconv_T *const sd_conv,
+ PossiblyFreedShadaEntry entry,
+ const size_t max_kbyte)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (entry.can_free_entry) {
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ shada_free_shada_entry(&entry.data);
+ return;
+ }
+#define RUN_WITH_CONVERTED_STRING(cstr, code) \
+ do { \
+ bool did_convert = false; \
+ if (sd_conv->vc_type != CONV_NONE && has_non_ascii((cstr))) { \
+ char *const converted_string = string_convert(sd_conv, (cstr), NULL); \
+ if (converted_string != NULL) { \
+ (cstr) = converted_string; \
+ did_convert = true; \
+ } \
+ } \
+ code \
+ if (did_convert) { \
+ xfree((cstr)); \
+ } \
+ } while (0)
+ switch (entry.data.type) {
+ case kSDItemUnknown:
+ case kSDItemMissing: {
+ assert(false);
+ }
+ case kSDItemSearchPattern: {
+ RUN_WITH_CONVERTED_STRING(entry.data.data.search_pattern.pat, {
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ });
+ break;
+ }
+ case kSDItemHistoryEntry: {
+ RUN_WITH_CONVERTED_STRING(entry.data.data.history_item.string, {
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ });
+ break;
+ }
+ case kSDItemSubString: {
+ RUN_WITH_CONVERTED_STRING(entry.data.data.sub_string.sub, {
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ });
+ break;
+ }
+ case kSDItemVariable: {
+ if (sd_conv->vc_type != CONV_NONE) {
+ convert_object(sd_conv, &entry.data.data.global_var.value);
+ }
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ break;
+ }
+ case kSDItemRegister: {
+ bool did_convert = false;
+ if (sd_conv->vc_type != CONV_NONE) {
+ size_t first_non_ascii = 0;
+ for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
+ if (has_non_ascii(entry.data.data.reg.contents[i])) {
+ first_non_ascii = i;
+ did_convert = true;
+ break;
+ }
+ }
+ if (did_convert) {
+ entry.data.data.reg.contents =
+ xmemdup(entry.data.data.reg.contents,
+ (entry.data.data.reg.contents_size
+ * sizeof(entry.data.data.reg.contents)));
+ for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
+ if (i >= first_non_ascii) {
+ entry.data.data.reg.contents[i] = get_converted_string(
+ sd_conv,
+ entry.data.data.reg.contents[i],
+ strlen(entry.data.data.reg.contents[i]));
+ } else {
+ entry.data.data.reg.contents[i] =
+ xstrdup(entry.data.data.reg.contents[i]);
+ }
+ }
+ }
+ }
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ if (did_convert) {
+ for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
+ xfree(entry.data.data.reg.contents[i]);
+ }
+ xfree(entry.data.data.reg.contents);
+ }
+ break;
+ }
+ case kSDItemHeader:
+ case kSDItemGlobalMark:
+ case kSDItemJump:
+ case kSDItemBufferList:
+ case kSDItemLocalMark:
+ case kSDItemChange: {
+ shada_pack_entry(packer, entry.data, max_kbyte);
+ break;
+ }
+ }
+#undef RUN_WITH_CONVERTED_STRING
+}
+
+/// Compare two FileMarks structure to order them by greatest_timestamp
+///
+/// Order is reversed: structure with greatest greatest_timestamp comes first.
+/// Function signature is compatible with qsort.
+static int compare_file_marks(const void *a, const void *b)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ const FileMarks *const *const a_fms = a;
+ const FileMarks *const *const b_fms = b;
+ return ((*a_fms)->greatest_timestamp == (*b_fms)->greatest_timestamp
+ ? 0
+ : ((*a_fms)->greatest_timestamp > (*b_fms)->greatest_timestamp
+ ? -1
+ : 1));
+}
+
/// Write ShaDa file
///
/// @param[in] sd_writer Structure containing file writer definition.
@@ -1699,11 +1853,6 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
ShaDaReadDef *const sd_reader)
FUNC_ATTR_NONNULL_ARG(1)
{
- // TODO(ZyX-I): Write only one search pattern, substitute search pattern and
- // substitute replacement string, which has the greatest timestamp. Make sure
- // that this also applies if ShaDa file contains more then one replacement
- // string.
- khash_t(bufset) *const removable_bufs = kh_init(bufset);
int max_kbyte_i = get_shada_parameter('s');
if (max_kbyte_i < 0) {
max_kbyte_i = 10;
@@ -1711,10 +1860,49 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
if (max_kbyte_i == 0) {
return;
}
+
+ WriteMergerState *const wms = xcalloc(1, sizeof(*wms));
+ bool dump_one_history[HIST_COUNT];
+ const bool dump_global_vars = (find_shada_parameter('!') != NULL);
+ int max_reg_lines = get_shada_parameter('<');
+ if (max_reg_lines < 0) {
+ max_reg_lines = get_shada_parameter('"');
+ }
+ const bool limit_reg_lines = max_reg_lines >= 0;
+ const bool dump_registers = (max_reg_lines != 0);
+ khash_t(bufset) *const removable_bufs = kh_init(bufset);
const size_t max_kbyte = (size_t) max_kbyte_i;
+ const size_t num_marked_files = (size_t) get_shada_parameter('\'');
+ const bool dump_global_marks = get_shada_parameter('f') != 0;
+ bool dump_history = false;
- msgpack_packer *packer = msgpack_packer_new(sd_writer,
- &msgpack_sd_writer_write);
+ // Initialize history merger
+ for (uint8_t i = 0; i < HIST_COUNT; i++) {
+ long num_saved = get_shada_parameter(hist_type2char(i));
+ if (num_saved == -1) {
+ num_saved = p_hi;
+ }
+ if (num_saved > 0) {
+ dump_history = true;
+ dump_one_history[i] = true;
+ hms_init(&wms->hms[i], i, (size_t) num_saved, sd_reader != NULL, false);
+ } else {
+ dump_one_history[i] = false;
+ }
+ }
+
+ const unsigned srni_flags = (
+ kSDReadUndisableableData
+ | kSDReadUnknown
+ | (dump_history ? kSDReadHistory : 0)
+ | (dump_registers ? kSDReadRegisters : 0)
+ | (dump_global_vars ? kSDReadVariables : 0)
+ | (dump_global_marks ? kSDReadGlobalMarks : 0)
+ | (num_marked_files ? kSDReadLocalMarks | kSDReadChanges : 0)
+ );
+
+ msgpack_packer *const packer = msgpack_packer_new(sd_writer,
+ &msgpack_sd_writer_write);
FOR_ALL_BUFFERS(buf) {
if (buf->b_ffname != NULL && shada_removable((char *) buf->b_ffname)) {
@@ -1723,10 +1911,7 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
}
}
- // TODO(ZyX-I): Iterate over sd_reader, keeping “replaced” values in a set.
-
- // First write values that do not require merging
- // 1. Header
+ // Write header
shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemHeader,
.timestamp = os_time(),
@@ -1748,7 +1933,7 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
}
}, 0);
- // 2. Buffer list
+ // Write buffer list
if (find_shada_parameter('%') != NULL) {
size_t buf_count = 0;
FOR_ALL_BUFFERS(buf) {
@@ -1784,129 +1969,94 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
xfree(buflist_entry.data.buffer_list.buffers);
}
- // 3. Jump list
- const void *jump_iter = NULL;
- do {
- xfmark_T fm;
- cleanup_jumplist();
- jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
- const buf_T *const buf = (fm.fmark.fnum == 0
- ? NULL
- : buflist_findnr(fm.fmark.fnum));
- if (buf != NULL
- ? SHADA_REMOVABLE(buf)
- : fm.fmark.fnum != 0) {
- continue;
- }
- const char *const fname = (char *) (fm.fmark.fnum == 0
- ? (fm.fname == NULL
- ? NULL
- : fm.fname)
- : buf->b_ffname);
- shada_pack_entry(packer, (ShadaEntry) {
- .type = kSDItemJump,
- .timestamp = fm.fmark.timestamp,
- .data = {
- .filemark = {
- .name = NUL,
- .mark = fm.fmark.mark,
- .fname = (char *) fname,
- .additional_data = fm.fmark.additional_data,
- }
+ // Write some of the variables
+ if (dump_global_vars) {
+ const void *var_iter = NULL;
+ const Timestamp cur_timestamp = os_time();
+ do {
+ typval_T vartv;
+ const char *name;
+ var_iter = var_shada_iter(var_iter, &name, &vartv);
+ if (var_iter == NULL && vartv.v_type == VAR_UNKNOWN) {
+ break;
}
- }, max_kbyte);
- } while (jump_iter != NULL);
-
- // FIXME No merging currently
-
-#define RUN_WITH_CONVERTED_STRING(cstr, code) \
- do { \
- bool did_convert = false; \
- if (sd_writer->sd_conv.vc_type != CONV_NONE && has_non_ascii((cstr))) { \
- char *const converted_string = string_convert(&sd_writer->sd_conv, \
- (cstr), NULL); \
- if (converted_string != NULL) { \
- (cstr) = converted_string; \
- did_convert = true; \
- } \
- } \
- code \
- if (did_convert) { \
- xfree((cstr)); \
- } \
- } while (0)
- // 4. History
- HistoryMergerState hms[HIST_COUNT];
- for (uint8_t i = 0; i < HIST_COUNT; i++) {
- long num_saved = get_shada_parameter(hist_type2char(i));
- if (num_saved == -1) {
- num_saved = p_hi;
- }
- if (num_saved > 0) {
- hms_init(&hms[i], i, (size_t) num_saved, false, false);
- hms_insert_whole_neovim_history(&hms[i]);
- HMS_ITER(&hms[i], cur_entry) {
- RUN_WITH_CONVERTED_STRING(cur_entry->data.data.history_item.string, {
- shada_pack_entry(packer, cur_entry->data, max_kbyte);
- });
+ Object obj = vim_to_object(&vartv);
+ if (sd_writer->sd_conv.vc_type != CONV_NONE) {
+ convert_object(&sd_writer->sd_conv, &obj);
}
- hms_dealloc(&hms[i]);
- }
+ shada_pack_entry(packer, (ShadaEntry) {
+ .type = kSDItemVariable,
+ .timestamp = cur_timestamp,
+ .data = {
+ .global_var = {
+ .name = (char *) name,
+ .value = obj,
+ .additional_elements = NULL,
+ }
+ }
+ }, max_kbyte);
+ api_free_object(obj);
+ clear_tv(&vartv);
+ int kh_ret;
+ (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret);
+ } while (var_iter != NULL);
}
- // 5. Search patterns
+ // Initialize search pattern
{
SearchPattern pat;
get_search_pattern(&pat);
- ShadaEntry sp_entry = (ShadaEntry) {
- .type = kSDItemSearchPattern,
- .timestamp = pat.timestamp,
+ wms->search_pattern = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
.data = {
- .search_pattern = {
- .magic = pat.magic,
- .smartcase = !pat.no_scs,
- .has_line_offset = pat.off.line,
- .place_cursor_at_end = pat.off.end,
- .offset = pat.off.off,
- .is_last_used = search_was_last_used(),
- .is_substitute_pattern = false,
- .highlighted = (!no_hlsearch && find_shada_parameter('h') != NULL),
- .pat = (char *) pat.pat,
- .additional_data = pat.additional_data,
+ .type = kSDItemSearchPattern,
+ .timestamp = pat.timestamp,
+ .data = {
+ .search_pattern = {
+ .magic = pat.magic,
+ .smartcase = !pat.no_scs,
+ .has_line_offset = pat.off.line,
+ .place_cursor_at_end = pat.off.end,
+ .offset = pat.off.off,
+ .is_last_used = search_was_last_used(),
+ .is_substitute_pattern = false,
+ .highlighted = (!no_hlsearch && find_shada_parameter('h') != NULL),
+ .pat = (char *) pat.pat,
+ .additional_data = pat.additional_data,
+ }
}
}
};
- RUN_WITH_CONVERTED_STRING(sp_entry.data.search_pattern.pat, {
- shada_pack_entry(packer, sp_entry, max_kbyte);
- });
}
+
+ // Initialize substitute search pattern
{
SearchPattern pat;
get_substitute_pattern(&pat);
- ShadaEntry sp_entry = (ShadaEntry) {
- .type = kSDItemSearchPattern,
- .timestamp = pat.timestamp,
+ wms->sub_search_pattern = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
.data = {
- .search_pattern = {
- .magic = pat.magic,
- .smartcase = !pat.no_scs,
- .has_line_offset = false,
- .place_cursor_at_end = false,
- .offset = 0,
- .is_last_used = !search_was_last_used(),
- .is_substitute_pattern = true,
- .highlighted = false,
- .pat = (char *) pat.pat,
- .additional_data = pat.additional_data,
+ .type = kSDItemSearchPattern,
+ .timestamp = pat.timestamp,
+ .data = {
+ .search_pattern = {
+ .magic = pat.magic,
+ .smartcase = !pat.no_scs,
+ .has_line_offset = false,
+ .place_cursor_at_end = false,
+ .offset = 0,
+ .is_last_used = !search_was_last_used(),
+ .is_substitute_pattern = true,
+ .highlighted = false,
+ .pat = (char *) pat.pat,
+ .additional_data = pat.additional_data,
+ }
}
}
};
- RUN_WITH_CONVERTED_STRING(sp_entry.data.search_pattern.pat, {
- shada_pack_entry(packer, sp_entry, max_kbyte);
- });
}
- // 6. Substitute string
+ // Initialize substitute replacement string
{
SubReplacementString sub;
sub_get_replacement(&sub);
@@ -1923,119 +2073,482 @@ static void shada_write(ShaDaWriteDef *const sd_writer,
}
}
};
- RUN_WITH_CONVERTED_STRING(sub_entry.data.sub_string.sub, {
- shada_pack_entry(packer, sub_entry, max_kbyte);
- });
}
- // 7. Global marks
- if (get_shada_parameter('f') != 0) {
- ShadaEntry *const global_marks = list_global_marks(removable_bufs);
- for (ShadaEntry *mark = global_marks; mark->type != kSDItemMissing;
- mark++) {
- shada_pack_entry(packer, *mark, max_kbyte);
- }
- xfree(global_marks);
- }
-
- // 8. Buffer marks and buffer change list
- FOR_ALL_BUFFERS(buf) {
- if (buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) {
+ // Initialize jump list
+ const void *jump_iter = NULL;
+ do {
+ xfmark_T fm;
+ cleanup_jumplist();
+ jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
+ const buf_T *const buf = (fm.fmark.fnum == 0
+ ? NULL
+ : buflist_findnr(fm.fmark.fnum));
+ if (buf != NULL
+ ? SHADA_REMOVABLE(buf)
+ : fm.fmark.fnum != 0) {
continue;
}
- ShadaEntry *const buffer_marks = list_buffer_marks(buf);
- for (ShadaEntry *mark = buffer_marks; mark->type != kSDItemMissing;
- mark++) {
- shada_pack_entry(packer, *mark, max_kbyte);
+ const char *const fname = (char *) (fm.fmark.fnum == 0
+ ? (fm.fname == NULL
+ ? NULL
+ : fm.fname)
+ : buf->b_ffname);
+ if (fname == NULL) {
+ continue;
}
- xfree(buffer_marks);
-
- for (int i = 0; i < buf->b_changelistlen; i++) {
- const fmark_T fm = buf->b_changelist[i];
- shada_pack_entry(packer, (ShadaEntry) {
- .type = kSDItemChange,
- .timestamp = fm.timestamp,
+ wms->jumps[wms->jumps_size++] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemJump,
+ .timestamp = fm.fmark.timestamp,
.data = {
.filemark = {
- .mark = fm.mark,
- .fname = (char *) buf->b_ffname,
- .additional_data = fm.additional_data,
+ .name = NUL,
+ .mark = fm.fmark.mark,
+ .fname = (char *) fname,
+ .additional_data = fm.fmark.additional_data,
}
}
- }, max_kbyte);
- }
- }
- // FIXME: Copy previous marks, up to num_marked_files
- // size_t num_marked_files = get_shada_parameter('\'');
+ }
+ };
+ } while (jump_iter != NULL);
- // 9. Registers
- int max_num_lines_i = get_shada_parameter('<');
- if (max_num_lines_i < 0) {
- max_num_lines_i = get_shada_parameter('"');
+ // Initialize global marks
+ if (dump_global_marks) {
+ const void *global_mark_iter = NULL;
+ do {
+ char name;
+ xfmark_T fm;
+ global_mark_iter = mark_global_iter(global_mark_iter, &name, &fm);
+ if (fm.fmark.mark.lnum == 0) {
+ break;
+ }
+ const char *fname;
+ if (fm.fmark.fnum == 0) {
+ assert(fm.fname != NULL);
+ if (shada_removable((const char *) fm.fname)) {
+ continue;
+ }
+ fname = (const char *) fm.fname;
+ } else {
+ const buf_T *const buf = buflist_findnr(fm.fmark.fnum);
+ if (buf == NULL || buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) {
+ continue;
+ }
+ fname = (const char *) buf->b_ffname;
+ }
+ wms->global_marks[mark_global_index(name)] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemGlobalMark,
+ .timestamp = fm.fmark.timestamp,
+ .data = {
+ .filemark = {
+ .mark = fm.fmark.mark,
+ .name = name,
+ .additional_data = fm.fmark.additional_data,
+ .fname = (char *) fname,
+ }
+ }
+ },
+ };
+ } while(global_mark_iter != NULL);
}
- if (max_num_lines_i != 0) {
- const size_t max_num_lines = (max_num_lines_i < 0
- ? 0
- : (size_t) max_num_lines_i);
- ShadaEntry *const registers = list_registers(max_num_lines);
- for (ShadaEntry *reg = registers; reg->type != kSDItemMissing; reg++) {
- bool did_convert = false;
- if (sd_writer->sd_conv.vc_type != CONV_NONE) {
- did_convert = true;
- reg->data.reg.contents = xmemdup(reg->data.reg.contents,
- (reg->data.reg.contents_size
- * sizeof(reg->data.reg.contents)));
- for (size_t i = 0; i < reg->data.reg.contents_size; i++) {
- reg->data.reg.contents[i] = get_converted_string(
- &sd_writer->sd_conv,
- reg->data.reg.contents[i], strlen(reg->data.reg.contents[i]));
+
+ // Initialize registers
+ if (dump_registers) {
+ const void *reg_iter = NULL;
+ do {
+ yankreg_T reg;
+ char name;
+ reg_iter = op_register_iter(reg_iter, &name, &reg);
+ if (reg.y_array == NULL) {
+ break;
+ }
+ if (limit_reg_lines && reg.y_size > max_reg_lines) {
+ continue;
+ }
+ wms->registers[op_reg_index(name)] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemRegister,
+ .timestamp = reg.timestamp,
+ .data = {
+ .reg = {
+ .contents = (char **) reg.y_array,
+ .contents_size = (size_t) reg.y_size,
+ .type = (uint8_t) reg.y_type,
+ .width = (size_t) (reg.y_type == MBLOCK ? reg.y_width : 0),
+ .additional_data = reg.additional_data,
+ .name = name,
+ }
+ }
}
+ };
+ } while(reg_iter != NULL);
+ }
+
+ // Initialize buffers
+ if (num_marked_files > 0) {
+ FOR_ALL_BUFFERS(buf) {
+ if (buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) {
+ continue;
}
- shada_pack_entry(packer, *reg, max_kbyte);
- if (did_convert) {
- for (size_t i = 0; i < reg->data.reg.contents_size; i++) {
- xfree(reg->data.reg.contents[i]);
+ const void *local_marks_iter = NULL;
+ const char *const fname = (const char *) buf->b_ffname;
+ khiter_t k;
+ int kh_ret;
+ k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret);
+ FileMarks *const filemarks = &kh_val(&wms->file_marks, k);
+ if (kh_ret > 0) {
+ kh_key(&wms->file_marks, k) = xstrdup(fname);
+ memset(filemarks, 0, sizeof(*filemarks));
+ }
+ filemarks->is_local_entry = true;
+ do {
+ fmark_T fm;
+ char name;
+ local_marks_iter = mark_buffer_iter(local_marks_iter, buf, &name, &fm);
+ if (fm.mark.lnum == 0) {
+ break;
+ }
+ filemarks->marks[mark_local_index(name)] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemLocalMark,
+ .timestamp = fm.timestamp,
+ .data = {
+ .filemark = {
+ .mark = fm.mark,
+ .name = name,
+ .fname = (char *) fname,
+ .additional_data = fm.additional_data,
+ }
+ }
+ }
+ };
+ if (fm.timestamp > filemarks->greatest_timestamp) {
+ filemarks->greatest_timestamp = fm.timestamp;
+ }
+ } while(local_marks_iter != NULL);
+ for (int i = 0; i < buf->b_changelistlen; i++) {
+ const fmark_T fm = buf->b_changelist[i];
+ filemarks->changes[i] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemChange,
+ .timestamp = fm.timestamp,
+ .data = {
+ .filemark = {
+ .mark = fm.mark,
+ .fname = (char *) fname,
+ .additional_data = fm.additional_data,
+ }
+ }
+ }
+ };
+ if (fm.timestamp > filemarks->greatest_timestamp) {
+ filemarks->greatest_timestamp = fm.timestamp;
}
- xfree(reg->data.reg.contents);
}
+ filemarks->changes_size = (size_t) buf->b_changelistlen;
}
- xfree(registers);
}
- // 10. Variables
- if (find_shada_parameter('!') != NULL) {
- const void *var_iter = NULL;
- const Timestamp cur_timestamp = os_time();
- do {
- typval_T vartv;
- const char *name;
- var_iter = var_shada_iter(var_iter, &name, &vartv);
- if (var_iter == NULL && vartv.v_type == VAR_UNKNOWN) {
+ if (sd_reader == NULL) {
+ goto shada_write_remaining;
+ }
+
+ ShadaEntry entry;
+ while (shada_read_next_item(sd_reader, &entry, srni_flags, max_kbyte)
+ == NOTDONE) {
+#define COMPARE_WITH_ENTRY(wms_entry_, entry) \
+ do { \
+ PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \
+ if (wms_entry->data.type != kSDItemMissing) { \
+ if (wms_entry->data.timestamp >= (entry).timestamp) { \
+ shada_free_shada_entry(&(entry)); \
+ break; \
+ } \
+ if (wms_entry->can_free_entry) { \
+ shada_free_shada_entry(&wms_entry->data); \
+ } \
+ } \
+ wms_entry->can_free_entry = true; \
+ wms_entry->data = (entry); \
+ } while (0)
+ switch (entry.type) {
+ case kSDItemMissing: {
break;
}
- Object obj = vim_to_object(&vartv);
- if (sd_writer->sd_conv.vc_type != CONV_NONE) {
- convert_object(&sd_writer->sd_conv, &obj);
+ case kSDItemHeader:
+ case kSDItemBufferList: {
+ assert(false);
}
- shada_pack_entry(packer, (ShadaEntry) {
- .type = kSDItemVariable,
- .timestamp = cur_timestamp,
- .data = {
- .global_var = {
- .name = (char *) name,
- .value = obj,
- .additional_elements = NULL,
+ case kSDItemUnknown: {
+ shada_pack_entry(packer, entry, 0);
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ case kSDItemSearchPattern: {
+ COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern
+ ? &wms->sub_search_pattern
+ : &wms->search_pattern), entry);
+ break;
+ }
+ case kSDItemSubString: {
+ COMPARE_WITH_ENTRY(&wms->replacement, entry);
+ break;
+ }
+ case kSDItemHistoryEntry: {
+ if (entry.data.history_item.histtype >= HIST_COUNT) {
+ shada_pack_entry(packer, entry, 0);
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ hms_insert(&wms->hms[entry.data.history_item.histtype], entry, true,
+ true);
+ break;
+ }
+ case kSDItemRegister: {
+ const int idx = op_reg_index(entry.data.reg.name);
+ if (idx < 0) {
+ shada_pack_entry(packer, entry, 0);
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ COMPARE_WITH_ENTRY(&wms->registers[idx], entry);
+ break;
+ }
+ case kSDItemVariable: {
+ if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) {
+ shada_pack_entry(packer, entry, 0);
+ }
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ case kSDItemGlobalMark: {
+ const int idx = mark_global_index(entry.data.filemark.name);
+ if (idx < 0) {
+ shada_pack_entry(packer, entry, 0);
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry);
+ break;
+ }
+ case kSDItemChange:
+ case kSDItemLocalMark: {
+ if (shada_removable(entry.data.filemark.fname)) {
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ const char *const fname = (const char *) entry.data.filemark.fname;
+ khiter_t k;
+ int kh_ret;
+ k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret);
+ FileMarks *const filemarks = &kh_val(&wms->file_marks, k);
+ if (kh_ret > 0) {
+ kh_key(&wms->file_marks, k) = xstrdup(fname);
+ memset(filemarks, 0, sizeof(*filemarks));
+ }
+ if (entry.timestamp > filemarks->greatest_timestamp) {
+ filemarks->greatest_timestamp = entry.timestamp;
+ }
+ if (entry.type == kSDItemLocalMark) {
+ const int idx = mark_local_index(entry.data.filemark.name);
+ if (idx < 0) {
+ filemarks->additional_marks = xrealloc(
+ filemarks->additional_marks,
+ (++filemarks->additional_marks_size
+ * sizeof(filemarks->additional_marks[0])));
+ filemarks->additional_marks[filemarks->additional_marks_size - 1] =
+ entry;
+ } else {
+ COMPARE_WITH_ENTRY(&filemarks->marks[idx], entry);
+ }
+ } else {
+ if (filemarks->is_local_entry) {
+ shada_free_shada_entry(&entry);
+ } else {
+ const int cl_len = (int) filemarks->changes_size;
+ int i;
+ for (i = cl_len; i > 0; i--) {
+ const ShadaEntry old_entry = filemarks->changes[i - 1].data;
+ if (old_entry.timestamp <= entry.timestamp) {
+ if (marks_equal(old_entry.data.filemark.mark,
+ entry.data.filemark.mark)) {
+ i = -1;
+ }
+ break;
+ }
+ }
+ if (i > 0) {
+ if (cl_len == JUMPLISTSIZE) {
+ if (filemarks->changes[0].can_free_entry) {
+ shada_free_shada_entry(&filemarks->changes[0].data);
+ }
+ memmove(&filemarks->changes[0], &filemarks->changes[1],
+ sizeof(filemarks->changes[0]) * (size_t) i);
+ } else if (i == 0) {
+ if (cl_len == JUMPLISTSIZE) {
+ i = -1;
+ } else {
+ memmove(&filemarks->changes[1], &filemarks->changes[0],
+ sizeof(filemarks->changes[0]) * (size_t) cl_len);
+ }
+ }
+ }
+ if (i != -1) {
+ filemarks->changes[i] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = true,
+ .data = entry
+ };
+ if (cl_len < JUMPLISTSIZE) {
+ filemarks->changes_size++;
+ }
+ } else {
+ shada_free_shada_entry(&entry);
+ }
}
}
- }, max_kbyte);
- api_free_object(obj);
- clear_tv(&vartv);
- } while (var_iter != NULL);
+ break;
+ }
+ case kSDItemJump: {
+ const int jl_len = (int) wms->jumps_size;
+ int i;
+ for (i = 0; i < jl_len; i++) {
+ const ShadaEntry old_entry = wms->jumps[i].data;
+ if (old_entry.timestamp >= entry.timestamp) {
+ if (marks_equal(old_entry.data.filemark.mark,
+ entry.data.filemark.mark)
+ && strcmp(old_entry.data.filemark.fname,
+ entry.data.filemark.fname) == 0) {
+ i = -1;
+ }
+ break;
+ }
+ }
+ if (i != -1) {
+ if (i < jl_len) {
+ if (jl_len == JUMPLISTSIZE) {
+ if (wms->jumps[0].can_free_entry) {
+ shada_free_shada_entry(&wms->jumps[0].data);
+ }
+ memmove(&wms->jumps[0], &wms->jumps[1],
+ sizeof(wms->jumps[0]) * (size_t) i);
+ } else {
+ memmove(&wms->jumps[i + 1], &wms->jumps[i],
+ sizeof(wms->jumps[0]) * (size_t) (jl_len - i));
+ }
+ } else if (i == jl_len) {
+ if (jl_len == JUMPLISTSIZE) {
+ i = -1;
+ } else if (jl_len > 0) {
+ memmove(&wms->jumps[1], &wms->jumps[0],
+ sizeof(wms->jumps[0]) * (size_t) jl_len);
+ }
+ }
+ }
+ if (i != -1) {
+ wms->jumps[i] = (PossiblyFreedShadaEntry) {
+ .can_free_entry = true,
+ .data = entry,
+ };
+ if (jl_len < JUMPLISTSIZE) {
+ wms->jumps_size++;
+ }
+ } else {
+ shada_free_shada_entry(&entry);
+ }
+ break;
+ }
+ }
+ }
+#undef COMPARE_WITH_ENTRY
+
+ // Write the rest
+shada_write_remaining:
+#define PACK_WMS_ARRAY(wms_array) \
+ do { \
+ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \
+ if (wms_array[i_].data.type != kSDItemMissing) { \
+ shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_array[i_], \
+ max_kbyte); \
+ } \
+ } \
+ } while (0)
+ PACK_WMS_ARRAY(wms->global_marks);
+ PACK_WMS_ARRAY(wms->registers);
+ for (size_t i = 0; i < wms->jumps_size; i++) {
+ shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i],
+ max_kbyte);
+ }
+#define PACK_WMS_ENTRY(wms_entry) \
+ do { \
+ if (wms_entry.data.type != kSDItemMissing) { \
+ shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \
+ max_kbyte); \
+ } \
+ } while (0)
+ PACK_WMS_ENTRY(wms->search_pattern);
+ PACK_WMS_ENTRY(wms->sub_search_pattern);
+ PACK_WMS_ENTRY(wms->replacement);
+#undef PACK_WMS_ENTRY
+
+ const size_t file_markss_size = kh_size(&wms->file_marks);
+ FileMarks **const all_file_markss =
+ xmalloc(file_markss_size * sizeof(*all_file_markss));
+ FileMarks **cur_file_marks = all_file_markss;
+ for (khint_t i = kh_begin(&wms->file_marks);
+ i != kh_end(&wms->file_marks);
+ i++) {
+ if (kh_exist(&wms->file_marks, i)) {
+ *cur_file_marks++ = &kh_val(&wms->file_marks, i);
+ xfree((void *) kh_key(&wms->file_marks, i));
+ }
+ }
+ qsort((void *) all_file_markss, file_markss_size, sizeof(*all_file_markss),
+ &compare_file_marks);
+ const size_t file_markss_to_dump = MIN(num_marked_files, file_markss_size);
+ for (size_t i = 0; i < file_markss_to_dump; i++) {
+ PACK_WMS_ARRAY(all_file_markss[i]->marks);
+ for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) {
+ shada_pack_encoded_entry(packer, &sd_writer->sd_conv,
+ all_file_markss[i]->changes[j], max_kbyte);
+ }
+ for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) {
+ shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], 0);
+ shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]);
+ }
+ xfree(all_file_markss[i]->additional_marks);
+ }
+ xfree(all_file_markss);
+#undef PACK_WMS_ARRAY
+
+ if (dump_history) {
+ for (size_t i = 0; i < HIST_COUNT; i++) {
+ if (dump_one_history[i]) {
+ hms_insert_whole_neovim_history(&wms->hms[i]);
+ HMS_ITER(&wms->hms[i], cur_entry) {
+ shada_pack_encoded_entry(packer, &sd_writer->sd_conv,
+ (PossiblyFreedShadaEntry) {
+ .data = cur_entry->data,
+ .can_free_entry =
+ cur_entry->can_free_entry,
+ }, max_kbyte);
+ }
+ hms_dealloc(&wms->hms[i]);
+ }
+ }
}
-#undef RUN_WITH_CONVERTED_STRING
+ kh_dealloc(file_marks, &wms->file_marks);
kh_destroy(bufset, removable_bufs);
msgpack_packer_free(packer);
+ kh_dealloc(strset, &wms->dumped_variables);
+ xfree(wms);
}
#undef PACK_STATIC_STR
@@ -2162,7 +2675,7 @@ shada_write_file_nomerge: {}
convert_setup(&sd_writer.sd_conv, p_enc, "utf-8");
- shada_write(&sd_writer, NULL);
+ shada_write(&sd_writer, (nomerge ? NULL : &sd_reader));
close_file((int)(intptr_t) sd_writer.cookie);
@@ -2519,12 +3032,15 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
/// @param[out] entry Address where next entry contents will be saved.
/// @param[in] flags Flags, determining whether and which items should be
/// skipped (see SRNIFlags enum).
+/// @param[in] max_kbyte If non-zero, skip reading entries which have length
+/// greater then given.
///
/// @return NOTDONE if entry was read correctly, FAIL if there were errors and
/// OK at EOF.
static int shada_read_next_item(ShaDaReadDef *const sd_reader,
ShadaEntry *const entry,
- const unsigned flags)
+ const unsigned flags,
+ const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
shada_read_next_item_start:
@@ -2558,7 +3074,8 @@ shada_read_next_item_start:
if ((type_u64 > SHADA_LAST_ENTRY
? !(flags & kSDReadUnknown)
- : !((unsigned) (1 << type_u64) & flags))) {
+ : !((unsigned) (1 << type_u64) & flags))
+ || (max_kbyte && length > max_kbyte * 1024)) {
if (fread_len(sd_reader, NULL, length) != OK) {
return FAIL;
}
@@ -3300,92 +3817,6 @@ shada_read_next_item_end:
return NOTDONE;
}
-/// Return a list with all global marks set in current NeoVim instance
-///
-/// List is not sorted.
-///
-/// @warning Listed marks must be used before any buffer- or mark-editing
-/// function is run.
-///
-/// @param[in] removable_bufs Set of buffers on removable media.
-///
-/// @return Array of ShadaEntry values, last one has type kSDItemMissing.
-///
-/// @warning Resulting ShadaEntry values must not be freed. Returned
-/// array must be freed with `xfree()`.
-static ShadaEntry *list_global_marks(
- const khash_t(bufset) *const removable_bufs)
-{
- const void *iter = NULL;
- const size_t nummarks = mark_global_amount();
- ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (nummarks + 1));
- ShadaEntry *cur = ret;
- if (nummarks) {
- do {
- cur->type = kSDItemGlobalMark;
- xfmark_T cur_fm;
- iter = mark_global_iter(iter, &(cur->data.filemark.name), &cur_fm);
- cur->data.filemark.mark = cur_fm.fmark.mark;
- cur->data.filemark.additional_data = cur_fm.fmark.additional_data;
- cur->timestamp = cur_fm.fmark.timestamp;
- if (cur_fm.fmark.fnum == 0) {
- if (cur_fm.fname == NULL) {
- continue;
- }
- cur->data.filemark.fname = (char *) cur_fm.fname;
- } else {
- const buf_T *const buf = buflist_findnr(cur_fm.fmark.fnum);
- if (buf == NULL || buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) {
- continue;
- } else {
- cur->data.filemark.fname = (char *) buf->b_ffname;
- }
- }
- cur++;
- } while(iter != NULL);
- }
- cur->type = kSDItemMissing;
- return ret;
-}
-
-/// Return a list with all buffer marks set in some buffer
-///
-/// List is not sorted.
-///
-/// @warning Listed marks must be used before any buffer- or mark-editing
-/// function is run.
-///
-/// @param[in] buf Buffer for which marks are listed.
-///
-/// @return Array of ShadaEntry values, last one has type kSDItemMissing.
-///
-/// @warning Resulting ShadaEntry values must not be freed. Returned
-/// array must be freed with `xfree()`.
-static ShadaEntry *list_buffer_marks(const buf_T *const buf)
-{
- const char *const fname = (char *) buf->b_ffname;
- const void *iter = NULL;
- const size_t nummarks = mark_buffer_amount(buf);
- ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (nummarks + 1));
- ShadaEntry *cur = ret;
- if (nummarks) {
- do {
- cur->type = kSDItemLocalMark;
- fmark_T cur_fm;
- iter = mark_buffer_iter(iter, buf, &(cur->data.filemark.name), &cur_fm);
- cur->data.filemark.mark = cur_fm.mark;
- cur->data.filemark.fname = (char *) fname;
- cur->data.filemark.additional_data = cur_fm.additional_data;
- cur->timestamp = cur_fm.timestamp;
- if (cur->data.filemark.mark.lnum != 0) {
- cur++;
- }
- } while(iter != NULL);
- }
- cur->type = kSDItemMissing;
- return ret;
-}
-
/// Check whether "name" is on removable media (according to 'shada')
///
/// @param[in] name Checked name.
@@ -3414,52 +3845,6 @@ bool shada_removable(const char *name)
return retval;
}
-/// Return a list with all registers and their contents
-///
-/// List is not sorted.
-///
-/// @warning Listed registers must be used before any register-editing function
-/// is run.
-///
-/// @param[in] max_num_lines Maximum number of lines in the register. If it is
-/// zero then all registers are listed.
-///
-/// @return Array of ShadaEntry values, last one has type kSDItemMissing.
-///
-/// @warning Resulting ShadaEntry values must not be freed. Returned
-/// array must be freed with `xfree()`.
-static ShadaEntry *list_registers(const size_t max_num_lines)
-{
- const void *iter = NULL;
- const size_t numregs = op_register_amount();
- ShadaEntry *const ret = xmalloc(sizeof(ShadaEntry) * (numregs + 1));
- ShadaEntry *cur = ret;
- if (numregs) {
- do {
- cur->type = kSDItemRegister;
- yankreg_T cur_reg;
- iter = op_register_iter(iter, &(cur->data.reg.name), &cur_reg);
- if (max_num_lines && (size_t) cur_reg.y_size > max_num_lines) {
- continue;
- }
- cur->data.reg.contents = (char **) cur_reg.y_array;
- cur->data.reg.type = (uint8_t) cur_reg.y_type;
- cur->data.reg.contents_size = (size_t) cur_reg.y_size;
- if (cur_reg.y_type == MBLOCK) {
- cur->data.reg.width = (size_t) cur_reg.y_width;
- } else {
- cur->data.reg.width = 0;
- }
- cur->data.reg.additional_data = cur_reg.additional_data;
- cur->timestamp = cur_reg.timestamp;
- cur++;
- } while(iter != NULL);
- }
- cur->type = kSDItemMissing;
- return ret;
-}
-
-
#if 0
static int viminfo_errcnt;