Merge pull request #2506 from ZyX-I/shada

Replace viminfo with ShaDa files
This commit is contained in:
Justin M. Keyes
2015-10-16 01:54:07 -04:00
86 changed files with 10039 additions and 2527 deletions

View File

@@ -597,7 +597,7 @@ static void init_type_metadata(Dictionary *metadata)
}
/// Creates a deep clone of an object
static Object copy_object(Object obj)
Object copy_object(Object obj)
{
switch (obj.type) {
case kObjectTypeNil:

View File

@@ -69,6 +69,8 @@
#define ADD(array, item) \
kv_push(Object, array, item)
#define STATIC_CSTR_AS_STRING(s) ((String) {.data = s, .size = sizeof(s) - 1})
// Helpers used by the generated msgpack-rpc api wrappers
#define api_init_boolean
#define api_init_integer

View File

@@ -84,7 +84,7 @@ return {
'User', -- user defined autocommand
'VimEnter', -- after starting Vim
'VimLeave', -- before exiting Vim
'VimLeavePre', -- before exiting Vim and writing .viminfo
'VimLeavePre', -- before exiting Vim and writing ShaDa file
'VimResized', -- after Vim window was resized
'WinEnter', -- after entering a window
'WinLeave', -- before leaving a window

View File

@@ -73,6 +73,7 @@
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
#include "nvim/shada.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
@@ -555,9 +556,21 @@ static void free_buffer(buf_T *buf)
free_buffer_stuff(buf, TRUE);
unref_var_dict(buf->b_vars);
aubuflocal_remove(buf);
dict_unref(buf->additional_data);
clear_fmark(&buf->b_last_cursor);
clear_fmark(&buf->b_last_insert);
clear_fmark(&buf->b_last_change);
for (size_t i = 0; i < NMARKS; i++) {
free_fmark(buf->b_namedm[i]);
}
for (int i = 0; i < buf->b_changelistlen; i++) {
free_fmark(buf->b_changelist[i]);
}
if (autocmd_busy) {
// Do not free the buffer structure while autocommands are executing,
// it's still needed. Free it when autocmd_busy is reset.
memset(&buf->b_namedm[0], 0, sizeof(buf->b_namedm));
memset(&buf->b_changelist[0], 0, sizeof(buf->b_changelist));
buf->b_next = au_pending_free_buf;
au_pending_free_buf = buf;
} else {
@@ -1978,12 +1991,18 @@ buflist_nr2name (
fullname ? buf->b_ffname : buf->b_fname);
}
/*
* Set the "lnum" and "col" for the buffer "buf" and the current window.
* When "copy_options" is TRUE save the local window option values.
* When "lnum" is 0 only do the options.
*/
static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options)
/// Set the line and column numbers for the given buffer and window
///
/// @param[in,out] buf Buffer for which line and column are set.
/// @param[in,out] win Window for which line and column are set.
/// @param[in] lnum Line number to be set. If it is zero then only
/// options are touched.
/// @param[in] col Column number to be set.
/// @param[in] copy_options If true save the local window option values.
void buflist_setfpos(buf_T *const buf, win_T *const win,
linenr_T lnum, colnr_T col,
bool copy_options)
FUNC_ATTR_NONNULL_ALL
{
wininfo_T *wip;
@@ -4164,93 +4183,6 @@ chk_modeline (
return retval;
}
int read_viminfo_bufferlist(vir_T *virp, int writing)
{
char_u *tab;
linenr_T lnum;
colnr_T col;
buf_T *buf;
char_u *sfname;
char_u *xline;
/* Handle long line and escaped characters. */
xline = viminfo_readstring(virp, 1, FALSE);
/* don't read in if there are files on the command-line or if writing: */
if (xline != NULL && !writing && ARGCOUNT == 0
&& find_viminfo_parameter('%') != NULL) {
/* Format is: <fname> Tab <lnum> Tab <col>.
* Watch out for a Tab in the file name, work from the end. */
lnum = 0;
col = 0;
tab = vim_strrchr(xline, '\t');
if (tab != NULL) {
*tab++ = '\0';
col = (colnr_T)atoi((char *)tab);
tab = vim_strrchr(xline, '\t');
if (tab != NULL) {
*tab++ = '\0';
lnum = atol((char *)tab);
}
}
/* Expand "~/" in the file name at "line + 1" to a full path.
* Then try shortening it by comparing with the current directory */
expand_env(xline, NameBuff, MAXPATHL);
sfname = path_shorten_fname_if_possible(NameBuff);
buf = buflist_new(NameBuff, sfname, (linenr_T)0, BLN_LISTED);
if (buf != NULL) { /* just in case... */
buf->b_last_cursor.lnum = lnum;
buf->b_last_cursor.col = col;
buflist_setfpos(buf, curwin, lnum, col, FALSE);
}
}
xfree(xline);
return viminfo_readline(virp);
}
void write_viminfo_bufferlist(FILE *fp)
{
char_u *line;
int max_buffers;
if (find_viminfo_parameter('%') == NULL)
return;
/* Without a number -1 is returned: do all buffers. */
max_buffers = get_viminfo_parameter('%');
/* Allocate room for the file name, lnum and col. */
#define LINE_BUF_LEN (MAXPATHL + 40)
line = xmalloc(LINE_BUF_LEN);
FOR_ALL_TAB_WINDOWS(tp, win) {
set_last_cursor(win);
}
fputs(_("\n# Buffer list:\n"), fp);
FOR_ALL_BUFFERS(buf) {
if (buf->b_fname == NULL
|| !buf->b_p_bl
|| bt_quickfix(buf)
|| removable(buf->b_ffname))
continue;
if (max_buffers-- == 0)
break;
putc('%', fp);
home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE);
vim_snprintf_add((char *)line, LINE_BUF_LEN, "\t%" PRId64 "\t%d",
(int64_t)buf->b_last_cursor.lnum,
buf->b_last_cursor.col);
viminfo_writestring(fp, line);
}
xfree(line);
}
/*
* Return special buffer name.
* Returns NULL when the buffer has a normal file name.

View File

@@ -327,15 +327,6 @@ typedef struct {
bool vc_fail; /* fail for invalid char, don't use '?' */
} vimconv_T;
/*
* Structure used for reading from the viminfo file.
*/
typedef struct {
char_u *vir_line; /* text of the current line */
FILE *vir_fd; /* file descriptor */
vimconv_T vir_conv; /* encoding conversion */
} vir_T;
#define CONV_NONE 0
#define CONV_TO_UTF8 1
#define CONV_9_TO_UTF8 2
@@ -515,21 +506,21 @@ struct file_buffer {
uint64_t b_orig_size; /* size of original file in bytes */
int b_orig_mode; /* mode of original file */
pos_T b_namedm[NMARKS]; /* current named marks (mark.c) */
fmark_T b_namedm[NMARKS]; /* current named marks (mark.c) */
/* These variables are set when VIsual_active becomes FALSE */
visualinfo_T b_visual;
int b_visual_mode_eval; /* b_visual.vi_mode for visualmode() */
pos_T b_last_cursor; /* cursor position when last unloading this
buffer */
pos_T b_last_insert; /* where Insert mode was left */
pos_T b_last_change; /* position of last change: '. mark */
fmark_T b_last_cursor; // cursor position when last unloading this
// buffer
fmark_T b_last_insert; // where Insert mode was left
fmark_T b_last_change; // position of last change: '. mark
/*
* the changelist contains old change positions
*/
pos_T b_changelist[JUMPLISTSIZE];
fmark_T b_changelist[JUMPLISTSIZE];
int b_changelistlen; /* number of active entries */
bool b_new_change; /* set by u_savecommon() */
@@ -553,7 +544,7 @@ struct file_buffer {
pos_T b_op_start_orig; // used for Insstart_orig
pos_T b_op_end;
bool b_marks_read; /* Have we read viminfo marks yet? */
bool b_marks_read; /* Have we read ShaDa marks yet? */
/*
* The following only used in undo.c.
@@ -757,6 +748,8 @@ struct file_buffer {
signlist_T *b_signlist; /* list of signs to draw */
Terminal *terminal; // Terminal instance associated with the buffer
dict_T *additional_data; // Additional data from shada file if any.
};
/*

View File

@@ -60,6 +60,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/event/loop.h"
#include "nvim/mark.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
@@ -6991,8 +6992,9 @@ ins_esc (
curwin->w_set_curswant = TRUE;
/* Remember the last Insert position in the '^ mark. */
if (!cmdmod.keepjumps)
curbuf->b_last_insert = curwin->w_cursor;
if (!cmdmod.keepjumps) {
RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum);
}
/*
* The cursor should end up on the last inserted character.

View File

@@ -107,18 +107,6 @@
#define AUTOLOAD_CHAR '#' /* Character used as separator in autoload
function/variable names. */
/*
* In a hashtab item "hi_key" points to "di_key" in a dictitem.
* This avoids adding a pointer to the hashtab item.
* DI2HIKEY() converts a dictitem pointer to a hashitem key pointer.
* HIKEY2DI() converts a hashitem key pointer to a dictitem pointer.
* HI2DI() converts a hashitem pointer to a dictitem pointer.
*/
static dictitem_T dumdi;
#define DI2HIKEY(di) ((di)->di_key)
#define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi)))
#define HI2DI(hi) HIKEY2DI((hi)->hi_key)
/*
* Structure returned by get_lval() and used by set_var_lval().
* For a plain name:
@@ -354,7 +342,7 @@ typedef struct {
typedef enum {
VAR_FLAVOUR_DEFAULT, /* doesn't start with uppercase */
VAR_FLAVOUR_SESSION, /* starts with uppercase, some lower */
VAR_FLAVOUR_VIMINFO /* all uppercase */
VAR_FLAVOUR_SHADA /* all uppercase */
} var_flavour_T;
/* values for vv_flags: */
@@ -5352,7 +5340,7 @@ static int list_concat(list_T *l1, list_T *l2, typval_T *tv)
return FAIL;
/* make a copy of the first list. */
l = list_copy(l1, FALSE, 0);
l = list_copy(NULL, l1, false, 0);
if (l == NULL)
return FAIL;
tv->v_type = VAR_LIST;
@@ -5363,13 +5351,20 @@ static int list_concat(list_T *l1, list_T *l2, typval_T *tv)
return OK;
}
/*
* Make a copy of list "orig". Shallow if "deep" is FALSE.
* The refcount of the new list is set to 1.
* See item_copy() for "copyID".
* Returns NULL if orig is NULL or some failure happens.
*/
static list_T *list_copy(list_T *orig, int deep, int copyID)
/// Make a copy of list
///
/// @param[in] conv If non-NULL, then all internal strings will be converted.
/// @param[in] orig Original list to copy.
/// @param[in] deep If false, then shallow copy will be done.
/// @param[in] copyID See var_item_copy().
///
/// @return Copied list. May be NULL in case original list is NULL or some
/// failure happens. The refcount of the new list is set to 1.
static list_T *list_copy(const vimconv_T *const conv,
list_T *const orig,
const bool deep,
const int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
listitem_T *item;
listitem_T *ni;
@@ -5388,7 +5383,7 @@ static list_T *list_copy(list_T *orig, int deep, int copyID)
item = item->li_next) {
ni = listitem_alloc();
if (deep) {
if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL) {
if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) {
xfree(ni);
break;
}
@@ -5546,6 +5541,7 @@ static int list_join(garray_T *const gap, list_T *const l,
bool garbage_collect(void)
{
bool abort = false;
#define ABORTING(func) abort = abort || func
// Only do this once.
want_garbage_collect = false;
@@ -5564,45 +5560,117 @@ bool garbage_collect(void)
// referenced through previous_funccal. This must be first, because if
// the item is referenced elsewhere the funccal must not be freed.
for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
}
// script-local variables
for (int i = 1; i <= ga_scripts.ga_len; ++i) {
abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);
ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL);
}
// buffer-local variables
FOR_ALL_BUFFERS(buf) {
abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID, NULL, NULL);
// buffer-local variables
ABORTING(set_ref_in_item)(&buf->b_bufvar.di_tv, copyID, NULL, NULL);
// buffer marks (ShaDa additional data)
ABORTING(set_ref_in_fmark)(buf->b_last_cursor, copyID);
ABORTING(set_ref_in_fmark)(buf->b_last_insert, copyID);
ABORTING(set_ref_in_fmark)(buf->b_last_change, copyID);
for (size_t i = 0; i < NMARKS; i++) {
ABORTING(set_ref_in_fmark)(buf->b_namedm[i], copyID);
}
// buffer change list (ShaDa additional data)
for (int i = 0; i < buf->b_changelistlen; i++) {
ABORTING(set_ref_in_fmark)(buf->b_changelist[i], copyID);
}
// buffer ShaDa additional data
ABORTING(set_ref_dict)(buf->additional_data, copyID);
}
// window-local variables
FOR_ALL_TAB_WINDOWS(tp, wp) {
abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID, NULL, NULL);
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
// window jump list (ShaDa additional data)
for (int i = 0; i < wp->w_jumplistlen; i++) {
ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID);
}
}
if (aucmd_win != NULL) {
abort = abort ||
set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL);
ABORTING(set_ref_in_item)(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL);
}
// registers (ShaDa additional data)
{
const void *reg_iter = NULL;
do {
yankreg_T reg;
char name = NUL;
reg_iter = op_register_iter(reg_iter, &name, &reg);
if (name != NUL) {
ABORTING(set_ref_dict)(reg.additional_data, copyID);
}
} while (reg_iter != NULL);
}
// global marks (ShaDa additional data)
{
const void *mark_iter = NULL;
do {
xfmark_T fm;
char name = NUL;
mark_iter = mark_global_iter(mark_iter, &name, &fm);
if (name != NUL) {
ABORTING(set_ref_dict)(fm.fmark.additional_data, copyID);
}
} while (mark_iter != NULL);
}
// tabpage-local variables
FOR_ALL_TABS(tp) {
abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, NULL, NULL);
ABORTING(set_ref_in_item)(&tp->tp_winvar.di_tv, copyID, NULL, NULL);
}
// global variables
abort = abort || set_ref_in_ht(&globvarht, copyID, NULL);
ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL);
// function-local variables
for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID, NULL);
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
}
// v: vars
abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL);
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
// history items (ShaDa additional elements)
if (p_hi) {
for (uint8_t i = 0; i < HIST_COUNT; i++) {
const void *iter = NULL;
do {
histentry_T hist;
iter = hist_iter(iter, i, false, &hist);
if (hist.hisstr != NULL) {
ABORTING(set_ref_list)(hist.additional_elements, copyID);
}
} while (iter != NULL);
}
}
// previously used search/substitute patterns (ShaDa additional data)
{
SearchPattern pat;
get_search_pattern(&pat);
ABORTING(set_ref_dict)(pat.additional_data, copyID);
get_substitute_pattern(&pat);
ABORTING(set_ref_dict)(pat.additional_data, copyID);
}
// previously used replacement string
{
SubReplacementString sub;
sub_get_replacement(&sub);
ABORTING(set_ref_list)(sub.additional_elements, copyID);
}
bool did_free = false;
if (!abort) {
@@ -5631,6 +5699,7 @@ bool garbage_collect(void)
verb_msg((char_u *)_(
"Not enough memory to set references, garbage collection aborted!"));
}
#undef ABORTING
return did_free;
}
@@ -5690,6 +5759,7 @@ static int free_unref_items(int copyID)
///
/// @returns true if setting references failed somehow.
bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
FUNC_ATTR_WARN_UNUSED_RESULT
{
bool abort = false;
ht_stack_T *ht_stack = NULL;
@@ -5732,6 +5802,7 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
///
/// @returns true if setting references failed somehow.
bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)
FUNC_ATTR_WARN_UNUSED_RESULT
{
bool abort = false;
list_stack_T *list_stack = NULL;
@@ -5772,6 +5843,7 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)
/// @returns true if setting references failed somehow.
bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
list_stack_T **list_stack)
FUNC_ATTR_WARN_UNUSED_RESULT
{
bool abort = false;
@@ -5821,6 +5893,52 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
return abort;
}
/// Mark all lists and dicts referenced in given mark
///
/// @returns true if setting references failed somehow.
static inline bool set_ref_in_fmark(fmark_T fm, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (fm.additional_data != NULL
&& fm.additional_data->dv_copyID != copyID) {
fm.additional_data->dv_copyID = copyID;
return set_ref_in_ht(&fm.additional_data->dv_hashtab, copyID, NULL);
}
return false;
}
/// Mark all lists and dicts referenced in given list and the list itself
///
/// @returns true if setting references failed somehow.
static inline bool set_ref_list(list_T *list, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (list != NULL) {
typval_T tv = (typval_T) {
.v_type = VAR_LIST,
.vval = { .v_list = list }
};
return set_ref_in_item(&tv, copyID, NULL, NULL);
}
return false;
}
/// Mark all lists and dicts referenced in given dict and the dict itself
///
/// @returns true if setting references failed somehow.
static inline bool set_ref_dict(dict_T *dict, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (dict != NULL) {
typval_T tv = (typval_T) {
.v_type = VAR_DICT,
.vval = { .v_dict = dict }
};
return set_ref_in_item(&tv, copyID, NULL, NULL);
}
return false;
}
/*
* Allocate an empty header for a dictionary.
*/
@@ -5964,13 +6082,20 @@ void dictitem_free(dictitem_T *item)
xfree(item);
}
/*
* Make a copy of dict "d". Shallow if "deep" is FALSE.
* The refcount of the new dict is set to 1.
* See item_copy() for "copyID".
* Returns NULL if orig is NULL or some other failure.
*/
static dict_T *dict_copy(dict_T *orig, int deep, int copyID)
/// Make a copy of dictionary
///
/// @param[in] conv If non-NULL, then all internal strings will be converted.
/// @param[in] orig Original dictionary to copy.
/// @param[in] deep If false, then shallow copy will be done.
/// @param[in] copyID See var_item_copy().
///
/// @return Copied dictionary. May be NULL in case original dictionary is NULL
/// or some failure happens. The refcount of the new dictionary is set
/// to 1.
static dict_T *dict_copy(const vimconv_T *const conv,
dict_T *const orig,
const bool deep,
const int copyID)
{
dictitem_T *di;
int todo;
@@ -5990,10 +6115,21 @@ static dict_T *dict_copy(dict_T *orig, int deep, int copyID)
if (!HASHITEM_EMPTY(hi)) {
--todo;
di = dictitem_alloc(hi->hi_key);
if (conv == NULL || conv->vc_type == CONV_NONE) {
di = dictitem_alloc(hi->hi_key);
} else {
char *const key = (char *) string_convert((vimconv_T *) conv,
hi->hi_key, NULL);
if (key == NULL) {
di = dictitem_alloc(hi->hi_key);
} else {
di = dictitem_alloc((char_u *) key);
xfree(key);
}
}
if (deep) {
if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
copyID) == FAIL) {
if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep,
copyID) == FAIL) {
xfree(di);
break;
}
@@ -6305,7 +6441,7 @@ failret:
/// the results.
/// @param firstargname Name of the first argument.
/// @param name Name of the target converter.
#define DEFINE_VIML_CONV_FUNCTIONS(name, firstargtype, firstargname) \
#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
static int name##_convert_one_value(firstargtype firstargname, \
MPConvStack *const mpstack, \
typval_T *const tv, \
@@ -6543,7 +6679,7 @@ name##_convert_one_value_regular_dict: \
return OK; \
} \
\
static int vim_to_##name(firstargtype firstargname, typval_T *const tv) \
scope int vim_to_##name(firstargtype firstargname, typval_T *const tv) \
FUNC_ATTR_WARN_UNUSED_RESULT \
{ \
current_copyID += COPYID_INC; \
@@ -6739,7 +6875,7 @@ vim_to_msgpack_error_ret: \
#define CONV_ALLOW_SPECIAL false
DEFINE_VIML_CONV_FUNCTIONS(string, garray_T *const, gap)
DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
#undef CONV_RECURSE
#define CONV_RECURSE(val, conv_type) \
@@ -6769,7 +6905,7 @@ DEFINE_VIML_CONV_FUNCTIONS(string, garray_T *const, gap)
return OK; \
} while (0)
DEFINE_VIML_CONV_FUNCTIONS(echo, garray_T *const, gap)
DEFINE_VIML_CONV_FUNCTIONS(static, echo, garray_T *const, gap)
#undef CONV_STRING
#undef CONV_STR_STRING
@@ -8344,7 +8480,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv)
*/
static void f_copy(typval_T *argvars, typval_T *rettv)
{
item_copy(&argvars[0], rettv, FALSE, 0);
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
/*
@@ -8513,7 +8649,9 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv)
EMSG(_(e_invarg));
else {
current_copyID += COPYID_INC;
item_copy(&argvars[0], rettv, TRUE, noref == 0 ? current_copyID : 0);
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? current_copyID
: 0));
}
}
@@ -10482,6 +10620,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"scrollbind",
"showcmd",
"cmdline_info",
"shada",
"signs",
"smartindent",
"startuptime",
@@ -10498,7 +10637,6 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"title",
"user-commands", /* was accidentally included in 5.4 */
"user_commands",
"viminfo",
"vertsplit",
"virtualedit",
"visual",
@@ -12486,7 +12624,7 @@ static inline bool vim_list_to_buf(const list_T *const list,
#define CONV_ALLOW_SPECIAL true
DEFINE_VIML_CONV_FUNCTIONS(msgpack, msgpack_packer *const, packer)
DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
#undef CONV_STRING
#undef CONV_STR_STRING
@@ -12592,7 +12730,7 @@ static inline ListReaderState init_lrstate(const list_T *const list)
}
/// Convert msgpack object to a VimL one
static int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
#define INIT_SPECIAL_DICT(tv, type, val) \
@@ -17353,14 +17491,14 @@ char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
* Get List v: variable value. Caller must take care of reference count when
* needed.
*/
list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_list;
}
/// Get Dictionary v: variable value. Caller must take care of reference count
/// when needed.
dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_dict;
}
@@ -18446,14 +18584,28 @@ void copy_tv(typval_T *from, typval_T *to)
}
}
/*
* Make a copy of an item.
* Lists and Dictionaries are also copied. A deep copy if "deep" is set.
* For deepcopy() "copyID" is zero for a full copy or the ID for when a
* reference to an already copied list/dict can be used.
* Returns FAIL or OK.
*/
static int item_copy(typval_T *from, typval_T *to, int deep, int copyID)
/// Make a copy of an item
///
/// Lists and Dictionaries are also copied.
///
/// @param[in] conv If not NULL, convert all copied strings.
/// @param[in] from Value to copy.
/// @param[out] to Location where to copy to.
/// @param[in] deep If true, use copy the container and all of the contained
/// containers (nested).
/// @param[in] copyID If non-zero then when container is referenced more then
/// once then copy of it that was already done is used. E.g.
/// when copying list `list = [list2, list2]` (`list[0] is
/// list[1]`) var_item_copy with zero copyID will emit
/// a copy with (`copy[0] isnot copy[1]`), with non-zero it
/// will emit a copy with (`copy[0] is copy[1]`) like in the
/// original list. Not use when deep is false.
int var_item_copy(const vimconv_T *const conv,
typval_T *const from,
typval_T *const to,
const bool deep,
const int copyID)
FUNC_ATTR_NONNULL_ARG(2, 3)
{
static int recurse = 0;
int ret = OK;
@@ -18467,10 +18619,23 @@ static int item_copy(typval_T *from, typval_T *to, int deep, int copyID)
switch (from->v_type) {
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_STRING:
case VAR_FUNC:
copy_tv(from, to);
break;
case VAR_STRING:
if (conv == NULL || conv->vc_type == CONV_NONE) {
copy_tv(from, to);
} else {
to->v_type = VAR_STRING;
to->v_lock = 0;
if ((to->vval.v_string = string_convert((vimconv_T *)conv,
from->vval.v_string,
NULL))
== NULL) {
to->vval.v_string = (char_u *) xstrdup((char *) from->vval.v_string);
}
}
break;
case VAR_LIST:
to->v_type = VAR_LIST;
to->v_lock = 0;
@@ -18480,8 +18645,9 @@ static int item_copy(typval_T *from, typval_T *to, int deep, int copyID)
/* use the copy made earlier */
to->vval.v_list = from->vval.v_list->lv_copylist;
++to->vval.v_list->lv_refcount;
} else
to->vval.v_list = list_copy(from->vval.v_list, deep, copyID);
} else {
to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID);
}
if (to->vval.v_list == NULL)
ret = FAIL;
break;
@@ -18494,13 +18660,14 @@ static int item_copy(typval_T *from, typval_T *to, int deep, int copyID)
/* use the copy made earlier */
to->vval.v_dict = from->vval.v_dict->dv_copydict;
++to->vval.v_dict->dv_refcount;
} else
to->vval.v_dict = dict_copy(from->vval.v_dict, deep, copyID);
} else {
to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID);
}
if (to->vval.v_dict == NULL)
ret = FAIL;
break;
default:
EMSG2(_(e_intern2), "item_copy()");
EMSG2(_(e_intern2), "var_item_copy()");
ret = FAIL;
}
--recurse;
@@ -20710,109 +20877,64 @@ static var_flavour_T var_flavour(char_u *varname)
if (ASCII_ISUPPER(*p)) {
while (*(++p))
if (ASCII_ISLOWER(*p))
if (ASCII_ISLOWER(*p)) {
return VAR_FLAVOUR_SESSION;
return VAR_FLAVOUR_VIMINFO;
} else
}
return VAR_FLAVOUR_SHADA;
} else {
return VAR_FLAVOUR_DEFAULT;
}
}
/*
* Restore global vars that start with a capital from the viminfo file
*/
int read_viminfo_varlist(vir_T *virp, int writing)
/// Iterate over global variables
///
/// @warning No modifications to global variable dictionary must be performed
/// while iteration is in progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
/// @param[out] name Variable name.
/// @param[out] rettv Variable value.
///
/// @return Pointer that needs to be passed to next `var_shada_iter` invocation
/// or NULL to indicate that iteration is over.
const void *var_shada_iter(const void *const iter, const char **const name,
typval_T *rettv)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3)
{
char_u *tab;
int type = VAR_NUMBER;
typval_T tv;
if (!writing && (find_viminfo_parameter('!') != NULL)) {
tab = vim_strchr(virp->vir_line + 1, '\t');
if (tab != NULL) {
*tab++ = NUL; /* isolate the variable name */
switch (*tab) {
case 'S': type = VAR_STRING; break;
case 'F': type = VAR_FLOAT; break;
case 'D': type = VAR_DICT; break;
case 'L': type = VAR_LIST; break;
}
tab = vim_strchr(tab, '\t');
if (tab != NULL) {
tv.v_type = type;
if (type == VAR_STRING || type == VAR_DICT || type == VAR_LIST)
tv.vval.v_string = viminfo_readstring(virp,
(int)(tab - virp->vir_line + 1), TRUE);
else if (type == VAR_FLOAT)
(void)string2float(tab + 1, &tv.vval.v_float);
else
tv.vval.v_number = atol((char *)tab + 1);
if (type == VAR_DICT || type == VAR_LIST) {
typval_T *etv = eval_expr(tv.vval.v_string, NULL);
if (etv == NULL)
/* Failed to parse back the dict or list, use it as a
* string. */
tv.v_type = VAR_STRING;
else {
xfree(tv.vval.v_string);
tv = *etv;
xfree(etv);
}
}
set_var(virp->vir_line + 1, &tv, FALSE);
if (tv.v_type == VAR_STRING)
xfree(tv.vval.v_string);
else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST)
clear_tv(&tv);
}
const hashitem_T *hi;
const hashitem_T *hifirst = globvarht.ht_array;
const size_t hinum = (size_t) globvarht.ht_mask + 1;
*name = NULL;
if (iter == NULL) {
hi = globvarht.ht_array;
while ((size_t) (hi - hifirst) < hinum
&& (HASHITEM_EMPTY(hi)
|| var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) {
hi++;
}
if ((size_t) (hi - hifirst) == hinum) {
return NULL;
}
} else {
hi = (const hashitem_T *) iter;
}
*name = (char *) HI2DI(hi)->di_key;
copy_tv(&(HI2DI(hi)->di_tv), rettv);
while ((size_t) (++hi - hifirst) < hinum) {
if (!HASHITEM_EMPTY(hi)
&& var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) {
return hi;
}
}
return viminfo_readline(virp);
return NULL;
}
/*
* Write global vars that start with a capital to the viminfo file
*/
void write_viminfo_varlist(FILE *fp)
void var_set_global(const char *const name, typval_T vartv)
{
hashitem_T *hi;
dictitem_T *this_var;
int todo;
char *s;
char_u *p;
if (find_viminfo_parameter('!') == NULL)
return;
fputs(_("\n# global variables:\n"), fp);
todo = (int)globvarht.ht_used;
for (hi = globvarht.ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
--todo;
this_var = HI2DI(hi);
if (var_flavour(this_var->di_key) == VAR_FLAVOUR_VIMINFO) {
switch (this_var->di_tv.v_type) {
case VAR_STRING: s = "STR"; break;
case VAR_NUMBER: s = "NUM"; break;
case VAR_FLOAT: s = "FLO"; break;
case VAR_DICT: s = "DIC"; break;
case VAR_LIST: s = "LIS"; break;
default: continue;
}
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
p = (char_u *) echo_string(&this_var->di_tv, NULL);
if (p != NULL) {
viminfo_writestring(fp, p);
}
xfree(p);
}
}
}
funccall_T *const saved_current_funccal = current_funccal;
current_funccal = NULL;
set_var((char_u *) name, &vartv, false);
current_funccal = saved_current_funccal;
}
int store_session_globals(FILE *fd)

View File

@@ -1,6 +1,8 @@
#ifndef NVIM_EVAL_H
#define NVIM_EVAL_H
#include <msgpack.h>
#include "nvim/profile.h"
/* Defines for Vim variables. These must match vimvars[] in eval.c! */
@@ -72,6 +74,8 @@ enum {
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
int vim_to_msgpack(msgpack_packer *const, typval_T *const);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
#endif

View File

@@ -2,6 +2,7 @@
#define NVIM_EVAL_DEFS_H
#include <limits.h>
#include <stddef.h>
#include "nvim/hashtab.h"
@@ -132,4 +133,16 @@ typedef struct list_stack_S {
struct list_stack_S *prev;
} list_stack_T;
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
/// Convert a dictitem pointer to a hashitem key pointer
#define DI2HIKEY(di) ((di)->di_key)
/// Convert a hashitem key pointer to a dictitem pointer
#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key)))
/// Convert a hashitem pointer to a dictitem pointer
#define HI2DI(hi) HIKEY2DI((hi)->hi_key)
#endif // NVIM_EVAL_DEFS_H

View File

@@ -67,6 +67,7 @@
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
/*
* Struct to hold the sign properties.
@@ -1391,550 +1392,6 @@ void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname)
(char *)opt, (char *)fname);
}
static int viminfo_errcnt;
static int no_viminfo(void)
{
/* "vim -i NONE" does not read or write a viminfo file */
return use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0;
}
/*
* Report an error for reading a viminfo file.
* Count the number of errors. When there are more than 10, return TRUE.
*/
int viminfo_error(char *errnum, char *message, char_u *line)
{
vim_snprintf((char *)IObuff, IOSIZE, _("%sviminfo: %s in line: "),
errnum, message);
STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff) - 1);
if (IObuff[STRLEN(IObuff) - 1] == '\n')
IObuff[STRLEN(IObuff) - 1] = NUL;
emsg(IObuff);
if (++viminfo_errcnt >= 10) {
EMSG(_("E136: viminfo: Too many errors, skipping rest of file"));
return TRUE;
}
return FALSE;
}
/*
* read_viminfo() -- Read the viminfo file. Registers etc. which are already
* set are not over-written unless "flags" includes VIF_FORCEIT. -- webb
*/
int
read_viminfo (
char_u *file, /* file name or NULL to use default name */
int flags /* VIF_WANT_INFO et al. */
)
{
FILE *fp;
char_u *fname;
if (no_viminfo())
return FAIL;
fname = viminfo_filename(file); /* get file name in allocated buffer */
fp = mch_fopen((char *)fname, READBIN);
if (p_verbose > 0) {
verbose_enter();
smsg(_("Reading viminfo file \"%s\"%s%s%s"),
fname,
(flags & VIF_WANT_INFO) ? _(" info") : "",
(flags & VIF_WANT_MARKS) ? _(" marks") : "",
(flags & VIF_GET_OLDFILES) ? _(" oldfiles") : "",
fp == NULL ? _(" FAILED") : "");
verbose_leave();
}
xfree(fname);
if (fp == NULL)
return FAIL;
viminfo_errcnt = 0;
do_viminfo(fp, NULL, flags);
fclose(fp);
return OK;
}
/*
* Write the viminfo file. The old one is read in first so that effectively a
* merge of current info and old info is done. This allows multiple vims to
* run simultaneously, without losing any marks etc.
* If "forceit" is TRUE, then the old file is not read in, and only internal
* info is written to the file.
*/
void write_viminfo(char_u *file, int forceit)
{
char_u *fname;
FILE *fp_in = NULL; /* input viminfo file, if any */
FILE *fp_out = NULL; /* output viminfo file */
char_u *tempname = NULL; /* name of temp viminfo file */
char_u *wp;
#if defined(UNIX)
mode_t umask_save;
#endif
if (no_viminfo())
return;
fname = viminfo_filename(file); /* may set to default if NULL */
fp_in = mch_fopen((char *)fname, READBIN);
if (fp_in == NULL) {
/* if it does exist, but we can't read it, don't try writing */
if (os_file_exists(fname))
goto end;
#if defined(UNIX)
/*
* For Unix we create the .viminfo non-accessible for others,
* because it may contain text from non-accessible documents.
*/
umask_save = umask(077);
#endif
fp_out = mch_fopen((char *)fname, WRITEBIN);
#if defined(UNIX)
(void)umask(umask_save);
#endif
} else {
/*
* There is an existing viminfo file. Create a temporary file to
* write the new viminfo into, in the same directory as the
* existing viminfo file, which will be renamed later.
*/
#ifdef UNIX
/*
* For Unix we check the owner of the file. It's not very nice to
* overwrite a user's viminfo file after a "su root", with a
* viminfo file that the user can't read.
*/
FileInfo old_info; // FileInfo of existing viminfo file
if (os_fileinfo((char *)fname, &old_info)
&& getuid() != ROOT_UID
&& !(old_info.stat.st_uid == getuid()
? (old_info.stat.st_mode & 0200)
: (old_info.stat.st_gid == getgid()
? (old_info.stat.st_mode & 0020)
: (old_info.stat.st_mode & 0002)))) {
int tt = msg_didany;
/* avoid a wait_return for this message, it's annoying */
EMSG2(_("E137: Viminfo file is not writable: %s"), fname);
msg_didany = tt;
fclose(fp_in);
goto end;
}
#endif
// Make tempname
tempname = (char_u *)modname((char *)fname, ".tmp", FALSE);
if (tempname != NULL) {
/*
* Check if tempfile already exists. Never overwrite an
* existing file!
*/
if (os_file_exists(tempname)) {
/*
* Try another name. Change one character, just before
* the extension.
*/
wp = tempname + STRLEN(tempname) - 5;
if (wp < path_tail(tempname)) /* empty file name? */
wp = path_tail(tempname);
for (*wp = 'z'; os_file_exists(tempname); --*wp) {
/*
* They all exist? Must be something wrong! Don't
* write the viminfo file then.
*/
if (*wp == 'a') {
xfree(tempname);
tempname = NULL;
break;
}
}
}
}
if (tempname != NULL) {
int fd;
/* Use os_open() to be able to use O_NOFOLLOW and set file
* protection:
* Unix: same as original file, but strip s-bit. Reset umask to
* avoid it getting in the way.
* Others: r&w for user only. */
# ifdef UNIX
umask_save = umask(0);
fd = os_open((char *)tempname,
O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW,
(int)((old_info.stat.st_mode & 0777) | 0600));
(void)umask(umask_save);
# else
fd = os_open((char *)tempname,
O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
# endif
if (fd < 0)
fp_out = NULL;
else
fp_out = fdopen(fd, WRITEBIN);
/*
* If we can't create in the same directory, try creating a
* "normal" temp file.
*/
if (fp_out == NULL) {
xfree(tempname);
if ((tempname = vim_tempname()) != NULL)
fp_out = mch_fopen((char *)tempname, WRITEBIN);
}
#ifdef UNIX
/*
* Make sure the owner can read/write it. This only works for
* root.
*/
if (fp_out != NULL) {
os_fchown(fileno(fp_out), old_info.stat.st_uid, old_info.stat.st_gid);
}
#endif
}
}
/*
* Check if the new viminfo file can be written to.
*/
if (fp_out == NULL) {
EMSG2(_("E138: Can't write viminfo file %s!"),
(fp_in == NULL || tempname == NULL) ? fname : tempname);
if (fp_in != NULL)
fclose(fp_in);
goto end;
}
if (p_verbose > 0) {
verbose_enter();
smsg(_("Writing viminfo file \"%s\""), fname);
verbose_leave();
}
viminfo_errcnt = 0;
do_viminfo(fp_in, fp_out, forceit ? 0 : (VIF_WANT_INFO | VIF_WANT_MARKS));
fclose(fp_out); /* errors are ignored !? */
if (fp_in != NULL) {
fclose(fp_in);
/* In case of an error keep the original viminfo file. Otherwise
* rename the newly written file. Give an error if that fails. */
if (viminfo_errcnt == 0 && vim_rename(tempname, fname) == -1) {
viminfo_errcnt++;
EMSG2(_("E886: Can't rename viminfo file to %s!"), fname);
}
if (viminfo_errcnt > 0) {
os_remove((char *)tempname);
}
}
end:
xfree(fname);
xfree(tempname);
}
/*
* Get the viminfo file name to use.
* If "file" is given and not empty, use it (has already been expanded by
* cmdline functions).
* Otherwise use "-i file_name", value from 'viminfo' or the default, and
* expand environment variables.
* Returns an allocated string.
*/
static char_u *viminfo_filename(char_u *file)
{
if (file == NULL || *file == NUL) {
if (use_viminfo != NULL)
file = use_viminfo;
else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL) {
#ifdef VIMINFO_FILE2
// don't use $HOME when not defined (turned into "c:/"!).
if (!os_env_exists("HOME")) {
// don't use $VIM when not available.
expand_env((char_u *)"$VIM", NameBuff, MAXPATHL);
if (STRCMP("$VIM", NameBuff) != 0) /* $VIM was expanded */
file = (char_u *)VIMINFO_FILE2;
else
file = (char_u *)VIMINFO_FILE;
} else
#endif
file = (char_u *)VIMINFO_FILE;
}
expand_env(file, NameBuff, MAXPATHL);
file = NameBuff;
}
return vim_strsave(file);
}
/*
* do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
*/
static void do_viminfo(FILE *fp_in, FILE *fp_out, int flags)
{
int count = 0;
int eof = FALSE;
vir_T vir;
int merge = FALSE;
vir.vir_line = xmalloc(LSIZE);
vir.vir_fd = fp_in;
vir.vir_conv.vc_type = CONV_NONE;
if (fp_in != NULL) {
if (flags & VIF_WANT_INFO) {
eof = read_viminfo_up_to_marks(&vir,
flags & VIF_FORCEIT, fp_out != NULL);
merge = TRUE;
} else if (flags != 0)
/* Skip info, find start of marks */
while (!(eof = viminfo_readline(&vir))
&& vir.vir_line[0] != '>')
;
}
if (fp_out != NULL) {
/* Write the info: */
fprintf(fp_out, _("# This viminfo file was generated by Nvim %s.\n"),
mediumVersion);
fputs(_("# You may edit it if you're careful!\n\n"), fp_out);
fputs(_("# Value of 'encoding' when this file was written\n"), fp_out);
fprintf(fp_out, "*encoding=%s\n\n", p_enc);
write_viminfo_search_pattern(fp_out);
write_viminfo_sub_string(fp_out);
write_viminfo_history(fp_out, merge);
write_viminfo_registers(fp_out);
write_viminfo_varlist(fp_out);
write_viminfo_filemarks(fp_out);
write_viminfo_bufferlist(fp_out);
count = write_viminfo_marks(fp_out);
}
if (fp_in != NULL
&& (flags & (VIF_WANT_MARKS | VIF_GET_OLDFILES | VIF_FORCEIT)))
copy_viminfo_marks(&vir, fp_out, count, eof, flags);
xfree(vir.vir_line);
if (vir.vir_conv.vc_type != CONV_NONE)
convert_setup(&vir.vir_conv, NULL, NULL);
}
/*
* read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the
* first part of the viminfo file which contains everything but the marks that
* are local to a file. Returns TRUE when end-of-file is reached. -- webb
*/
static int read_viminfo_up_to_marks(vir_T *virp, int forceit, int writing)
{
int eof;
prepare_viminfo_history(forceit ? 9999 : 0, writing);
eof = viminfo_readline(virp);
while (!eof && virp->vir_line[0] != '>') {
switch (virp->vir_line[0]) {
/* Characters reserved for future expansion, ignored now */
case '+': /* "+40 /path/dir file", for running vim without args */
case '|': /* to be defined */
case '^': /* to be defined */
case '<': /* long line - ignored */
/* A comment or empty line. */
case NUL:
case '\r':
case '\n':
case '#':
eof = viminfo_readline(virp);
break;
case '*': /* "*encoding=value" */
eof = viminfo_encoding(virp);
break;
case '!': /* global variable */
eof = read_viminfo_varlist(virp, writing);
break;
case '%': /* entry for buffer list */
eof = read_viminfo_bufferlist(virp, writing);
break;
case '"':
eof = read_viminfo_register(virp, forceit);
break;
case '/': /* Search string */
case '&': /* Substitute search string */
case '~': /* Last search string, followed by '/' or '&' */
eof = read_viminfo_search_pattern(virp, forceit);
break;
case '$':
eof = read_viminfo_sub_string(virp, forceit);
break;
case ':':
case '?':
case '=':
case '@':
eof = read_viminfo_history(virp, writing);
break;
case '-':
case '\'':
eof = read_viminfo_filemark(virp, forceit);
break;
default:
if (viminfo_error("E575: ", _("Illegal starting char"),
virp->vir_line))
eof = TRUE;
else
eof = viminfo_readline(virp);
break;
}
}
/* Finish reading history items. */
if (!writing)
finish_viminfo_history();
/* Change file names to buffer numbers for fmarks. */
FOR_ALL_BUFFERS(buf) {
fmarks_check_names(buf);
}
return eof;
}
/*
* Compare the 'encoding' value in the viminfo file with the current value of
* 'encoding'. If different and the 'c' flag is in 'viminfo', setup for
* conversion of text with iconv() in viminfo_readstring().
*/
static int viminfo_encoding(vir_T *virp)
{
char_u *p;
int i;
if (get_viminfo_parameter('c') != 0) {
p = vim_strchr(virp->vir_line, '=');
if (p != NULL) {
/* remove trailing newline */
++p;
for (i = 0; vim_isprintc(p[i]); ++i)
;
p[i] = NUL;
convert_setup(&virp->vir_conv, p, p_enc);
}
}
return viminfo_readline(virp);
}
/*
* Read a line from the viminfo file.
* Returns TRUE for end-of-file;
*/
int viminfo_readline(vir_T *virp)
{
return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
}
/*
* check string read from viminfo file
* remove '\n' at the end of the line
* - replace CTRL-V CTRL-V with CTRL-V
* - replace CTRL-V 'n' with '\n'
*
* Check for a long line as written by viminfo_writestring().
*
* Return the string in allocated memory.
*/
char_u *
viminfo_readstring (
vir_T *virp,
int off, /* offset for virp->vir_line */
int convert /* convert the string */
)
FUNC_ATTR_NONNULL_RET
{
char_u *retval;
char_u *s, *d;
if (virp->vir_line[off] == Ctrl_V && ascii_isdigit(virp->vir_line[off + 1])) {
ssize_t len = atol((char *)virp->vir_line + off + 1);
retval = xmalloc(len);
// TODO(philix): change type of vim_fgets() size argument to size_t
(void)vim_fgets(retval, (int)len, virp->vir_fd);
s = retval + 1; /* Skip the leading '<' */
} else {
retval = vim_strsave(virp->vir_line + off);
s = retval;
}
/* Change CTRL-V CTRL-V to CTRL-V and CTRL-V n to \n in-place. */
d = retval;
while (*s != NUL && *s != '\n') {
if (s[0] == Ctrl_V && s[1] != NUL) {
if (s[1] == 'n')
*d++ = '\n';
else
*d++ = Ctrl_V;
s += 2;
} else
*d++ = *s++;
}
*d = NUL;
if (convert && virp->vir_conv.vc_type != CONV_NONE && *retval != NUL) {
d = string_convert(&virp->vir_conv, retval, NULL);
if (d != NULL) {
xfree(retval);
retval = d;
}
}
return retval;
}
/*
* write string to viminfo file
* - replace CTRL-V with CTRL-V CTRL-V
* - replace '\n' with CTRL-V 'n'
* - add a '\n' at the end
*
* For a long line:
* - write " CTRL-V <length> \n " in first line
* - write " < <string> \n " in second line
*/
void viminfo_writestring(FILE *fd, char_u *p)
{
int c;
char_u *s;
int len = 0;
for (s = p; *s != NUL; ++s) {
if (*s == Ctrl_V || *s == '\n')
++len;
++len;
}
/* If the string will be too long, write its length and put it in the next
* line. Take into account that some room is needed for what comes before
* the string (e.g., variable name). Add something to the length for the
* '<', NL and trailing NUL. */
if (len > LSIZE / 2)
fprintf(fd, "\026%d\n<", len + 3);
while ((c = *p++) != NUL) {
if (c == Ctrl_V || c == '\n') {
putc(Ctrl_V, fd);
if (c == '\n')
c = 'n';
}
putc(c, fd);
}
putc('\n', fd);
}
void print_line_no_prefix(linenr_T lnum, int use_number, int list)
{
char_u numbuf[30];
@@ -3364,8 +2821,33 @@ int check_secure(void)
return FALSE;
}
static char_u *old_sub = NULL; /* previous substitute pattern */
static int global_need_beginline; /* call beginline() after ":g" */
/// Previous substitute replacement string
static SubReplacementString old_sub = {NULL, 0, NULL};
static int global_need_beginline; // call beginline() after ":g"
/// Get old substitute replacement string
///
/// @param[out] ret_sub Location where old string will be saved.
void sub_get_replacement(SubReplacementString *const ret_sub)
FUNC_ATTR_NONNULL_ALL
{
*ret_sub = old_sub;
}
/// Set substitute string and timestamp
///
/// @warning `sub` must be in allocated memory. It is not copied.
///
/// @param[in] sub New replacement string.
void sub_set_replacement(SubReplacementString sub)
{
xfree(old_sub.sub);
if (sub.additional_elements != old_sub.additional_elements) {
list_unref(old_sub.additional_elements);
}
old_sub = sub;
}
/* do_sub()
*
@@ -3473,16 +2955,19 @@ void do_sub(exarg_T *eap)
}
if (!eap->skip) {
xfree(old_sub);
old_sub = vim_strsave(sub);
sub_set_replacement((SubReplacementString) {
.sub = xstrdup((char *) sub),
.timestamp = os_time(),
.additional_elements = NULL,
});
}
} else if (!eap->skip) { /* use previous pattern and substitution */
if (old_sub == NULL) { /* there is no previous command */
if (old_sub.sub == NULL) { /* there is no previous command */
EMSG(_(e_nopresub));
return;
}
pat = NULL; /* search_regcomp() will use previous pattern */
sub = old_sub;
sub = (char_u *) old_sub.sub;
/* Vi compatibility quirk: repeating with ":s" keeps the cursor in the
* last column after using "$". */
@@ -4501,27 +3986,10 @@ void global_exe(char_u *cmd)
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
}
int read_viminfo_sub_string(vir_T *virp, int force)
{
if (force)
xfree(old_sub);
if (force || old_sub == NULL)
old_sub = viminfo_readstring(virp, 1, TRUE);
return viminfo_readline(virp);
}
void write_viminfo_sub_string(FILE *fp)
{
if (get_viminfo_parameter('/') != 0 && old_sub != NULL) {
fputs(_("\n# Last Substitute String:\n$"), fp);
viminfo_writestring(fp, old_sub);
}
}
#if defined(EXITFREE)
void free_old_sub(void)
{
xfree(old_sub);
sub_set_replacement((SubReplacementString) {NULL, 0, NULL});
}
#endif

