diff options
author | ZyX <kp-pav@yandex.ru> | 2015-07-04 22:36:06 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2015-10-08 21:59:55 +0300 |
commit | 8c93877e1c3205f6b42b3e4c94d9be6262557dfb (patch) | |
tree | c413b07fe5f7ce9d09dd889204bf3b6ad23eb44b | |
parent | 4eeafe7f72de2e63c68977ff929a4a0d49c711f9 (diff) | |
download | rneovim-8c93877e1c3205f6b42b3e4c94d9be6262557dfb.tar.gz rneovim-8c93877e1c3205f6b42b3e4c94d9be6262557dfb.tar.bz2 rneovim-8c93877e1c3205f6b42b3e4c94d9be6262557dfb.zip |
shada: Add support for dumping and restoring buffer list
-rw-r--r-- | src/nvim/buffer.c | 23 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/shada.c | 208 | ||||
-rw-r--r-- | test/functional/shada/buffers_spec.lua | 65 |
4 files changed, 268 insertions, 32 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5f7120e47c..53612977ee 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -30,6 +30,7 @@ #include <inttypes.h> #include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -556,6 +557,10 @@ static void free_buffer(buf_T *buf) free_buffer_stuff(buf, TRUE); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); + if (buf->additional_data != NULL) { + api_free_dictionary(*buf->additional_data); + xfree(buf->additional_data); + } free_fmark(buf->b_last_cursor); free_fmark(buf->b_last_insert); free_fmark(buf->b_last_change); @@ -1988,12 +1993,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; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f217cba2af..2be5386de9 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -26,7 +26,7 @@ typedef struct file_buffer buf_T; // Forward declaration #include "nvim/eval_defs.h" // for proftime_T #include "nvim/profile.h" -// for String +// for String and Dictionary #include "nvim/api/private/defs.h" #define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma) @@ -748,6 +748,8 @@ struct file_buffer { signlist_T *b_signlist; /* list of signs to draw */ Terminal *terminal; // Terminal instance associated with the buffer + + Dictionary *additional_data; // Additional data from shada file if any. }; /* diff --git a/src/nvim/shada.c b/src/nvim/shada.c index aaa9f1d9c1..2d555faded 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -35,6 +35,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/globals.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/misc2.h" #include "nvim/ex_getln.h" #include "nvim/search.h" @@ -66,6 +67,10 @@ KHASH_MAP_INIT_STR(fnamebufs, buf_T *) #define emsgn(a, ...) emsgn((char_u *) a, __VA_ARGS__) #define home_replace_save(a, b) \ ((char *)home_replace_save(a, (char_u *)b)) +#define path_shorten_fname_if_possible(b) \ + ((char *)path_shorten_fname_if_possible((char_u *)b)) +#define buflist_new(ffname, sfname, ...) \ + (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) // From http://www.boost.org/doc/libs/1_43_0/boost/detail/endian.hpp + some // additional checks done after examining `{compiler} -dM -E - < /dev/null` @@ -180,7 +185,14 @@ typedef struct { char *sub; Array *additional_elements; } sub_string; - Array buffer_list; + struct buffer_list { + size_t size; + struct buffer_list_buffer { + pos_T pos; + char *fname; + Dictionary *additional_data; + } *buffers; + } buffer_list; } data; } ShadaEntry; @@ -617,7 +629,24 @@ static void shada_read(FILE *const fp, const int flags) break; } case kSDItemBufferList: { - // FIXME + if (!(flags & kShaDaWantInfo) || find_viminfo_parameter('%') == NULL + || ARGCOUNT != 0) { + shada_free_shada_entry(&cur_entry); + break; + } + for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { + char *const sfname = path_shorten_fname_if_possible( + cur_entry.data.buffer_list.buffers[i].fname); + buf_T *const buf = buflist_new( + cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, + BLN_LISTED); + if (buf != NULL) { + RESET_FMARK(&buf->b_last_cursor, + cur_entry.data.buffer_list.buffers[i].pos, 0); + buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum, + buf->b_last_cursor.mark.col, false); + } + } shada_free_shada_entry(&cur_entry); break; } @@ -1019,7 +1048,46 @@ static void shada_pack_entry(msgpack_packer *const packer, break; } case kSDItemBufferList: { - msgpack_rpc_from_array(entry.data.buffer_list, spacker); + msgpack_pack_array(spacker, entry.data.buffer_list.size); + for (size_t i = 0; i < entry.data.buffer_list.size; i++) { + const size_t map_size = (size_t) ( + 1 // Buffer name + // Line number: defaults to 1 + + (entry.data.buffer_list.buffers[i].pos.lnum != 1) + // Column number: defaults to 0 + + (entry.data.buffer_list.buffers[i].pos.col != 0) + // Additional entries, if any: + + (entry.data.buffer_list.buffers[i].additional_data == NULL + ? 0 + : entry.data.buffer_list.buffers[i].additional_data->size) + ); + msgpack_pack_map(spacker, map_size); + PACK_STATIC_STR("file"); + msgpack_rpc_from_string( + cstr_as_string(entry.data.buffer_list.buffers[i].fname), spacker); + if (entry.data.buffer_list.buffers[i].pos.lnum != 1) { + PACK_STATIC_STR("line"); + msgpack_pack_uint64( + spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.lnum); + } + if (entry.data.buffer_list.buffers[i].pos.col != 0) { + PACK_STATIC_STR("col"); + msgpack_pack_uint64( + spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.col); + } + if (entry.data.buffer_list.buffers[i].additional_data != NULL) { + for (size_t j = 0; + j < entry.data.buffer_list.buffers[i].additional_data->size; + j++) { + msgpack_rpc_from_string( + entry.data.buffer_list.buffers[i].additional_data->items[j].key, + spacker); + msgpack_rpc_from_object( + entry.data.buffer_list.buffers[i].additional_data->items[j].value, + spacker); + } + } + } break; } case kSDItemHeader: { @@ -1095,35 +1163,38 @@ static void shada_write(FILE *const newfp, FILE *const oldfp) // 2. Buffer list if (find_viminfo_parameter('%') != NULL) { - msgpack_pack_uint64(packer, (uint64_t) kSDItemBufferList); - msgpack_pack_uint64(packer, (uint64_t) os_time()); - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write); - size_t buf_count = 0; FOR_ALL_BUFFERS(buf) { if (buf->b_ffname != NULL && !SHADA_REMOVABLE(buf)) { buf_count++; } } - msgpack_pack_array(packer, buf_count); + + ShadaEntry buflist_entry = (ShadaEntry) { + .type = kSDItemBufferList, + .timestamp = os_time(), + .data = { + .buffer_list = { + .size = buf_count, + .buffers = xmalloc(buf_count + * sizeof(*buflist_entry.data.buffer_list.buffers)), + }, + }, + }; + size_t i = 0; FOR_ALL_BUFFERS(buf) { if (buf->b_ffname == NULL || SHADA_REMOVABLE(buf)) { continue; } - msgpack_pack_map(packer, 3); - PACK_STATIC_STR("fname"); - msgpack_rpc_from_string(cstr_as_string((char *) buf->b_ffname), packer); - PACK_STATIC_STR("lnum"); - msgpack_pack_uint64(packer, (uint64_t) buf->b_last_cursor.mark.lnum); - PACK_STATIC_STR("col"); - msgpack_pack_uint64(packer, (uint64_t) buf->b_last_cursor.mark.col); + buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) { + .pos = buf->b_last_cursor.mark, + .fname = (char *) buf->b_ffname, + .additional_data = buf->additional_data, + }; + i++; } - msgpack_pack_uint64(packer, (uint64_t) sbuf.size); - packer->callback(packer->data, sbuf.data, (unsigned) sbuf.size); - msgpack_packer_free(spacker); - msgpack_sbuffer_destroy(&sbuf); + shada_pack_entry(packer, buflist_entry, 0); + xfree(buflist_entry.data.buffer_list.buffers); } // 3. Jump list @@ -1460,7 +1531,15 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemBufferList: { - api_free_array(entry->data.buffer_list); + for (size_t i = 0; i < entry->data.buffer_list.size; i++) { + xfree(entry->data.buffer_list.buffers[i].fname); + if (entry->data.buffer_list.buffers[i].additional_data != NULL) { + api_free_dictionary( + *entry->data.buffer_list.buffers[i].additional_data); + xfree(entry->data.buffer_list.buffers[i].additional_data); + } + } + xfree(entry->data.buffer_list.buffers); break; } } @@ -1609,7 +1688,7 @@ shada_read_next_item_start: // First: manually unpack type, timestamp and length. // This is needed to avoid both seeking and having to maintain a buffer. - uint64_t type_u64; + uint64_t type_u64 = (uint64_t) kSDItemMissing; uint64_t timestamp_u64; uint64_t length_u64; @@ -2258,12 +2337,88 @@ shada_read_next_item_start: break; } case kSDItemBufferList: { - if (!msgpack_rpc_to_array(&(unpacked.data), &(entry->data.buffer_list))) { + if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { emsgn("Error while reading ShaDa file: " - "buffer list entry at position %" PRId64 " is not an array", + "buffer list entry at position %" PRId64 " " + "is not an array", (int64_t) initial_fpos); goto shada_read_next_item_error; } + entry->data.buffer_list = (struct buffer_list) { + .size = 0, + .buffers = NULL, + }; + if (unpacked.data.via.array.size == 0) { + break; + } + entry->data.buffer_list.buffers = + xcalloc(unpacked.data.via.array.size, + sizeof(*entry->data.buffer_list.buffers)); + for (size_t i = 0; i < unpacked.data.via.array.size; i++) { + entry->data.buffer_list.size++; + msgpack_unpacked unpacked_2 = (msgpack_unpacked) { + .data = unpacked.data.via.array.ptr[i], + }; + { + msgpack_unpacked unpacked = unpacked_2; + if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + emsgn("Error while reading ShaDa file: " + "buffer list at position %" PRId64 " " + "contains entry that is not a dictionary", + (int64_t) initial_fpos); + goto shada_read_next_item_error; + } + entry->data.buffer_list.buffers[i].pos.lnum = 1; + garray_T ad_ga; + ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + { + const size_t j = i; + { + for (size_t i = 0; i < unpacked.data.via.map.size; i++) { + CHECK_KEY_IS_STR("buffer list entry"); + LONG_KEY("buffer list entry", "line", + entry->data.buffer_list.buffers[j].pos.lnum) + else INTEGER_KEY("buffer list entry", "col", + entry->data.buffer_list.buffers[j].pos.col) + else STRING_KEY("buffer list entry", "file", + entry->data.buffer_list.buffers[j].fname) + else ADDITIONAL_KEY + } + } + } + if (entry->data.buffer_list.buffers[i].fname == NULL) { + emsgn("Error while reading ShaDa file: " + "buffer list at position %" PRId64 " " + "contains entry that does not have a file name", + (int64_t) initial_fpos); + ga_clear(&ad_ga); + goto shada_read_next_item_error; + } + if (ad_ga.ga_len) { + msgpack_object obj = { + .type = MSGPACK_OBJECT_MAP, + .via = { + .map = { + .size = (uint32_t) ad_ga.ga_len, + .ptr = ad_ga.ga_data, + } + } + }; + entry->data.buffer_list.buffers[i].additional_data = + xmalloc(sizeof(Dictionary)); + if (!msgpack_rpc_to_dictionary( + &obj, entry->data.buffer_list.buffers[i].additional_data)) { + emsgu("Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry that cannot be converted to a Dictionary", + (uint64_t) initial_fpos); + ga_clear(&ad_ga); + goto shada_read_next_item_error; + } + } + ga_clear(&ad_ga); + } + } break; } case kSDItemMissing: { @@ -2297,6 +2452,9 @@ shada_read_next_item_start: shada_read_next_item_error: msgpack_unpacked_destroy(&unpacked); msgpack_unpacker_free(unpacker); + entry->type = (ShadaEntryType) type_u64; + shada_free_shada_entry(entry); + entry->type = kSDItemMissing; return FAIL; shada_read_next_item_end: msgpack_unpacked_destroy(&unpacked); diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua new file mode 100644 index 0000000000..97dafa533b --- /dev/null +++ b/test/functional/shada/buffers_spec.lua @@ -0,0 +1,65 @@ +-- ShaDa buffer list saving/reading support +local helpers = require('test.functional.helpers') +local nvim, nvim_window, nvim_curwin, nvim_command, nvim_feed, nvim_eval, eq = + helpers.nvim, helpers.window, helpers.curwin, helpers.command, helpers.feed, + helpers.eval, helpers.eq + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +local nvim_current_line = function() + return nvim_window('get_cursor', nvim_curwin())[1] +end + +describe('ShaDa support code', function() + testfilename = 'Xtestfile-functional-shada-buffers' + testfilename_2 = 'Xtestfile-functional-shada-buffers-2' + before_each(reset) + after_each(clear) + + it('is able to dump and restore buffer list', function() + set_additional_cmd('set viminfo+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + -- nvim_command('redir! > /tmp/vistr | verbose set viminfo? | redir END') + -- nvim_command('wviminfo /tmp/foo') + nvim_command('qall') + reset() + -- nvim_command('call writefile([&viminfo], "/tmp/vistr")') + eq(3, nvim_eval('bufnr("$")')) + eq('', nvim_eval('bufname(1)')) + eq(testfilename, nvim_eval('bufname(2)')) + eq(testfilename_2, nvim_eval('bufname(3)')) + end) + + it('does not restore buffer list without % in &viminfo', function() + set_additional_cmd('set viminfo+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + -- nvim_command('redir! > /tmp/vistr | verbose set viminfo? | redir END') + -- nvim_command('wviminfo /tmp/foo') + set_additional_cmd('') + nvim_command('qall') + reset() + -- nvim_command('call writefile([&viminfo], "/tmp/vistr")') + eq(1, nvim_eval('bufnr("$")')) + eq('', nvim_eval('bufname(1)')) + end) + + it('does not dump buffer list without % in &viminfo', function() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + -- nvim_command('redir! > /tmp/vistr | verbose set viminfo? | redir END') + -- nvim_command('wviminfo /tmp/foo') + set_additional_cmd('set viminfo+=%') + nvim_command('qall') + reset() + -- nvim_command('call writefile([&viminfo], "/tmp/vistr")') + eq(1, nvim_eval('bufnr("$")')) + eq('', nvim_eval('bufname(1)')) + end) +end) |