View File

@@ -3,6 +3,9 @@
#include <stdbool.h>
#include "nvim/os/time.h"
#include "nvim/eval_defs.h"
/* flags for do_ecmd() */
#define ECMD_HIDE 0x01 /* don't free the current buffer */
#define ECMD_SET_HELP 0x02 /* set b_help flag of (new) buffer before
@@ -16,11 +19,12 @@
#define ECMD_LAST (linenr_T)-1 /* use last position in all files */
#define ECMD_ONE (linenr_T)1 /* use first line */
/* flags for read_viminfo() and children */
#define VIF_WANT_INFO 1 /* load non-mark info */
#define VIF_WANT_MARKS 2 /* load file marks */
#define VIF_FORCEIT 4 /* overwrite info already read */
#define VIF_GET_OLDFILES 8 /* load v:oldfiles */
/// Previous :substitute replacement string definition
typedef struct {
char *sub; ///< Previous replacement string.
Timestamp timestamp; ///< Time when it was last set.
list_T *additional_elements; ///< Additional data left from ShaDa file.
} SubReplacementString;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds.h.generated.h"

View File

@@ -2119,6 +2119,12 @@ return {
addr_type=ADDR_LINES,
func='ex_wrongmodifier',
},
{
command='rshada',
flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
func='ex_shada',
},
{
command='runtime',
flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN),
@@ -2153,7 +2159,7 @@ return {
command='rviminfo',
flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
func='ex_viminfo',
func='ex_shada',
},
{
command='substitute',
@@ -3031,6 +3037,12 @@ return {
addr_type=ADDR_LINES,
func='ex_wsverb',
},
{
command='wshada',
flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
func='ex_shada',
},
{
command='wundo',
flags=bit.bor(BANG, NEEDARG, FILE1),
@@ -3041,7 +3053,7 @@ return {
command='wviminfo',
flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
func='ex_viminfo',
func='ex_shada',
},
{
command='xit',

View File

@@ -75,6 +75,7 @@
#include "nvim/mouse.h"
#include "nvim/event/rstream.h"
#include "nvim/event/wstream.h"
#include "nvim/shada.h"
static int quitmore = 0;
static int ex_pressedreturn = FALSE;
@@ -9139,22 +9140,21 @@ int put_line(FILE *fd, char *s)
}
/*
* ":rviminfo" and ":wviminfo".
* ":rshada" and ":wshada".
*/
static void ex_viminfo(exarg_T *eap)
static void ex_shada(exarg_T *eap)
{
char_u *save_viminfo;
char_u *save_shada;
save_viminfo = p_viminfo;
if (*p_viminfo == NUL)
p_viminfo = (char_u *)"'100";
if (eap->cmdidx == CMD_rviminfo) {
if (read_viminfo(eap->arg, VIF_WANT_INFO | VIF_WANT_MARKS
| (eap->forceit ? VIF_FORCEIT : 0)) == FAIL)
EMSG(_("E195: Cannot open viminfo file for reading"));
} else
write_viminfo(eap->arg, eap->forceit);
p_viminfo = save_viminfo;
save_shada = p_shada;
if (*p_shada == NUL)
p_shada = (char_u *)"'100";
if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) {
(void) shada_read_everything((char *) eap->arg, eap->forceit, false);
} else {
shada_write_file((char *) eap->arg, eap->forceit);
}
p_shada = save_shada;
}
/*

View File

@@ -65,6 +65,7 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
/*
* Variables shared between getcmdline(), redrawcmdline() and others.
@@ -100,12 +101,6 @@ static int cmd_showtail; /* Only show path tail in lists ? */
static int new_cmdpos; /* position set by set_cmdline_pos() */
typedef struct hist_entry {
int hisnum; /* identifying number */
int viminfo; /* when TRUE hisstr comes from viminfo */
char_u *hisstr; /* actual entry, separator char after the NUL */
} histentry_T;
/*
* Type used by call_user_expand_func
*/
@@ -4230,12 +4225,10 @@ void init_history(void)
// delete entries that don't fit in newlen, if any
for (int i = 0; i < i1; i++) {
xfree(history[type][i].hisstr);
history[type][i].hisstr = NULL;
hist_free_entry(history[type] + i);
}
for (int i = i1 + l1; i < i2; i++) {
xfree(history[type][i].hisstr);
history[type][i].hisstr = NULL;
hist_free_entry(history[type] + i);
}
}
@@ -4253,11 +4246,18 @@ void init_history(void)
}
}
static void clear_hist_entry(histentry_T *hisptr)
static inline void hist_free_entry(histentry_T *hisptr)
FUNC_ATTR_NONNULL_ALL
{
hisptr->hisnum = 0;
hisptr->viminfo = FALSE;
hisptr->hisstr = NULL;
xfree(hisptr->hisstr);
list_unref(hisptr->additional_elements);
clear_hist_entry(hisptr);
}
static inline void clear_hist_entry(histentry_T *hisptr)
FUNC_ATTR_NONNULL_ALL
{
memset(hisptr, 0, sizeof(*hisptr));
}
/*
@@ -4268,9 +4268,8 @@ static int
in_history (
int type,
char_u *str,
int move_to_front, /* Move the entry to the front if it exists */
int sep,
int writing /* ignore entries read from viminfo */
int move_to_front, // Move the entry to the front if it exists
int sep
)
{
int i;
@@ -4288,7 +4287,6 @@ in_history (
* well. */
p = history[type][i].hisstr;
if (STRCMP(str, p) == 0
&& !(writing && history[type][i].viminfo)
&& (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) {
if (!move_to_front)
return TRUE;
@@ -4300,6 +4298,7 @@ in_history (
} while (i != hisidx[type]);
if (last_i >= 0) {
list_T *const list = history[type][i].additional_elements;
str = history[type][i].hisstr;
while (i != hisidx[type]) {
if (++i >= hislen)
@@ -4307,12 +4306,14 @@ in_history (
history[type][last_i] = history[type][i];
last_i = i;
}
list_unref(list);
history[type][i].hisnum = ++hisnum[type];
history[type][i].viminfo = FALSE;
history[type][i].hisstr = str;
return TRUE;
history[type][i].timestamp = os_time();
history[type][i].additional_elements = NULL;
return true;
}
return FALSE;
return false;
}
/*
@@ -4372,27 +4373,27 @@ add_to_history (
if (maptick == last_maptick) {
/* Current line is from the same mapping, remove it */
hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]];
xfree(hisptr->hisstr);
clear_hist_entry(hisptr);
hist_free_entry(hisptr);
--hisnum[histype];
if (--hisidx[HIST_SEARCH] < 0)
hisidx[HIST_SEARCH] = hislen - 1;
}
last_maptick = -1;
}
if (!in_history(histype, new_entry, TRUE, sep, FALSE)) {
if (!in_history(histype, new_entry, true, sep)) {
if (++hisidx[histype] == hislen)
hisidx[histype] = 0;
hisptr = &history[histype][hisidx[histype]];
xfree(hisptr->hisstr);
hist_free_entry(hisptr);
/* Store the separator after the NUL of the string. */
len = (int)STRLEN(new_entry);
hisptr->hisstr = vim_strnsave(new_entry, len + 2);
hisptr->timestamp = os_time();
hisptr->additional_elements = NULL;
hisptr->hisstr[len + 1] = sep;
hisptr->hisnum = ++hisnum[histype];
hisptr->viminfo = FALSE;
if (histype == HIST_SEARCH && in_map)
last_maptick = maptick;
}
@@ -4545,23 +4546,21 @@ char_u *get_history_entry(int histype, int idx)
return (char_u *)"";
}
/*
* Clear all entries of a history.
* "histype" may be one of the HIST_ values.
*/
int clr_history(int histype)
/// Clear all entries in a history
///
/// @param[in] histype One of the HIST_ values.
///
/// @return OK if there was something to clean and histype was one of HIST_
/// values, FAIL otherwise.
int clr_history(const int histype)
{
int i;
histentry_T *hisptr;
if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) {
hisptr = history[histype];
for (i = hislen; i--; ) {
xfree(hisptr->hisstr);
clear_hist_entry(hisptr);
histentry_T *hisptr = history[histype];
for (int i = hislen; i--; hisptr++) {
hist_free_entry(hisptr);
}
hisidx[histype] = -1; /* mark history as cleared */
hisnum[histype] = 0; /* reset identifier counter */
hisidx[histype] = -1; // mark history as cleared
hisnum[histype] = 0; // reset identifier counter
return OK;
}
return FAIL;
@@ -4578,7 +4577,7 @@ int del_history_entry(int histype, char_u *str)
int idx;
int i;
int last;
int found = FALSE;
bool found = false;
regmatch.regprog = NULL;
regmatch.rm_ic = FALSE; /* always match case */
@@ -4595,9 +4594,8 @@ int del_history_entry(int histype, char_u *str)
if (hisptr->hisstr == NULL)
break;
if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0)) {
found = TRUE;
xfree(hisptr->hisstr);
clear_hist_entry(hisptr);
found = true;
hist_free_entry(hisptr);
} else {
if (i != last) {
history[histype][last] = *hisptr;
@@ -4628,7 +4626,7 @@ int del_history_idx(int histype, int idx)
if (i < 0)
return FALSE;
idx = hisidx[histype];
xfree(history[histype][i].hisstr);
hist_free_entry(&history[histype][i]);
/* When deleting the last added search string in a mapping, reset
* last_maptick, so that the last added search string isn't deleted again.
@@ -4641,9 +4639,10 @@ int del_history_idx(int histype, int idx)
history[histype][i] = history[histype][j];
i = j;
}
clear_hist_entry(&history[histype][i]);
if (--i < 0)
clear_hist_entry(&history[histype][idx]);
if (--i < 0) {
i += hislen;
}
hisidx[histype] = i;
return TRUE;
}
@@ -4762,248 +4761,31 @@ void ex_history(exarg_T *eap)
}
}
/*
* Buffers for history read from a viminfo file. Only valid while reading.
*/
static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL, NULL};
static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0};
static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0};
static int viminfo_add_at_front = FALSE;
/*
* Translate a history type number to the associated character.
*/
static int
hist_type2char (
int type,
int use_question /* use '?' instead of '/' */
)
/// Translate a history type number to the associated character
int hist_type2char(int type)
FUNC_ATTR_CONST
{
if (type == HIST_CMD)
return ':';
if (type == HIST_SEARCH) {
if (use_question)
return '?';
else
switch (type) {
case HIST_CMD: {
return ':';
}
case HIST_SEARCH: {
return '/';
}
if (type == HIST_EXPR)
return '=';
return '@';
}
/*
* Prepare for reading the history from the viminfo file.
* This allocates history arrays to store the read history lines.
*/
void prepare_viminfo_history(int asklen, int writing)
{
int i;
int num;
init_history();
viminfo_add_at_front = (asklen != 0 && !writing);
if (asklen > hislen)
asklen = hislen;
for (int type = 0; type < HIST_COUNT; ++type) {
/* Count the number of empty spaces in the history list. Entries read
* from viminfo previously are also considered empty. If there are
* more spaces available than we request, then fill them up. */
for (i = 0, num = 0; i < hislen; i++)
if (history[type][i].hisstr == NULL || history[type][i].viminfo)
num++;
int len = asklen;
if (num > len)
len = num;
if (len <= 0)
viminfo_history[type] = NULL;
else
viminfo_history[type] = xmalloc(len * sizeof(char_u *));
if (viminfo_history[type] == NULL)
len = 0;
viminfo_hislen[type] = len;
viminfo_hisidx[type] = 0;
}
}
/*
* Accept a line from the viminfo, store it in the history array when it's
* new.
*/
int read_viminfo_history(vir_T *virp, int writing)
{
int type;
char_u *val;
type = hist_char2type(virp->vir_line[0]);
if (viminfo_hisidx[type] < viminfo_hislen[type]) {
val = viminfo_readstring(virp, 1, TRUE);
if (val != NULL && *val != NUL) {
int sep = (*val == ' ' ? NUL : *val);
if (!in_history(type, val + (type == HIST_SEARCH),
viminfo_add_at_front, sep, writing)) {
/* Need to re-allocate to append the separator byte. */
size_t len = STRLEN(val);
char_u *p = xmalloc(len + 2);
if (type == HIST_SEARCH) {
/* Search entry: Move the separator from the first
* column to after the NUL. */
memmove(p, val + 1, len);
p[len] = sep;
} else {
/* Not a search entry: No separator in the viminfo
* file, add a NUL separator. */
memmove(p, val, len + 1);
p[len + 1] = NUL;
}
viminfo_history[type][viminfo_hisidx[type]++] = p;
}
}
xfree(val);
}
return viminfo_readline(virp);
}
/*
* Finish reading history lines from viminfo. Not used when writing viminfo.
*/
void finish_viminfo_history(void)
{
int idx;
int i;
int type;
for (type = 0; type < HIST_COUNT; ++type) {
if (history[type] == NULL)
continue;
idx = hisidx[type] + viminfo_hisidx[type];
if (idx >= hislen)
idx -= hislen;
else if (idx < 0)
idx = hislen - 1;
if (viminfo_add_at_front)
hisidx[type] = idx;
else {
if (hisidx[type] == -1)
hisidx[type] = hislen - 1;
do {
if (history[type][idx].hisstr != NULL
|| history[type][idx].viminfo)
break;
if (++idx == hislen)
idx = 0;
} while (idx != hisidx[type]);
if (idx != hisidx[type] && --idx < 0)
idx = hislen - 1;
}
for (i = 0; i < viminfo_hisidx[type]; i++) {
xfree(history[type][idx].hisstr);
history[type][idx].hisstr = viminfo_history[type][i];
history[type][idx].viminfo = TRUE;
if (--idx < 0)
idx = hislen - 1;
}
idx += 1;
idx %= hislen;
for (i = 0; i < viminfo_hisidx[type]; i++) {
history[type][idx++].hisnum = ++hisnum[type];
idx %= hislen;
}
xfree(viminfo_history[type]);
viminfo_history[type] = NULL;
viminfo_hisidx[type] = 0;
}
}
/*
* Write history to viminfo file in "fp".
* When "merge" is TRUE merge history lines with a previously read viminfo
* file, data is in viminfo_history[].
* When "merge" is FALSE just write all history lines. Used for ":wviminfo!".
*/
void write_viminfo_history(FILE *fp, int merge)
{
int i;
int type;
int num_saved;
char_u *p;
int c;
int round;
init_history();
if (hislen == 0)
return;
for (type = 0; type < HIST_COUNT; ++type) {
num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
if (num_saved == 0)
continue;
if (num_saved < 0) /* Use default */
num_saved = hislen;
fprintf(fp, _("\n# %s History (newest to oldest):\n"),
type == HIST_CMD ? _("Command Line") :
type == HIST_SEARCH ? _("Search String") :
type == HIST_EXPR ? _("Expression") :
_("Input Line"));
if (num_saved > hislen)
num_saved = hislen;
/*
* Merge typed and viminfo history:
* round 1: history of typed commands.
* round 2: history from recently read viminfo.
*/
for (round = 1; round <= 2; ++round) {
if (round == 1)
/* start at newest entry, somewhere in the list */
i = hisidx[type];
else if (viminfo_hisidx[type] > 0)
/* start at newest entry, first in the list */
i = 0;
else
/* empty list */
i = -1;
if (i >= 0)
while (num_saved > 0
&& !(round == 2 && i >= viminfo_hisidx[type])) {
p = round == 1 ? history[type][i].hisstr
: viminfo_history[type] == NULL ? NULL
: viminfo_history[type][i];
if (p != NULL && (round == 2
|| !merge
|| !history[type][i].viminfo)) {
--num_saved;
fputc(hist_type2char(type, TRUE), fp);
/* For the search history: put the separator in the
* second column; use a space if there isn't one. */
if (type == HIST_SEARCH) {
c = p[STRLEN(p) + 1];
putc(c == NUL ? ' ' : c, fp);
}
viminfo_writestring(fp, p);
}
if (round == 1) {
/* Decrement index, loop around and stop when back at
* the start. */
if (--i < 0)
i = hislen - 1;
if (i == hisidx[type])
break;
} else {
/* Increment index. Stop at the end in the while. */
++i;
}
}
}
for (i = 0; i < viminfo_hisidx[type]; ++i)
if (viminfo_history[type] != NULL)
xfree(viminfo_history[type][i]);
xfree(viminfo_history[type]);
viminfo_history[type] = NULL;
viminfo_hisidx[type] = 0;
case HIST_EXPR: {
return '=';
}
case HIST_INPUT: {
return '@';
}
case HIST_DEBUG: {
return '>';
}
default: {
assert(false);
}
}
return NUL;
}
/*
@@ -5294,3 +5076,87 @@ char_u *script_get(exarg_T *eap, char_u *cmd)
return (char_u *)ga.ga_data;
}
/// Iterate over history items
///
/// @warning No history-editing functions must be run while iteration is in
/// progress.
///
/// @param[in] iter Pointer to the last history entry.
/// @param[in] history_type Type of the history (HIST_*). Ignored if iter
/// parameter is not NULL.
/// @param[in] zero If true then zero (but not free) returned items.
///
/// @warning When using this parameter user is
/// responsible for calling clr_history()
/// itself after iteration is over. If
/// clr_history() is not called behaviour is
/// undefined. No functions that work with
/// history must be called during iteration
/// in this case.
/// @param[out] hist Next history entry.
///
/// @return Pointer used in next iteration or NULL to indicate that iteration
/// was finished.
const void *hist_iter(const void *const iter, const uint8_t history_type,
const bool zero, histentry_T *const hist)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4)
{
*hist = (histentry_T) {
.hisstr = NULL
};
if (hisidx[history_type] == -1) {
return NULL;
}
histentry_T *const hstart = &(history[history_type][0]);
histentry_T *const hlast = (
&(history[history_type][hisidx[history_type]]));
const histentry_T *const hend = &(history[history_type][hislen - 1]);
histentry_T *hiter;
if (iter == NULL) {
histentry_T *hfirst = hlast;
do {
hfirst++;
if (hfirst > hend) {
hfirst = hstart;
}
if (hfirst->hisstr != NULL) {
break;
}
} while (hfirst != hlast);
hiter = hfirst;
} else {
hiter = (histentry_T *) iter;
}
if (hiter == NULL) {
return NULL;
}
*hist = *hiter;
if (zero) {
memset(hiter, 0, sizeof(*hiter));
}
if (hiter == hlast) {
return NULL;
}
hiter++;
return (const void *) ((hiter > hend) ? hstart : hiter);
}
/// Get array of history items
///
/// @param[in] history_type Type of the history to get array for.
/// @param[out] new_hisidx Location where last index in the new array should
/// be saved.
/// @param[out] new_hisnum Location where last history number in the new
/// history should be saved.
///
/// @return Pointer to the array or NULL.
histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx,
int **const new_hisnum)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
init_history();
*new_hisidx = &(hisidx[history_type]);
*new_hisnum = &(hisnum[history_type]);
return history[history_type];
}

View File

@@ -1,6 +1,7 @@
#ifndef NVIM_EX_GETLN_H
#define NVIM_EX_GETLN_H
#include "nvim/eval_defs.h"
#include "nvim/ex_cmds.h"
/* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */
@@ -23,18 +24,28 @@
#define WILD_ESCAPE 128
#define WILD_ICASE 256
/*
* There are four history tables:
*/
#define HIST_CMD 0 /* colon commands */
#define HIST_SEARCH 1 /* search commands */
#define HIST_EXPR 2 /* expressions (from entering = register) */
#define HIST_INPUT 3 /* input() lines */
#define HIST_DEBUG 4 /* debug commands */
#define HIST_COUNT 5 /* number of history tables */
/// Present history tables
typedef enum {
HIST_CMD, ///< Colon commands.
HIST_SEARCH, ///< Search commands.
HIST_EXPR, ///< Expressions (e.g. from entering = register).
HIST_INPUT, ///< input() lines.
HIST_DEBUG, ///< Debug commands.
} HistoryType;
/// Number of history tables
#define HIST_COUNT (HIST_DEBUG + 1)
typedef char_u *(*CompleteListItemGetter)(expand_T *, int);
/// History entry definition
typedef struct hist_entry {
int hisnum; ///< Entry identifier number.
char_u *hisstr; ///< Actual entry, separator char after the NUL.
Timestamp timestamp; ///< Time when entry was added.
list_T *additional_elements; ///< Additional entries from ShaDa file.
} histentry_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.h.generated.h"
#endif

View File

@@ -57,6 +57,7 @@
#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/shada.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
@@ -2166,16 +2167,17 @@ readfile_charconvert (
/*
* Read marks for the current buffer from the viminfo file, when we support
* Read marks for the current buffer from the ShaDa file, when we support
* buffer marks and the buffer has a name.
*/
static void check_marks_read(void)
{
if (!curbuf->b_marks_read && get_viminfo_parameter('\'') > 0
&& curbuf->b_ffname != NULL)
read_viminfo(NULL, VIF_WANT_MARKS);
if (!curbuf->b_marks_read && get_shada_parameter('\'') > 0
&& curbuf->b_ffname != NULL) {
shada_read_marks();
}
/* Always set b_marks_read; needed when 'viminfo' is changed to include
/* Always set b_marks_read; needed when 'shada' is changed to include
* the ' parameter after opening a buffer. */
curbuf->b_marks_read = true;
}

View File

@@ -891,8 +891,8 @@ EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */
EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
EXTERN int need_highlight_changed INIT(= TRUE);
EXTERN char_u *use_viminfo INIT(= NULL); /* name of viminfo file to use */
EXTERN int need_highlight_changed INIT(= true);
EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
#define NSCRIPT 15
EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */

View File

@@ -114,8 +114,8 @@ int main() {
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
#ifndef NVIM_LIB_KHASH_H
#define NVIM_LIB_KHASH_H
/*!
@header
@@ -194,166 +194,229 @@ static const double __ac_HASH_UPPER = 0.77;
khval_t *vals; \
} kh_##name##_t;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
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); \
extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#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); \
extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
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) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
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); \
kfree(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
REAL_FATTR_UNUSED; \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
REAL_FATTR_UNUSED; \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
REAL_FATTR_UNUSED; \
SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
h->keys = new_keys; \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
REAL_FATTR_UNUSED; \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) { \
kh_resize_##name(h, h->n_buckets - 1); /* clear "deleted" elements */ \
} else { \
kh_resize_##name(h, h->n_buckets + 1); /* expand the hash table */ \
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
REAL_FATTR_UNUSED; \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#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(h->keys); \
kfree(h->flags); \
kfree(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) { \
kh_dealloc_##name(h); \
kfree(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
REAL_FATTR_UNUSED; \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
REAL_FATTR_UNUSED; \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || \
!__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) { \
return h->n_buckets; \
} \
} \
return __ac_iseither(h->flags, i) ? h->n_buckets : i; \
} else { \
return 0; \
} \
} \
SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
REAL_FATTR_UNUSED; \
SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of */ \
/* [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) { \
new_n_buckets = 4; \
} \
/* requested size is too small */ \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) { \
j = 0; \
} else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) \
* sizeof(khint32_t)); \
memset(new_flags, 0xaa, \
__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc( \
(void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
h->keys = new_keys; \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc( \
(void *)h->vals, new_n_buckets * sizeof(khval_t)); \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) { \
val = h->vals[j]; \
} \
__ac_set_isdel_true(h->flags, j); \
/* kick-out process; sort of like in Cuckoo hashing */ \
while (1) { \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) { \
i = (i + (++step)) & new_mask; \
} \
__ac_set_isempty_false(new_flags, i); \
/* kick out the existing element */ \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \
{ \
khkey_t tmp = h->keys[i]; \
h->keys[i] = key; \
key = tmp; \
} \
if (kh_is_map) { \
khval_t tmp = h->vals[i]; \
h->vals[i] = val; \
val = tmp; \
} \
/* mark it as deleted in the old hash table */ \
__ac_set_isdel_true(h->flags, i); \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) { \
h->vals[i] = val; \
} \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, \
new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) { \
h->vals = (khval_t*)krealloc((void *)h->vals, \
new_n_buckets * sizeof(khval_t)); \
} \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
REAL_FATTR_UNUSED; \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size << 1)) { \
kh_resize_##name(h, h->n_buckets - 1); /* clear "deleted" elements */ \
} else { \
kh_resize_##name(h, h->n_buckets + 1); /* expand the hash table */ \
} \
} /* TODO: implement automatically shrinking; */ \
/* resize() already support shrinking */ \
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; \
k = __hash_func(key); \
i = k & mask; \
if (__ac_isempty(h->flags, i)) { \
x = i; /* for speed up */ \
} else { \
last = i; \
while (!__ac_isempty(h->flags, i) \
&& (__ac_isdel(h->flags, i) \
|| !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) { \
site = i; \
} \
i = (i + (++step)) & mask; \
if (i == last) { \
x = site; \
break; \
} \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) { \
x = site; \
} else { \
x = i; \
} \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
h->size++; \
h->n_occupied++; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
h->size++; \
*ret = 2; \
} else { \
*ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
} \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
REAL_FATTR_UNUSED; \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
@@ -446,6 +509,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]
@@ -577,6 +647,24 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key)
code; \
} }
/*! @function
@abstract Iterate over the keys in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_key(h, kvar, code) \
{ \
khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); __i++) { \
if (!kh_exist(h, __i)) { \
continue; \
} \
(kvar) = kh_key(h, __i); \
code; \
} \
}
/* More conenient interfaces */
/*! @function
@@ -622,6 +710,21 @@ typedef const char *kh_cstr_t;
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#endif /* __AC_KHASH_H */
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Return a literal for an empty hash table.
@param name Name of the hash table [symbol]
*/
#define KHASH_EMPTY_TABLE(name) \
((kh_##name##_t) { \
.n_buckets = 0, \
.size = 0, \
.n_occupied = 0, \
.upper_bound = 0, \
.flags = NULL, \
.keys = NULL, \
.vals = NULL, \
})
#endif // NVIM_LIB_KHASH_H

281
src/nvim/lib/ringbuf.h Normal file
View File

@@ -0,0 +1,281 @@
/// Macros-based ring buffer implementation.
///
/// Supported functions:
///
/// - new: allocates new ring buffer.
/// - dealloc: free ring buffer itself.
/// - free: free ring buffer and all its elements.
/// - push: adds element to the end of the buffer.
/// - length: get buffer length.
/// - size: size of the ring buffer.
/// - idx: get element at given index.
/// - idx_p: get pointer to the element at given index.
/// - insert: insert element at given position.
/// - remove: remove element from given position.
#ifndef NVIM_LIB_RINGBUF_H
#define NVIM_LIB_RINGBUF_H
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include "nvim/memory.h"
#include "nvim/func_attr.h"
#define _RINGBUF_LENGTH(rb) \
((rb)->first == NULL ? 0 \
: ((rb)->next == (rb)->first) ? (size_t) ((rb)->buf_end - (rb)->buf) + 1 \
: ((rb)->next > (rb)->first) ? (size_t) ((rb)->next - (rb)->first) \
: (size_t) ((rb)->next - (rb)->buf + (rb)->buf_end - (rb)->first + 1))
#define _RINGBUF_NEXT(rb, var) \
((var) == (rb)->buf_end ? (rb)->buf : (var) + 1)
#define _RINGBUF_PREV(rb, var) \
((var) == (rb)->buf ? (rb)->buf_end : (var) - 1)
/// Iterate over all ringbuf values
///
/// @param rb Ring buffer to iterate over.
/// @param RBType Type of the ring buffer element.
/// @param varname Variable name.
#define RINGBUF_FORALL(rb, RBType, varname) \
size_t varname##_length_fa_ = _RINGBUF_LENGTH(rb); \
for (RBType *varname = ((rb)->first == NULL ? (rb)->next : (rb)->first); \
varname##_length_fa_; \
(varname = _RINGBUF_NEXT(rb, varname)), \
varname##_length_fa_--)
/// Iterate over all ringbuf values, from end to the beginning
///
/// Unlike previous RINGBUF_FORALL uses already defined variable, in place of
/// defining variable in the cycle body.
///
/// @param rb Ring buffer to iterate over.
/// @param RBType Type of the ring buffer element.
/// @param varname Variable name.
#define RINGBUF_ITER_BACK(rb, RBType, varname) \
size_t varname##_length_ib_ = _RINGBUF_LENGTH(rb); \
for (varname = ((rb)->next == (rb)->buf ? (rb)->buf_end : (rb)->next - 1); \
varname##_length_ib_; \
(varname = _RINGBUF_PREV(rb, varname)), \
varname##_length_ib_--)
/// Define a ring buffer structure
///
/// @param TypeName Ring buffer type name. Actual type name will be
/// `{TypeName}RingBuffer`.
/// @param RBType Type of the single ring buffer element.
#define RINGBUF_TYPEDEF(TypeName, RBType) \
typedef struct { \
RBType *buf; \
RBType *next; \
RBType *first; \
RBType *buf_end; \
} TypeName##RingBuffer;
/// Initialize a new ring buffer
///
/// @param TypeName Ring buffer type name. Actual type name will be
/// `{TypeName}RingBuffer`.
/// @param funcprefix Prefix for all ring buffer functions. Function name will
/// look like `{funcprefix}_rb_{function_name}`.
/// @param RBType Type of the single ring buffer element.
/// @param rbfree Function used to free ring buffer element. May be
/// a macros like `#define RBFREE(item)` (to skip freeing).
///
/// Intended function signature: `void *rbfree(RBType *)`;
#define RINGBUF_INIT(TypeName, funcprefix, RBType, rbfree) \
\
\
static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \
REAL_FATTR_WARN_UNUSED_RESULT; \
static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \
{ \
assert(size != 0); \
RBType *buf = xmalloc(size * sizeof(RBType)); \
return (TypeName##RingBuffer) { \
.buf = buf, \
.next = buf, \
.first = NULL, \
.buf_end = buf + size - 1, \
}; \
} \
\
static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \
REAL_FATTR_UNUSED; \
static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \
{ \
if (rb == NULL) { \
return; \
} \
RINGBUF_FORALL(rb, RBType, rbitem) { \
rbfree(rbitem); \
} \
xfree(rb->buf); \
} \
\
static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \
REAL_FATTR_UNUSED; \
static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \
{ \
xfree(rb->buf); \
} \
\
static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \
RBType item) \
REAL_FATTR_NONNULL_ARG(1); \
static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \
RBType item) \
{ \
if (rb->next == rb->first) { \
rbfree(rb->first); \
rb->first = _RINGBUF_NEXT(rb, rb->first); \
} else if (rb->first == NULL) { \
rb->first = rb->next; \
} \
*rb->next = item; \
rb->next = _RINGBUF_NEXT(rb, rb->next); \
} \
\
static inline ptrdiff_t funcprefix##_rb_find_idx( \
const TypeName##RingBuffer *const rb, const RBType *const item_p) \
REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \
static inline ptrdiff_t funcprefix##_rb_find_idx( \
const TypeName##RingBuffer *const rb, const RBType *const item_p) \
{ \
assert(rb->buf <= item_p); \
assert(rb->buf_end >= item_p); \
if (rb->first == NULL) { \
return -1; \
} else if (item_p >= rb->first) { \
return item_p - rb->first; \
} else { \
return item_p - rb->buf + rb->buf_end - rb->first + 1; \
} \
} \
\
static inline size_t funcprefix##_rb_size( \
const TypeName##RingBuffer *const rb) \
REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
static inline size_t funcprefix##_rb_size( \
const TypeName##RingBuffer *const rb) \
{ \
return (size_t) (rb->buf_end - rb->buf) + 1; \
} \
\
static inline size_t funcprefix##_rb_length( \
const TypeName##RingBuffer *const rb) \
REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
static inline size_t funcprefix##_rb_length( \
const TypeName##RingBuffer *const rb) \
{ \
return _RINGBUF_LENGTH(rb); \
} \
\
static inline RBType *funcprefix##_rb_idx_p( \
const TypeName##RingBuffer *const rb, const size_t idx) \
REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
static inline RBType *funcprefix##_rb_idx_p( \
const TypeName##RingBuffer *const rb, const size_t idx) \
{ \
assert(idx <= funcprefix##_rb_size(rb)); \
assert(idx <= funcprefix##_rb_length(rb)); \
if (rb->first + idx > rb->buf_end) { \
return rb->buf + ((rb->first + idx) - (rb->buf_end + 1)); \
} else { \
return rb->first + idx; \
} \
} \
\
static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \
const size_t idx) \
REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \
static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \
const size_t idx) \
{ \
return *funcprefix##_rb_idx_p(rb, idx); \
} \
\
static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \
const size_t idx, \
RBType item) \
REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \
static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \
const size_t idx, \
RBType item) \
{ \
assert(idx <= funcprefix##_rb_size(rb)); \
assert(idx <= funcprefix##_rb_length(rb)); \
const size_t length = funcprefix##_rb_length(rb); \
if (idx == length) { \
funcprefix##_rb_push(rb, item); \
return; \
} \
RBType *const insertpos = funcprefix##_rb_idx_p(rb, idx); \
if (insertpos == rb->next) { \
funcprefix##_rb_push(rb, item); \
return; \
} \
if (length == funcprefix##_rb_size(rb)) { \
rbfree(rb->first); \
} \
if (insertpos < rb->next) { \
memmove(insertpos + 1, insertpos, \
(size_t) ((uintptr_t) rb->next - (uintptr_t) insertpos)); \
} else { \
assert(insertpos > rb->first); \
assert(rb->next <= rb->first); \
memmove(rb->buf + 1, rb->buf, \
(size_t) ((uintptr_t) rb->next - (uintptr_t) rb->buf)); \
*rb->buf = *rb->buf_end; \
memmove(insertpos + 1, insertpos, \
(size_t) ((uintptr_t) (rb->buf_end + 1) - (uintptr_t) insertpos)); \
} \
*insertpos = item; \
if (length == funcprefix##_rb_size(rb)) { \
rb->first = _RINGBUF_NEXT(rb, rb->first); \
} \
rb->next = _RINGBUF_NEXT(rb, rb->next); \
} \
\
static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \
const size_t idx) \
REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \
static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \
const size_t idx) \
{ \
assert(idx < funcprefix##_rb_size(rb)); \
assert(idx < funcprefix##_rb_length(rb)); \
RBType *const rmpos = funcprefix##_rb_idx_p(rb, idx); \
rbfree(rmpos); \
if (rmpos == rb->next - 1) { \
rb->next--; \
if (rb->first == rb->next) { \
rb->first = NULL; \
rb->next = rb->buf; \
} \
} else if (rmpos == rb->first) { \
rb->first = _RINGBUF_NEXT(rb, rb->first); \
if (rb->first == rb->next) { \
rb->first = NULL; \
rb->next = rb->buf; \
} \
} else if (rb->first < rb->next || rb->next == rb->buf) { \
assert(rmpos > rb->first); \
assert(rmpos <= _RINGBUF_PREV(rb, rb->next)); \
memmove(rb->first + 1, rb->first, \
(size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \
rb->first = _RINGBUF_NEXT(rb, rb->first); \
} else if (rmpos < rb->next) { \
memmove(rmpos, rmpos + 1, \
(size_t) ((uintptr_t) rb->next - (uintptr_t) rmpos)); \
rb->next = _RINGBUF_PREV(rb, rb->next); \
} else { \
assert(rb->first < rb->buf_end); \
memmove(rb->first + 1, rb->first, \
(size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \
rb->first = _RINGBUF_NEXT(rb, rb->first); \
} \
}
#endif // NVIM_LIB_RINGBUF_H

View File

@@ -49,6 +49,7 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
@@ -58,6 +59,7 @@
#include "nvim/ui.h"
#include "nvim/version.h"
#include "nvim/window.h"
#include "nvim/shada.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
@@ -377,12 +379,12 @@ int main(int argc, char **argv)
}
/*
* Read in registers, history etc, but not marks, from the viminfo file.
* Read in registers, history etc, from the ShaDa file.
* This is where v:oldfiles gets filled.
*/
if (*p_viminfo != NUL) {
read_viminfo(NULL, VIF_WANT_INFO | VIF_GET_OLDFILES);
TIME_MSG("reading viminfo");
if (*p_shada != NUL) {
shada_read_everything(NULL, false, true);
TIME_MSG("reading ShaDa");
}
/* It's better to make v:oldfiles an empty list than NULL. */
if (get_vim_var_list(VV_OLDFILES) == NULL)
@@ -803,9 +805,10 @@ void getout(int exitval)
apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf);
}
if (p_viminfo && *p_viminfo != NUL)
/* Write out the registers, history, marks etc, to the viminfo file */
write_viminfo(NULL, FALSE);
if (p_shada && *p_shada != NUL) {
// Write out the registers, history, marks etc, to the ShaDa file
shada_write_file(NULL, false);
}
if (get_vim_var_nr(VV_DYING) <= 1)
apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, FALSE, curbuf);
@@ -1164,7 +1167,7 @@ static void command_line_scan(mparm_T *parmp)
}
/*FALLTHROUGH*/
case 'S': /* "-S {file}" execute Vim script */
case 'i': /* "-i {viminfo}" use for viminfo */
case 'i': /* "-i {shada}" use for ShaDa file */
case 'u': /* "-u {vimrc}" vim inits file */
case 'U': /* "-U {gvimrc}" gvim inits file */
case 'W': /* "-W {scriptout}" overwrite */
@@ -1235,8 +1238,8 @@ static void command_line_scan(mparm_T *parmp)
parmp->use_ef = (char_u *)argv[0];
break;
case 'i': /* "-i {viminfo}" use for viminfo */
use_viminfo = (char_u *)argv[0];
case 'i': /* "-i {shada}" use for shada */
used_shada_file = argv[0];
break;
case 's': /* "-s {scriptin}" read from script file */
@@ -2039,7 +2042,7 @@ static void usage(void)
mch_msg(_(" -r, -L List swap files and exit\n"));
mch_msg(_(" -r <file> Recover crashed session\n"));
mch_msg(_(" -u <nvimrc> Use <nvimrc> instead of the default\n"));
mch_msg(_(" -i <nviminfo> Use <nviminfo> instead of the default\n"));
mch_msg(_(" -i <shada> Use <shada> instead of the default " SHADA_FILE "\n")); // NOLINT(whitespace/line_length)
mch_msg(_(" --noplugin Don't load plugin scripts\n"));
mch_msg(_(" -o[N] Open N windows (default: one for each file)\n"));
mch_msg(_(" -O[N] Like -o but split vertically\n"));

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,78 @@
#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"
#include "nvim/pos.h"
#include "nvim/os/time.h"
/// Set fmark using given value
#define SET_FMARK(fmarkp_, mark_, fnum_) \
do { \
fmark_T *const fmarkp__ = fmarkp_; \
fmarkp__->mark = mark_; \
fmarkp__->fnum = fnum_; \
fmarkp__->timestamp = os_time(); \
fmarkp__->additional_data = NULL; \
} while (0)
/// Free and set fmark using given value
#define RESET_FMARK(fmarkp_, mark_, fnum_) \
do { \
fmark_T *const fmarkp___ = fmarkp_; \
free_fmark(*fmarkp___); \
SET_FMARK(fmarkp___, mark_, fnum_); \
} while (0)
/// Clear given fmark
#define CLEAR_FMARK(fmarkp_) \
RESET_FMARK(fmarkp_, ((pos_T) {0, 0, 0}), 0)
/// Set given extended mark (regular mark + file name)
#define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
xfmarkp__->fname = fname_; \
SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
} while (0)
/// Free and set given extended mark (regular mark + file name)
#define RESET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
free_xfmark(*xfmarkp__); \
xfmarkp__->fname = fname_; \
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"

View File

@@ -2,25 +2,47 @@
#define NVIM_MARK_DEFS_H
#include "nvim/pos.h"
#include "nvim/os/time.h"
#include "nvim/eval_defs.h"
/*
* marks: positions in a file
* (a normal mark is a lnum/col pair, the same as a file position)
*/
#define NMARKS ('z' - 'a' + 1) /* max. # of named marks */
#define JUMPLISTSIZE 100 /* max. # of marks in jump list */
#define TAGSTACKSIZE 20 /* max. # of tags in tag stack */
/// Number of possible numbered global marks
#define EXTRA_MARKS ('9' - '0' + 1)
/// Maximum possible number of letter marks
#define NMARKS ('z' - 'a' + 1)
/// 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
/// Maximum number of tags in tag stack
#define TAGSTACKSIZE 20
/// Structure defining single local mark
typedef struct filemark {
pos_T mark; /* cursor position */
int fnum; /* file number */
pos_T mark; ///< Cursor position.
int fnum; ///< File number.
Timestamp timestamp; ///< Time when this mark was last set.
dict_T *additional_data; ///< Additional data from ShaDa file.
} fmark_T;
/* Xtended file mark: also has a file name */
/// Structure defining extended mark (mark with file name attached)
typedef struct xfilemark {
fmark_T fmark;
char_u *fname; /* file name, used when fnum == 0 */
fmark_T fmark; ///< Actual mark.
char_u *fname; ///< File name, used when fnum == 0.
} xfmark_T;
#endif // NVIM_MARK_DEFS_H

View File

@@ -66,7 +66,7 @@
* (4) The encoding of the file is specified with 'fileencoding'. Conversion
* is to be done when it's different from 'encoding'.
*
* The viminfo file is a special case: Only text is converted, not file names.
* The ShaDa file is a special case: Only text is converted, not file names.
* Vim scripts may contain an ":encoding" command. This has an effect for
* some commands, like ":menutrans"
*/

View File

@@ -30,7 +30,7 @@
/// Try to free memory. Used when trying to recover from out of memory errors.
/// @see {xmalloc}
static void try_to_free_memory(void)
void try_to_free_memory(void)
{
static bool trying_to_free = false;
// avoid recursive calls

View File

@@ -2042,8 +2042,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
/* set the '. mark */
if (!cmdmod.keepjumps) {
curbuf->b_last_change.lnum = lnum;
curbuf->b_last_change.col = col;
RESET_FMARK(&curbuf->b_last_change, ((pos_T) {lnum, col, 0}), 0);
/* Create a new entry if a new undo-able change was started or we
* don't have an entry yet. */
@@ -2054,7 +2053,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
/* Don't create a new entry when the line number is the same
* as the last one and the column is not too far away. Avoids
* creating many entries for typing "xxxxx". */
p = &curbuf->b_changelist[curbuf->b_changelistlen - 1];
p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark;
if (p->lnum != lnum)
add = TRUE;
else {
@@ -2074,7 +2073,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
/* changelist is full: remove oldest entry */
curbuf->b_changelistlen = JUMPLISTSIZE - 1;
memmove(curbuf->b_changelist, curbuf->b_changelist + 1,
sizeof(pos_T) * (JUMPLISTSIZE - 1));
sizeof(curbuf->b_changelist[0]) * (JUMPLISTSIZE - 1));
FOR_ALL_TAB_WINDOWS(tp, wp) {
/* Correct position in changelist for other windows on
* this buffer. */

View File

@@ -6327,8 +6327,8 @@ static void nv_g_cmd(cmdarg_T *cap)
* "gi": start Insert at the last position.
*/
case 'i':
if (curbuf->b_last_insert.lnum != 0) {
curwin->w_cursor = curbuf->b_last_insert;
if (curbuf->b_last_insert.mark.lnum != 0) {
curwin->w_cursor = curbuf->b_last_insert.mark;
check_cursor_lnum();
i = (int)STRLEN(get_cursor_line_ptr());
if (curwin->w_cursor.col > (colnr_T)i) {

View File

@@ -50,26 +50,11 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/input.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 viminfo:
#define STAR_REGISTER 37
#define PLUS_REGISTER 38
#define NUM_REGISTERS 39
#include "nvim/os/time.h"
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;
@@ -778,19 +763,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 = op_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 +867,16 @@ int do_record(int c)
return retval;
}
static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data)
FUNC_ATTR_NONNULL_ARG(1)
{
if (reg->additional_data == additional_data) {
return;
}
dict_unref(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 +906,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 +2255,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 +2277,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 +2356,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 *));
reg->additional_data = NULL;
reg->timestamp = os_time();
y_idx = 0;
lnum = oap->start.lnum;
@@ -4433,171 +4422,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 +4563,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 +4796,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 +5188,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 ShaDa file.
int i = 0;
for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) {
@@ -5411,6 +5240,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 +5309,91 @@ 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_PURE
{
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_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
*name = NUL;
const yankreg_T *iter_reg = (iter == NULL
? &(y_regs[0])
: (const yankreg_T *const) iter);
while (iter_reg - &(y_regs[0]) < NUM_SAVED_REGISTERS && reg_empty(iter_reg)) {
iter_reg++;
}
if (iter_reg - &(y_regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) {
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_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
///
/// @param[in] name Register name.
/// @param[in] reg Register value.
///
/// @return true on success, false on failure.
bool op_register_set(const char name, const yankreg_T reg)
{
int i = op_reg_index(name);
if (i == -1) {
return false;
}
free_register(&y_regs[i]);
y_regs[i] = reg;
return true;
}
/// Get register with the given name
///
/// @param[in] name Register name.
///
/// @return Pointer to the register contents or NULL.
const yankreg_T *op_register_get(const char name)
{
int i = op_reg_index(name);
if (i == -1) {
return NULL;
}
return &y_regs[i];
}

View File

@@ -3,7 +3,11 @@
#include <stdbool.h>
#include "nvim/macros.h"
#include "nvim/ascii.h"
#include "nvim/types.h"
#include "nvim/eval_defs.h"
#include "nvim/os/time.h"
typedef int (*Indenter)(void);
@@ -15,6 +19,22 @@ typedef int (*Indenter)(void);
#define PUT_LINE_SPLIT 16 /* split line for linewise register */
#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!
*/
@@ -47,14 +67,6 @@ typedef int (*Indenter)(void);
#define OP_FORMAT2 26 /* "gw" format operator, keeps cursor pos */
#define OP_FUNCTION 27 /* "g@" call 'operatorfunc' */
/// Contents of a yank (read-write) register
typedef struct yankreg {
char_u **y_array; ///< pointer to array of line pointers
linenr_T y_size; ///< number of lines in y_array
char_u y_type; ///< MLINE, MCHAR or MBLOCK
colnr_T y_width; ///< only set if y_type == MBLOCK
} yankreg_T;
/// Flags for get_reg_contents().
enum GRegFlags {
kGRegNoExpr = 1, ///< Do not allow expression register.
@@ -62,6 +74,41 @@ enum GRegFlags {
kGRegList = 4 ///< Return list.
};
/// Definition of one register
typedef struct yankreg {
char_u **y_array; ///< Pointer to an array of line pointers.
linenr_T y_size; ///< Number of lines in y_array.
char_u y_type; ///< Register type: MLINE, MCHAR or MBLOCK.
colnr_T y_width; ///< Register width (only valid for y_type == MBLOCK).
Timestamp timestamp; ///< Time when register was last modified.
dict_T *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

View File

@@ -1738,16 +1738,16 @@ set_options_bin (
/*
* Find the parameter represented by the given character (eg ', :, ", or /),
* and return its associated value in the 'viminfo' string.
* and return its associated value in the 'shada' string.
* Only works for number parameters, not for 'r' or 'n'.
* If the parameter is not specified in the string or there is no following
* number, return -1.
*/
int get_viminfo_parameter(int type)
int get_shada_parameter(int type)
{
char_u *p;
p = find_viminfo_parameter(type);
p = find_shada_parameter(type);
if (p != NULL && ascii_isdigit(*p))
return atoi((char *)p);
return -1;
@@ -1755,14 +1755,14 @@ int get_viminfo_parameter(int type)
/*
* Find the parameter represented by the given character (eg ''', ':', '"', or
* '/') in the 'viminfo' option and return a pointer to the string after it.
* '/') in the 'shada' option and return a pointer to the string after it.
* Return NULL if the parameter is not specified in the string.
*/
char_u *find_viminfo_parameter(int type)
char_u *find_shada_parameter(int type)
{
char_u *p;
for (p = p_viminfo; *p; ++p) {
for (p = p_shada; *p; ++p) {
if (*p == type)
return p + 1;
if (*p == 'n') /* 'n' is always the last one */
@@ -1968,6 +1968,8 @@ static void redraw_titles(void) {
redraw_tabline = TRUE;
}
static int shada_idx = -1;
/*
* Set a string option to a new value (without checking the effect).
* The string is copied into allocated memory.
@@ -2001,6 +2003,8 @@ set_string_option_direct (
if (options[idx].var == NULL) /* can't set hidden option */
return;
assert((void *) options[idx].var != (void *) &p_shada);
s = vim_strsave(val);
{
varp = (char_u **)get_varp_scope(&(options[idx]),
@@ -2441,10 +2445,16 @@ did_set_string_option (
verbose_stop();
if (*p_vfile != NUL && verbose_open() == FAIL)
errmsg = e_invarg;
}
/* 'viminfo' */
else if (varp == &p_viminfo) {
for (s = p_viminfo; *s; ) {
/* 'shada' */
} else if (varp == &p_shada) {
// TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
// option.
opt_idx = ((options[opt_idx].fullname[0] == 'v')
? (shada_idx == -1
? ((shada_idx = findoption((char_u *) "shada")))
: shada_idx)
: opt_idx);
for (s = p_shada; *s; ) {
/* Check it's a valid character */
if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
errmsg = illegal_char(errbuf, *s);
@@ -2486,7 +2496,7 @@ did_set_string_option (
break;
}
}
if (*p_viminfo && errmsg == NULL && get_viminfo_parameter('\'') < 0)
if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0)
errmsg = (char_u *)N_("E528: Must specify a ' value");
}
/* 'showbreak' */

View File

@@ -558,7 +558,7 @@ EXTERN long p_ur; /* 'undoreload' */
EXTERN long p_uc; /* 'updatecount' */
EXTERN long p_ut; /* 'updatetime' */
EXTERN char_u *p_fcs; /* 'fillchar' */
EXTERN char_u *p_viminfo; /* 'viminfo' */
EXTERN char_u *p_shada; /* 'shada' */
EXTERN char_u *p_vdir; /* 'viewdir' */
EXTERN char_u *p_vop; /* 'viewoptions' */
EXTERN unsigned vop_flags; /* uses SSOP_ flags */

View File

@@ -1991,6 +1991,14 @@ return {
vim="blank,buffers,curdir,folds,help,tabpages,winsize"
}}
},
{
full_name='shada', abbreviation='sd',
type='string', list='comma', scope={'global'},
deny_duplicates=true,
secure=true,
varname='p_shada',
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{
full_name='shell', abbreviation='sh',
type='string', scope={'global'},
@@ -2584,7 +2592,7 @@ return {
type='string', list='comma', scope={'global'},
deny_duplicates=true,
secure=true,
varname='p_viminfo',
varname='p_shada',
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{

View File

@@ -21,4 +21,9 @@ typedef struct {
uv_dirent_t ent; ///< @private The entry information.
} Directory;
/// Function to convert -errno error to char * error description
///
/// -errno errors are returned by a number of os functions.
#define os_strerror uv_strerror
#endif // NVIM_OS_FS_DEFS_H

View File

@@ -103,3 +103,12 @@ struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
time_t rawtime = time(NULL);
return os_localtime_r(&rawtime, result);
}
/// Obtains the current UNIX timestamp
///
/// @return Seconds since epoch.
Timestamp os_time(void)
FUNC_ATTR_WARN_UNUSED_RESULT
{
return (Timestamp) time(NULL);
}

View File

@@ -5,6 +5,8 @@
#include <stdbool.h>
#include <time.h>
typedef uint64_t Timestamp;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/time.h.generated.h"
#endif

View File

@@ -43,8 +43,8 @@
#ifndef VIMRC_FILE
# define VIMRC_FILE ".nvimrc"
#endif
#ifndef VIMINFO_FILE
# define VIMINFO_FILE "~/.nviminfo"
#ifndef SHADA_FILE
# define SHADA_FILE "~/.nvim/shada/main.shada"
#endif
// Default for 'backupdir'.

View File

@@ -9,7 +9,7 @@
// Defines needed to fix the build on Windows:
// - USR_EXRC_FILE
// - USR_VIMRC_FILE
// - VIMINFO_FILE
// - SHADA_FILE
// - DFLT_DIR
// - DFLT_BDIR
// - DFLT_VDIR

View File

@@ -79,23 +79,6 @@
* Henry Spencer's regular expression library. See regexp.c.
*/
/* The offset for a search command is store in a soff struct */
/* Note: only spats[0].off is really used */
struct soffset {
int dir; /* search direction, '/' or '?' */
int line; /* search has line offset */
int end; /* search set cursor at end */
long off; /* line or char offset */
};
/* A search pattern and its attributes are stored in a spat struct */
struct spat {
char_u *pat; /* the pattern (in allocated memory) or NULL */
int magic; /* magicness of the pattern */
int no_scs; /* no smartcase for this pattern */
struct soffset off;
};
/*
* Two search patterns are remembered: One for the :substitute command and
* one for other searches. last_idx points to the one that was used the last
@@ -103,8 +86,10 @@ struct spat {
*/
static struct spat spats[2] =
{
{NULL, TRUE, FALSE, {'/', 0, 0, 0L}}, /* last used search pat */
{NULL, TRUE, FALSE, {'/', 0, 0, 0L}} /* last used substitute pat */
// Last used search pattern
[0] = {NULL, true, false, 0, {'/', false, false, 0L}, NULL},
// Last used substitute pattern
[1] = {NULL, true, false, 0, {'/', false, false, 0L}, NULL}
};
static int last_idx = 0; /* index in spats[] for RE_LAST */
@@ -256,10 +241,12 @@ char_u *reverse_text(char_u *s) FUNC_ATTR_NONNULL_RET
void save_re_pat(int idx, char_u *pat, int magic)
{
if (spats[idx].pat != pat) {
xfree(spats[idx].pat);
free_spat(&spats[idx]);
spats[idx].pat = vim_strsave(pat);
spats[idx].magic = magic;
spats[idx].no_scs = no_smartcase;
spats[idx].timestamp = os_time();
spats[idx].additional_data = NULL;
last_idx = idx;
/* If 'hlsearch' set and search pat changed: need redraw. */
if (p_hls)
@@ -291,21 +278,29 @@ void save_search_patterns(void)
void restore_search_patterns(void)
{
if (--save_level == 0) {
xfree(spats[0].pat);
free_spat(&spats[0]);
spats[0] = saved_spats[0];
set_vv_searchforward();
xfree(spats[1].pat);
free_spat(&spats[1]);
spats[1] = saved_spats[1];
last_idx = saved_last_idx;
SET_NO_HLSEARCH(saved_no_hlsearch);
}
}
static inline void free_spat(struct spat *const spat)
{
xfree(spat->pat);
dict_unref(spat->additional_data);
}
#if defined(EXITFREE)
void free_search_patterns(void)
{
xfree(spats[0].pat);
xfree(spats[1].pat);
free_spat(&spats[0]);
free_spat(&spats[1]);
memset(spats, 0, sizeof(spats));
if (mr_pattern_alloced) {
xfree(mr_pattern);
@@ -414,17 +409,19 @@ void reset_search_dir(void)
}
/*
* Set the last search pattern. For ":let @/ =" and viminfo.
* Set the last search pattern. For ":let @/ =" and ShaDa file.
* Also set the saved search pattern, so that this works in an autocommand.
*/
void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
{
xfree(spats[idx].pat);
free_spat(&spats[idx]);
/* An empty string means that nothing should be matched. */
if (*s == NUL)
spats[idx].pat = NULL;
else
spats[idx].pat = (char_u *) xstrdup((char *) s);
spats[idx].timestamp = os_time();
spats[idx].additional_data = NULL;
spats[idx].magic = magic;
spats[idx].no_scs = FALSE;
spats[idx].off.dir = '/';
@@ -435,7 +432,7 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
if (setlast)
last_idx = idx;
if (save_level) {
xfree(saved_spats[idx].pat);
free_spat(&saved_spats[idx]);
saved_spats[idx] = spats[0];
if (spats[idx].pat == NULL)
saved_spats[idx].pat = NULL;
@@ -1053,7 +1050,7 @@ int do_search(
else if ((options & SEARCH_OPT) &&
(*p == 'e' || *p == 's' || *p == 'b')) {
if (*p == 'e') /* end */
spats[0].off.end = SEARCH_END;
spats[0].off.end = true;
++p;
}
if (ascii_isdigit(*p) || *p == '+' || *p == '-') { /* got an offset */
@@ -1166,12 +1163,13 @@ int do_search(
lrFswap(searchstr,0);
c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD,
searchstr, count, spats[0].off.end + (options &
(SEARCH_KEEP + SEARCH_PEEK +
SEARCH_HIS
+ SEARCH_MSG + SEARCH_START
+ ((pat != NULL && *pat ==
';') ? 0 : SEARCH_NOOF))),
searchstr, count, (spats[0].off.end * SEARCH_END
+ (options &
(SEARCH_KEEP + SEARCH_PEEK +
SEARCH_HIS
+ SEARCH_MSG + SEARCH_START
+ ((pat != NULL && *pat ==
';') ? 0 : SEARCH_NOOF)))),
RE_LAST, (linenr_T)0, tm);
if (dircp != NULL)
@@ -4605,105 +4603,45 @@ static void show_pat_in_path(char_u *line, int type, int did_show, int action, F
}
}
int read_viminfo_search_pattern(vir_T *virp, int force)
/// Get last search pattern
void get_search_pattern(SearchPattern *const pat)
{
char_u *lp;
int idx = -1;
int magic = FALSE;
int no_scs = FALSE;
int off_line = FALSE;
int off_end = 0;
long off = 0;
int setlast = FALSE;
static int hlsearch_on = FALSE;
char_u *val;
/*
* Old line types:
* "/pat", "&pat": search/subst. pat
* "~/pat", "~&pat": last used search/subst. pat
* New line types:
* "~h", "~H": hlsearch highlighting off/on
* "~<magic><smartcase><line><end><off><last><which>pat"
* <magic>: 'm' off, 'M' on
* <smartcase>: 's' off, 'S' on
* <line>: 'L' line offset, 'l' char offset
* <end>: 'E' from end, 'e' from start
* <off>: decimal, offset
* <last>: '~' last used pattern
* <which>: '/' search pat, '&' subst. pat
*/
lp = virp->vir_line;
if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M')) { /* new line type */
if (lp[1] == 'M') /* magic on */
magic = TRUE;
if (lp[2] == 's')
no_scs = TRUE;
if (lp[3] == 'L')
off_line = TRUE;
if (lp[4] == 'E')
off_end = SEARCH_END;
lp += 5;
off = getdigits_long(&lp);
}
if (lp[0] == '~') { /* use this pattern for last-used pattern */
setlast = TRUE;
lp++;
}
if (lp[0] == '/')
idx = RE_SEARCH;
else if (lp[0] == '&')
idx = RE_SUBST;
else if (lp[0] == 'h') /* ~h: 'hlsearch' highlighting off */
hlsearch_on = FALSE;
else if (lp[0] == 'H') /* ~H: 'hlsearch' highlighting on */
hlsearch_on = TRUE;
if (idx >= 0) {
if (force || spats[idx].pat == NULL) {
val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1), TRUE);
set_last_search_pat(val, idx, magic, setlast);
xfree(val);
spats[idx].no_scs = no_scs;
spats[idx].off.line = off_line;
spats[idx].off.end = off_end;
spats[idx].off.off = off;
if (setlast) {
SET_NO_HLSEARCH(!hlsearch_on);
}
}
}
return viminfo_readline(virp);
memcpy(pat, &(spats[0]), sizeof(spats[0]));
}
void write_viminfo_search_pattern(FILE *fp)
/// Get last substitute pattern
void get_substitute_pattern(SearchPattern *const pat)
{
if (get_viminfo_parameter('/') != 0) {
fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c",
(no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H');
wvsp_one(fp, RE_SEARCH, "", '/');
wvsp_one(fp, RE_SUBST, _("Substitute "), '&');
}
memcpy(pat, &(spats[1]), sizeof(spats[1]));
memset(&(pat->off), 0, sizeof(pat->off));
}
static void
wvsp_one (
FILE *fp, /* file to write to */
int idx, /* spats[] index */
char *s, /* search pat */
int sc /* dir char */
)
/// Set last search pattern
void set_search_pattern(const SearchPattern pat)
{
if (spats[idx].pat != NULL) {
fprintf(fp, _("\n# Last %sSearch Pattern:\n~"), s);
/* off.dir is not stored, it's reset to forward */
fprintf(fp, "%c%c%c%c%" PRId64 "%s%c",
spats[idx].magic ? 'M' : 'm', /* magic */
spats[idx].no_scs ? 's' : 'S', /* smartcase */
spats[idx].off.line ? 'L' : 'l', /* line offset */
spats[idx].off.end ? 'E' : 'e', /* offset from end */
(int64_t)spats[idx].off.off, /* offset */
last_idx == idx ? "~" : "", /* last used pat */
sc);
viminfo_writestring(fp, spats[idx].pat);
}
free_spat(&spats[0]);
memcpy(&(spats[0]), &pat, sizeof(spats[0]));
}
/// Set last substitute pattern
void set_substitute_pattern(const SearchPattern pat)
{
free_spat(&spats[1]);
memcpy(&(spats[1]), &pat, sizeof(spats[1]));
memset(&(spats[1].off), 0, sizeof(spats[1].off));
}
/// Set last used search pattern
///
/// @param[in] is_substitute_pattern If true set substitute pattern as last
/// used. Otherwise sets search pattern.
void set_last_used_pattern(const bool is_substitute_pattern)
{
last_idx = (is_substitute_pattern ? 1 : 0);
}
/// Returns true if search pattern was the last used one
bool search_was_last_used(void)
{
return last_idx == 0;
}

View File

@@ -1,6 +1,9 @@
#ifndef NVIM_SEARCH_H
#define NVIM_SEARCH_H
#include <stdbool.h>
#include <stdint.h>
/* Values for the find_pattern_in_path() function args 'type' and 'action': */
#define FIND_ANY 1
#define FIND_DEFINE 2
@@ -39,6 +42,27 @@
#define RE_BOTH 2 /* save pat in both patterns */
#define RE_LAST 2 /* use last used pattern if "pat" is NULL */
/// Structure containing offset definition for the last search pattern
///
/// @note Only offset for the last search pattern is used, not for the last
/// substitute pattern.
typedef struct soffset {
char dir; ///< Search direction: forward ('/') or backward ('?')
bool line; ///< True if search has line offset.
bool end; ///< True if search sets cursor at the end.
int64_t off; ///< Actual offset value.
} SearchOffset;
/// Structure containing last search pattern and its attributes.
typedef struct spat {
char_u *pat; ///< The pattern (in allocated memory) or NULL.
bool magic; ///< Magicness of the pattern.
bool no_scs; ///< No smartcase for this pattern.
Timestamp timestamp; ///< Time of the last change.
SearchOffset off; ///< Pattern offset.
dict_T *additional_data; ///< Additional data from ShaDa file.
} SearchPattern;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "search.h.generated.h"
#endif

4040
src/nvim/shada.c Normal file

File diff suppressed because it is too large Load Diff

7
src/nvim/shada.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef NVIM_SHADA_H
#define NVIM_SHADA_H
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "shada.h.generated.h"
#endif
#endif // NVIM_SHADA_H

View File

@@ -483,6 +483,21 @@ bool has_non_ascii(const char_u *s)
return false;
}
/// Return true if string "s" contains a non-ASCII character (128 or higher).
/// When "s" is NULL false is returned.
bool has_non_ascii_len(const char *const s, const size_t len)
FUNC_ATTR_PURE
{
if (s != NULL) {
for (size_t i = 0; i < len; i++) {
if ((uint8_t) s[i] >= 128) {
return true;
}
}
}
return false;
}
/*
* Concatenate two strings and return the result in allocated memory.
*/

View File

@@ -130,7 +130,7 @@ static char_u *tagmatchname = NULL; /* name of last used tag */
* Tag for preview window is remembered separately, to avoid messing up the
* normal tagstack.
*/
static taggy_T ptag_entry = {NULL, {INIT_POS_T(0, 0, 0), 0}, 0, 0};
static taggy_T ptag_entry = {NULL, {INIT_POS_T(0, 0, 0), 0, 0, NULL}, 0, 0};
/*
* Jump to tag; handling of tag commands and tag stack

View File

@@ -32,7 +32,7 @@ endfunc
$put ='VimLeave done'
write
endfunc
:set viminfo='100
:set shada='100
:au BufUnload * call CloseAll()
:au VimLeave * call WriteToOut()
:e small.vim

View File

@@ -325,6 +325,14 @@ static long get_undolevel(void)
return curbuf->b_p_ul;
}
static inline void zero_fmark_additional_data(fmark_T *fmarks)
{
for (size_t i = 0; i < NMARKS; i++) {
dict_unref(fmarks[i].additional_data);
fmarks[i].additional_data = NULL;
}
}
/*
* Common code for various ways to save text before a change.
* "top" is the line above the first changed line.
@@ -467,7 +475,9 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
/* save named marks and Visual marks for undo */
memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
zero_fmark_additional_data(curbuf->b_namedm);
memmove(uhp->uh_namedm, curbuf->b_namedm,
sizeof(curbuf->b_namedm[0]) * NMARKS);
uhp->uh_visual = curbuf->b_visual;
curbuf->b_u_newhead = uhp;
@@ -785,7 +795,7 @@ static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
undo_write_bytes(bi, (uintmax_t)uhp->uh_flags, 2);
// Assume NMARKS will stay the same.
for (size_t i = 0; i < (size_t)NMARKS; i++) {
serialize_pos(bi, uhp->uh_namedm[i]);
serialize_pos(bi, uhp->uh_namedm[i].mark);
}
serialize_visualinfo(bi, &uhp->uh_visual);
uint8_t time_buf[8];
@@ -831,8 +841,11 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name)
unserialize_pos(bi, &uhp->uh_cursor);
uhp->uh_cursor_vcol = undo_read_4c(bi);
uhp->uh_flags = undo_read_2c(bi);
const Timestamp cur_timestamp = os_time();
for (size_t i = 0; i < (size_t)NMARKS; i++) {
unserialize_pos(bi, &uhp->uh_namedm[i]);
unserialize_pos(bi, &uhp->uh_namedm[i].mark);
uhp->uh_namedm[i].timestamp = cur_timestamp;
uhp->uh_namedm[i].fnum = 0;
}
unserialize_visualinfo(bi, &uhp->uh_visual);
uhp->uh_time = undo_read_time(bi);
@@ -2009,7 +2022,7 @@ static void u_undoredo(int undo)
u_entry_T *newlist = NULL;
int old_flags;
int new_flags;
pos_T namedm[NMARKS];
fmark_T namedm[NMARKS];
visualinfo_T visualinfo;
int empty_buffer; /* buffer became empty */
u_header_T *curhead = curbuf->b_u_curhead;
@@ -2029,7 +2042,8 @@ static void u_undoredo(int undo)
/*
* save marks before undo/redo
*/
memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
zero_fmark_additional_data(curbuf->b_namedm);
memmove(namedm, curbuf->b_namedm, sizeof(curbuf->b_namedm[0]) * NMARKS);
visualinfo = curbuf->b_visual;
curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
curbuf->b_op_start.col = 0;
@@ -2158,7 +2172,8 @@ static void u_undoredo(int undo)
* restore marks from before undo/redo
*/
for (i = 0; i < NMARKS; ++i)
if (curhead->uh_namedm[i].lnum != 0) {
if (curhead->uh_namedm[i].mark.lnum != 0) {
free_fmark(curbuf->b_namedm[i]);
curbuf->b_namedm[i] = curhead->uh_namedm[i];
curhead->uh_namedm[i] = namedm[i];
}

View File

@@ -5,6 +5,7 @@
#include "nvim/pos.h"
#include "nvim/buffer_defs.h"
#include "nvim/mark_defs.h"
/* Structure to store info about the Visual area. */
typedef struct {
@@ -54,7 +55,7 @@ struct u_header {
pos_T uh_cursor; /* cursor position before saving */
long uh_cursor_vcol;
int uh_flags; /* see below */
pos_T uh_namedm[NMARKS]; /* marks before undo/after redo */
fmark_T uh_namedm[NMARKS]; /* marks before undo/after redo */
visualinfo_T uh_visual; /* Visual areas before undo/after redo */
time_t uh_time; /* timestamp when the change was made */
long uh_save_nr; /* set when the file was saved after the

View File

@@ -1925,7 +1925,7 @@ int win_close(win_T *win, int free_buf)
&& (last_window() || curtab != prev_curtab
|| close_last_window_tabpage(win, free_buf, prev_curtab))) {
/* Autocommands have close all windows, quit now. Restore
* curwin->w_buffer, otherwise writing viminfo may fail. */
* curwin->w_buffer, otherwise writing ShaDa file may fail. */
if (curwin->w_buffer == NULL)
curwin->w_buffer = curbuf;
getout(0);