diff options
Diffstat (limited to 'src')
75 files changed, 5694 insertions, 3139 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 487b554d6d..a473f99278 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -41,22 +41,25 @@ include_directories(${GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) -file(MAKE_DIRECTORY ${GENERATED_DIR}/os) -file(MAKE_DIRECTORY ${GENERATED_DIR}/api) -file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private) -file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc) -file(MAKE_DIRECTORY ${GENERATED_DIR}/tui) -file(MAKE_DIRECTORY ${GENERATED_DIR}/event) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/event) - -file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c - tui/*.c event/*.c) + +file(GLOB NEOVIM_SOURCES *.c) + +foreach(subdir + os + api + api/private + msgpack_rpc + tui + event + eval + ) + file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) + file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) + file(GLOB sources ${subdir}/*.c) + list(APPEND NEOVIM_SOURCES ${sources}) +endforeach() + file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) @@ -88,7 +91,6 @@ set(CONV_SOURCES mbyte.c memline.c message.c - misc1.c ops.c regexp.c screen.c diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c25a9789c5..55b535c78c 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -423,13 +423,16 @@ Object buffer_get_var(Buffer buffer, String name, Error *err) return dict_get_value(buf->b_vars, name, err); } -/// Sets a buffer-scoped (b:) variable. 'nil' value deletes the variable. +/// Sets a buffer-scoped (b:) variable /// /// @param buffer The buffer handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -438,7 +441,27 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(buf->b_vars, name, value, err); + return dict_set_value(buf->b_vars, name, value, false, err); +} + +/// Removes a buffer-scoped (b:) variable +/// +/// @param buffer The buffer handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object buffer_del_var(Buffer buffer, String name, Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(buf->b_vars, name, NIL, true, err); } /// Gets a buffer option value diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 6c8e324649..fbfa87d5ae 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -99,4 +99,3 @@ struct key_value_pair { #endif // NVIM_API_PRIVATE_DEFS_H - diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7a0b5191d7..db3e499427 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -90,14 +90,17 @@ Object dict_get_value(dict_T *dict, String key, Error *err) } /// Set a value in a dict. Objects are recursively expanded into their -/// vimscript equivalents. Passing 'nil' as value deletes the key. +/// vimscript equivalents. /// /// @param dict The vimscript dict /// @param key The key /// @param value The new value +/// @param del Delete key in place of setting it. Argument `value` is ignored in +/// this case. /// @param[out] err Details of an error that may have occurred /// @return the old value, if any -Object dict_set_value(dict_T *dict, String key, Object value, Error *err) +Object dict_set_value(dict_T *dict, String key, Object value, bool del, + Error *err) { Object rv = OBJECT_INIT; @@ -118,7 +121,7 @@ Object dict_set_value(dict_T *dict, String key, Object value, Error *err) dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size); - if (value.type == kObjectTypeNil) { + if (del) { // Delete the key if (di == NULL) { // Doesn't exist, fail @@ -397,13 +400,13 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) switch (obj.type) { case kObjectTypeNil: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = 0; + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = kSpecialVarNull; break; case kObjectTypeBoolean: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = obj.data.boolean; + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse; break; case kObjectTypeBuffer: @@ -651,6 +654,21 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) } switch (obj->v_type) { + case VAR_SPECIAL: + switch (obj->vval.v_special) { + case kSpecialVarTrue: + case kSpecialVarFalse: { + rv.type = kObjectTypeBoolean; + rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue); + break; + } + case kSpecialVarNull: { + rv.type = kObjectTypeNil; + break; + } + } + break; + case VAR_STRING: rv.type = kObjectTypeString; rv.data.string = cstr_to_string((char *) obj->vval.v_string); @@ -730,6 +748,10 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) } } break; + + case VAR_UNKNOWN: + case VAR_FUNC: + break; } return rv; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 126ee4072d..c8311b0aa0 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -54,13 +54,16 @@ Object tabpage_get_var(Tabpage tabpage, String name, Error *err) return dict_get_value(tab->tp_vars, name, err); } -/// Sets a tab-scoped (t:) variable. 'nil' value deletes the variable. +/// Sets a tab-scoped (t:) variable /// /// @param tabpage handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The tab page handle +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -69,7 +72,27 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(tab->tp_vars, name, value, err); + return dict_set_value(tab->tp_vars, name, value, false, err); +} + +/// Removes a tab-scoped (t:) variable +/// +/// @param tabpage handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +{ + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(tab->tp_vars, name, NIL, true, err); } /// Gets the current window in a tab page diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1c1822aa32..46ac3c9022 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -297,7 +297,7 @@ void vim_change_directory(String dir, Error *err) return; } - post_chdir(false); + post_chdir(kCdScopeGlobal); try_end(err); } @@ -337,15 +337,31 @@ Object vim_get_var(String name, Error *err) return dict_get_value(&globvardict, name, err); } -/// Sets a global variable. Passing 'nil' as value deletes the variable. +/// Sets a global variable /// /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return the old value if any +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object vim_set_var(String name, Object value, Error *err) { - return dict_set_value(&globvardict, name, value, err); + return dict_set_value(&globvardict, name, value, false, err); +} + +/// Removes a global variable +/// +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object vim_del_var(String name, Error *err) +{ + return dict_set_value(&globvardict, name, NIL, true, err); } /// Gets a vim variable diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index aad616c7bf..a52f53a3e6 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -197,13 +197,16 @@ Object window_get_var(Window window, String name, Error *err) return dict_get_value(win->w_vars, name, err); } -/// Sets a window-scoped (w:) variable. 'nil' value deletes the variable. +/// Sets a window-scoped (w:) variable /// /// @param window The window handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object window_set_var(Window window, String name, Object value, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -212,7 +215,27 @@ Object window_set_var(Window window, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(win->w_vars, name, value, err); + return dict_set_value(win->w_vars, name, value, false, err); +} + +/// Removes a window-scoped (w:) variable +/// +/// @param window The window handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object window_del_var(Window window, String name, Error *err) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(win->w_vars, name, NIL, true, err); } /// Gets a window option value diff --git a/src/nvim/assert.h b/src/nvim/assert.h index 3a900aca65..0ce48e4766 100644 --- a/src/nvim/assert.h +++ b/src/nvim/assert.h @@ -8,17 +8,32 @@ // defined(__has_feature) && __has_feature(...). Therefore we define Clang's // __has_feature and __has_extension macro's before referring to them. #ifndef __has_feature - #define __has_feature(x) 0 +# define __has_feature(x) 0 #endif #ifndef __has_extension - #define __has_extension __has_feature +# define __has_extension __has_feature #endif -/// STATIC_ASSERT(condition, message) - assert at compile time if !cond +/// @def STATIC_ASSERT +/// @brief Assert at compile time if condition is not satisfied. /// -/// example: -/// STATIC_ASSERT(sizeof(void *) == 8, "need 64-bits mode"); +/// Should be put on its own line, followed by a semicolon. +/// +/// Example: +/// +/// STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode"); +/// +/// @param[in] condition Condition to check, should be an integer constant +/// expression. +/// @param[in] message Message which will be given if check fails. + +/// @def STATIC_ASSERT_EXPR +/// @brief Like #STATIC_ASSERT, but can be used where expressions are used. +/// +/// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message +/// given in this case is not very nice with the current implementation though +/// and `message` argument is ignored. // define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is // detected or the compiler is known to support it. Note that Clang in C99 @@ -29,50 +44,74 @@ // clearer messages we get from _Static_assert, we suppress the warnings // temporarily. +#define STATIC_ASSERT_PRAGMA_START +#define STATIC_ASSERT_PRAGMA_END +#define STATIC_ASSERT(...) \ + do { \ + STATIC_ASSERT_PRAGMA_START \ + STATIC_ASSERT_STATEMENT(__VA_ARGS__); \ + STATIC_ASSERT_PRAGMA_END \ + } while (0) + // the easiest case, when the mode is C11 (generic compiler) or Clang // advertises explicit support for c_static_assert, meaning it won't warn. #if __STDC_VERSION__ >= 201112L || __has_feature(c_static_assert) - #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) // if we're dealing with gcc >= 4.6 in C99 mode, we can still use // _Static_assert but we need to suppress warnings, this is pretty ugly. #elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) - #define STATIC_ASSERT(cond, msg) \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-pedantic\"") \ - _Static_assert(cond, msg); \ - _Pragma("GCC diagnostic pop") \ + +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) + +# undef STATIC_ASSERT_PRAGMA_START +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-pedantic\"") \ + +# undef STATIC_ASSERT_PRAGMA_END +# define STATIC_ASSERT_PRAGMA_END \ + _Pragma("GCC diagnostic pop") \ // the same goes for clang in C99 mode, but we suppress a different warning #elif defined(__clang__) && __has_extension(c_static_assert) - #define STATIC_ASSERT(cond, msg) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \ - _Static_assert(cond, msg); \ - _Pragma("clang diagnostic pop") \ + +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) + +# undef STATIC_ASSERT_PRAGMA_START +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \ + +# undef STATIC_ASSERT_PRAGMA_END +# define STATIC_ASSERT_PRAGMA_END \ + _Pragma("clang diagnostic pop") \ // TODO(aktau): verify that this works, don't have MSVC on hand. #elif _MSC_VER >= 1600 - #define STATIC_ASSERT(cond, msg) static_assert(cond, msg) + +# define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg) // fallback for compilers that don't support _Static_assert or static_assert // not as pretty but gets the job done. Credit goes to Pádraig Brady and // contributors. #else - #define ASSERT_CONCAT_(a, b) a##b - #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) - // These can't be used after statements in c89. - #ifdef __COUNTER__ - #define STATIC_ASSERT(e,m) \ - { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; } - #else - // This can't be used twice on the same line so ensure if using in headers - // that the headers are not included twice (by wrapping in #ifndef...#endif) - // Note it doesn't cause an issue when used on same line of separate modules - // compiled with gcc -combine -fwhole-program. - #define STATIC_ASSERT(e,m) \ - { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; } - #endif +# define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR +#endif + +#define ASSERT_CONCAT_(a, b) a##b +#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) +// These can't be used after statements in c89. +#ifdef __COUNTER__ +# define STATIC_ASSERT_EXPR(e, m) \ + ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }) 0) +#else +// This can't be used twice on the same line so ensure if using in headers +// that the headers are not included twice (by wrapping in #ifndef...#endif) +// Note it doesn't cause an issue when used on same line of separate modules +// compiled with gcc -combine -fwhole-program. +# define STATIC_ASSERT_EXPR(e, m) \ + ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }) 0) #endif #endif // NVIM_ASSERT_H diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c514c4378e..529d0889bd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1553,6 +1553,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_ep); clear_string_option(&buf->b_p_path); clear_string_option(&buf->b_p_tags); + clear_string_option(&buf->b_p_tc); clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_tsr); clear_string_option(&buf->b_p_qe); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 86e63eb52c..c0cd801cd4 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -666,19 +666,21 @@ struct file_buffer { long b_p_wm_nopaste; ///< b_p_wm saved for paste mode char_u *b_p_keymap; ///< 'keymap' - /* local values for options which are normally global */ - char_u *b_p_gp; /* 'grepprg' local value */ - char_u *b_p_mp; /* 'makeprg' local value */ - char_u *b_p_efm; /* 'errorformat' local value */ - char_u *b_p_ep; /* 'equalprg' local value */ - char_u *b_p_path; /* 'path' local value */ - int b_p_ar; /* 'autoread' local value */ - char_u *b_p_tags; /* 'tags' local value */ - char_u *b_p_dict; /* 'dictionary' local value */ - char_u *b_p_tsr; /* 'thesaurus' local value */ - long b_p_ul; /* 'undolevels' local value */ - int b_p_udf; /* 'undofile' */ - char_u *b_p_lw; // 'lispwords' local value + // local values for options which are normally global + char_u *b_p_gp; ///< 'grepprg' local value + char_u *b_p_mp; ///< 'makeprg' local value + char_u *b_p_efm; ///< 'errorformat' local value + char_u *b_p_ep; ///< 'equalprg' local value + char_u *b_p_path; ///< 'path' local value + int b_p_ar; ///< 'autoread' local value + char_u *b_p_tags; ///< 'tags' local value + char_u *b_p_tc; ///< 'tagcase' local value + unsigned b_tc_flags; ///< flags for 'tagcase' + char_u *b_p_dict; ///< 'dictionary' local value + char_u *b_p_tsr; ///< 'thesaurus' local value + long b_p_ul; ///< 'undolevels' local value + int b_p_udf; ///< 'undofile' + char_u *b_p_lw; ///< 'lispwords' local value /* end of buffer options */ @@ -816,10 +818,12 @@ struct tabpage_S { was set */ diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); - int tp_diff_invalid; /* list of diffs is outdated */ - frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */ - dictitem_T tp_winvar; /* variable for "t:" Dictionary */ - dict_T *tp_vars; /* internal variables, local to tab page */ + int tp_diff_invalid; ///< list of diffs is outdated */ + frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots + dictitem_T tp_winvar; ///< variable for "t:" Dictionary + dict_T *tp_vars; ///< internal variables, local to tab page + char_u *localdir; ///< Absolute path of local directory or + ///< NULL }; /* @@ -953,16 +957,14 @@ struct window_S { time through cursupdate() to the current virtual column */ - /* - * the next six are used to update the visual part - */ - char w_old_visual_mode; /* last known VIsual_mode */ - linenr_T w_old_cursor_lnum; /* last known end of visual part */ - colnr_T w_old_cursor_fcol; /* first column for block visual part */ - colnr_T w_old_cursor_lcol; /* last column for block visual part */ - linenr_T w_old_visual_lnum; /* last known start of visual part */ - colnr_T w_old_visual_col; /* last known start of visual part */ - colnr_T w_old_curswant; /* last known value of Curswant */ + // the next seven are used to update the visual part + char w_old_visual_mode; ///< last known VIsual_mode + linenr_T w_old_cursor_lnum; ///< last known end of visual part + colnr_T w_old_cursor_fcol; ///< first column for block visual part + colnr_T w_old_cursor_lcol; ///< last column for block visual part + linenr_T w_old_visual_lnum; ///< last known start of visual part + colnr_T w_old_visual_col; ///< last known start of visual part + colnr_T w_old_curswant; ///< last known value of Curswant /* * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1149ca1e62..4826e70727 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -29,7 +29,6 @@ #include "nvim/path.h" #include "nvim/screen.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" @@ -658,9 +657,9 @@ void ex_diffupdate(exarg_T *eap) } // We need three temp file names. - char_u *tmp_orig = vim_tempname(); - char_u *tmp_new = vim_tempname(); - char_u *tmp_diff = vim_tempname(); + char *tmp_orig = (char *) vim_tempname(); + char *tmp_new = (char *) vim_tempname(); + char *tmp_diff = (char *) vim_tempname(); if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) { goto theend; @@ -670,11 +669,11 @@ void ex_diffupdate(exarg_T *eap) // are no differences. Can't use the return value, it's non-zero when // there are differences. // May try twice, first with "-a" and then without. - int io_error = FALSE; - int ok = FALSE; + int io_error = false; + bool ok = false; for (;;) { - ok = FALSE; - FILE *fd = mch_fopen((char *)tmp_orig, "w"); + ok = false; + FILE *fd = mch_fopen(tmp_orig, "w"); if (fd == NULL) { io_error = TRUE; @@ -683,7 +682,7 @@ void ex_diffupdate(exarg_T *eap) io_error = TRUE; } fclose(fd); - fd = mch_fopen((char *)tmp_new, "w"); + fd = mch_fopen(tmp_new, "w"); if (fd == NULL) { io_error = TRUE; @@ -693,7 +692,7 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); diff_file(tmp_orig, tmp_new, tmp_diff); - fd = mch_fopen((char *)tmp_diff, "r"); + fd = mch_fopen(tmp_diff, "r"); if (fd == NULL) { io_error = TRUE; @@ -712,10 +711,10 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); } - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); + os_remove(tmp_diff); + os_remove(tmp_new); } - os_remove((char *)tmp_orig); + os_remove(tmp_orig); } // When using 'diffexpr' break here. @@ -756,7 +755,7 @@ void ex_diffupdate(exarg_T *eap) // Write the first buffer to a tempfile. buf_T *buf = curtab->tp_diffbuf[idx_orig]; - if (diff_write(buf, tmp_orig) == FAIL) { + if (diff_write(buf, (char_u *) tmp_orig) == FAIL) { goto theend; } @@ -767,17 +766,17 @@ void ex_diffupdate(exarg_T *eap) continue; // skip buffer that isn't loaded } - if (diff_write(buf, tmp_new) == FAIL) { + if (diff_write(buf, (char_u *) tmp_new) == FAIL) { continue; } diff_file(tmp_orig, tmp_new, tmp_diff); // Read the diff output and add each entry to the diff list. - diff_read(idx_orig, idx_new, tmp_diff); - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); + diff_read(idx_orig, idx_new, (char_u *) tmp_diff); + os_remove(tmp_diff); + os_remove(tmp_new); } - os_remove((char *)tmp_orig); + os_remove(tmp_orig); // force updating cursor position on screen curwin->w_valid_cursor.lnum = 0; @@ -795,15 +794,16 @@ theend: /// @param tmp_orig /// @param tmp_new /// @param tmp_diff -static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) +static void diff_file(const char *const tmp_orig, const char *const tmp_new, + const char *const tmp_diff) { if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); } else { - size_t len = STRLEN(tmp_orig) + STRLEN(tmp_new) + STRLEN(tmp_diff) - + STRLEN(p_srr) + 27; - char_u *cmd = xmalloc(len); + const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + + STRLEN(p_srr) + 27); + char *const cmd = xmalloc(len); /* We don't want $DIFF_OPTIONS to get in the way. */ if (os_getenv("DIFF_OPTIONS")) { @@ -813,19 +813,17 @@ static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) /* Build the diff command and execute it. Always use -a, binary * differences are of no use. Ignore errors, diff returns * non-zero when differences have been found. */ - vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s", - diff_a_works == FALSE ? "" : "-a ", + vim_snprintf(cmd, len, "diff %s%s%s%s%s %s", + diff_a_works ? "-a " : "", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", (diff_flags & DIFF_ICASE) ? "-i " : "", tmp_orig, tmp_new); - append_redir(cmd, len, p_srr, tmp_diff); + append_redir(cmd, len, (char *) p_srr, tmp_diff); block_autocmds(); // Avoid ShellCmdPost stuff - (void)call_shell( - cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL - ); + (void)call_shell((char_u *) cmd, + kShellOptFilter | kShellOptSilent | kShellOptDoOut, + NULL); unblock_autocmds(); xfree(cmd); } @@ -902,9 +900,11 @@ void ex_diffpatch(exarg_T *eap) if (*p_pex != NUL) { // Use 'patchexpr' to generate the new file. #ifdef UNIX - eval_patch(tmp_orig, fullname != NULL ? fullname : eap->arg, tmp_new); + eval_patch((char *) tmp_orig, + (char *) (fullname != NULL ? fullname : eap->arg), + (char *) tmp_new); #else - eval_patch(tmp_orig, eap->arg, tmp_new); + eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new); #endif // ifdef UNIX } else { // Build the patch command and execute it. Ignore errors. Switch to diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 614a5d43be..667ce1e779 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -199,7 +199,7 @@ typedef struct insert_state { int did_restart_edit; // remember if insert mode was restarted // after a ctrl+o bool nomove; - uint8_t *ptr; + char_u *ptr; } InsertState; @@ -270,8 +270,8 @@ static void insert_enter(InsertState *s) s->ptr = (char_u *)"i"; } - set_vim_var_string(VV_INSERTMODE, s->ptr, 1); - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ + set_vim_var_string(VV_INSERTMODE, (char *) s->ptr, 1); + set_vim_var_string(VV_CHAR, NULL, -1); apply_autocmds(EVENT_INSERTENTER, NULL, NULL, false, curbuf); // Make sure the cursor didn't move. Do call check_cursor_col() in @@ -1815,7 +1815,7 @@ static bool del_char_after_col(int limit_col) if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) { return false; } - del_bytes((long)(ecol - curwin->w_cursor.col), false, true); + del_bytes(ecol - curwin->w_cursor.col, false, true); } else { del_char(false); } @@ -7239,15 +7239,15 @@ static void ins_insert(int replaceState) return; } - set_vim_var_string(VV_INSERTMODE, - (char_u *)((State & REPLACE_FLAG) ? "i" : - replaceState == VREPLACE ? "v" : - "r"), 1); - apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf); - if (State & REPLACE_FLAG) + set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" : + replaceState == VREPLACE ? "v" : + "r"), 1); + apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, false, curbuf); + if (State & REPLACE_FLAG) { State = INSERT | (State & LANGMAP); - else + } else { State = replaceState | (State & LANGMAP); + } AppendCharToRedobuff(K_INS); showmode(); ui_cursor_shape(); /* may show different cursor shape */ @@ -7741,6 +7741,8 @@ static void ins_mousescroll(int dir) (long)(curwin->w_botline - curwin->w_topline)); else scroll_redraw(dir, 3L); + } else { + mouse_scroll_horiz(dir); } did_scroll = TRUE; } @@ -8480,22 +8482,22 @@ static colnr_T get_nolist_virtcol(void) */ static char_u *do_insert_char_pre(int c) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; // Return quickly when there is nothing to do. if (!has_event(EVENT_INSERTCHARPRE)) { return NULL; } if (has_mbyte) { - buf[(*mb_char2bytes)(c, buf)] = NUL; + buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL; } else { buf[0] = c; buf[1] = NUL; } - /* Lock the text to avoid weird things from happening. */ - ++textlock; - set_vim_var_string(VV_CHAR, buf, -1); /* set v:char */ + // Lock the text to avoid weird things from happening. + textlock++; + set_vim_var_string(VV_CHAR, buf, -1); char_u *res = NULL; if (apply_autocmds(EVENT_INSERTCHARPRE, NULL, NULL, FALSE, curbuf)) { @@ -8506,8 +8508,8 @@ static char_u *do_insert_char_pre(int c) res = vim_strsave(get_vim_var_str(VV_CHAR)); } - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ - --textlock; + set_vim_var_string(VV_CHAR, NULL, -1); + textlock--; return res; } diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 0289b2c3a6..0d61f26bcc 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -36,12 +36,6 @@ typedef int (*IndentGetter)(void); #define INSCHAR_NO_FEX 8 /* don't use 'formatexpr' */ #define INSCHAR_COM_LIST 16 /* format comments with list/2nd line indent */ -/* direction for nv_mousescroll() and ins_mousescroll() */ -#define MSCR_DOWN 0 /* DOWN must be FALSE */ -#define MSCR_UP 1 -#define MSCR_LEFT -1 -#define MSCR_RIGHT -2 - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "edit.h.generated.h" #endif diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a72f17a2b9..8b04d9afa4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -66,13 +66,14 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/mouse.h" #include "nvim/terminal.h" #include "nvim/undo.h" #include "nvim/version.h" #include "nvim/window.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/decode.h" #include "nvim/os/os.h" #include "nvim/event/libuv_process.h" #include "nvim/event/pty_process.h" @@ -87,7 +88,6 @@ #include "nvim/os/dl.h" #include "nvim/os/input.h" #include "nvim/event/loop.h" -#include "nvim/lib/kvec.h" #include "nvim/lib/queue.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -142,13 +142,6 @@ typedef struct lval_S { char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ } lval_T; -/// Structure defining state for read_from_list() -typedef struct { - const listitem_T *li; ///< Item currently read. - size_t offset; ///< Byte offset inside the read item. - size_t li_length; ///< Length of the string inside the read item. -} ListReaderState; - static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_listidx = N_("E684: list index out of range: %" PRId64); @@ -185,17 +178,7 @@ static dictitem_T globvars_var; /* variable used for g: */ */ static hashtab_T compat_hashtab; -/* - * When recursively copying lists and dicts we need to remember which ones we - * have done to avoid endless recursiveness. This unique ID is used for that. - * The last bit is used for previous_funccal, ignored when comparing. - */ -static int current_copyID = 0; -#define COPYID_INC 2 -#define COPYID_MASK (~0x1) - -/// Abort conversion to string after a recursion error. -static bool did_echo_string_emsg = false; +hashtab_T func_hashtab; /* * Array to hold the hashtab with variables local to each sourced script. @@ -298,94 +281,112 @@ typedef enum { #define VV_RO 2 /* read-only */ #define VV_RO_SBX 4 /* read-only in the sandbox */ -#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0} +#define VV(idx, name, type, flags) \ + [idx] = { \ + .vv_name = name, \ + .vv_di = { \ + .di_tv = { .v_type = type }, \ + .di_flags = 0, \ + }, \ + .vv_filler = { 0 }, \ + .vv_flags = flags, \ + } // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { - char *vv_name; /* name of variable, without v: */ - dictitem_T vv_di; /* value and name for key */ - char vv_filler[16]; /* space for LONGEST name below!!! */ - char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */ -} vimvars[VV_LEN] = -{ - /* - * The order here must match the VV_ defines in eval.h! - * Initializing a union does not work, leave tv.vval empty to get zero's. - */ - { VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO }, - { VV_NAME("count1", VAR_NUMBER), VV_RO }, - { VV_NAME("prevcount", VAR_NUMBER), VV_RO }, - { VV_NAME("errmsg", VAR_STRING), VV_COMPAT }, - { VV_NAME("warningmsg", VAR_STRING), 0 }, - { VV_NAME("statusmsg", VAR_STRING), 0 }, - { VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO }, - { VV_NAME("this_session", VAR_STRING), VV_COMPAT }, - { VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO }, - { VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX }, - { VV_NAME("termresponse", VAR_STRING), VV_RO }, - { VV_NAME("fname", VAR_STRING), VV_RO }, - { VV_NAME("lang", VAR_STRING), VV_RO }, - { VV_NAME("lc_time", VAR_STRING), VV_RO }, - { VV_NAME("ctype", VAR_STRING), VV_RO }, - { VV_NAME("charconvert_from", VAR_STRING), VV_RO }, - { VV_NAME("charconvert_to", VAR_STRING), VV_RO }, - { VV_NAME("fname_in", VAR_STRING), VV_RO }, - { VV_NAME("fname_out", VAR_STRING), VV_RO }, - { VV_NAME("fname_new", VAR_STRING), VV_RO }, - { VV_NAME("fname_diff", VAR_STRING), VV_RO }, - { VV_NAME("cmdarg", VAR_STRING), VV_RO }, - { VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX }, - { VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX }, - { VV_NAME("folddashes", VAR_STRING), VV_RO_SBX }, - { VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX }, - { VV_NAME("progname", VAR_STRING), VV_RO }, - { VV_NAME("servername", VAR_STRING), VV_RO }, - { VV_NAME("dying", VAR_NUMBER), VV_RO }, - { VV_NAME("exception", VAR_STRING), VV_RO }, - { VV_NAME("throwpoint", VAR_STRING), VV_RO }, - { VV_NAME("register", VAR_STRING), VV_RO }, - { VV_NAME("cmdbang", VAR_NUMBER), VV_RO }, - { VV_NAME("insertmode", VAR_STRING), VV_RO }, - { VV_NAME("val", VAR_UNKNOWN), VV_RO }, - { VV_NAME("key", VAR_UNKNOWN), VV_RO }, - { VV_NAME("profiling", VAR_NUMBER), VV_RO }, - { VV_NAME("fcs_reason", VAR_STRING), VV_RO }, - { VV_NAME("fcs_choice", VAR_STRING), 0 }, - { VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO }, - { VV_NAME("beval_winnr", VAR_NUMBER), VV_RO }, - { VV_NAME("beval_lnum", VAR_NUMBER), VV_RO }, - { VV_NAME("beval_col", VAR_NUMBER), VV_RO }, - { VV_NAME("beval_text", VAR_STRING), VV_RO }, - { VV_NAME("scrollstart", VAR_STRING), 0 }, - { VV_NAME("swapname", VAR_STRING), VV_RO }, - { VV_NAME("swapchoice", VAR_STRING), 0 }, - { VV_NAME("swapcommand", VAR_STRING), VV_RO }, - { VV_NAME("char", VAR_STRING), 0 }, - { VV_NAME("mouse_win", VAR_NUMBER), 0 }, - { VV_NAME("mouse_lnum", VAR_NUMBER), 0 }, - { VV_NAME("mouse_col", VAR_NUMBER), 0 }, - { VV_NAME("operator", VAR_STRING), VV_RO }, - { VV_NAME("searchforward", VAR_NUMBER), 0 }, - { VV_NAME("hlsearch", VAR_NUMBER), 0 }, - { VV_NAME("oldfiles", VAR_LIST), 0 }, - { VV_NAME("windowid", VAR_NUMBER), VV_RO }, - { VV_NAME("progpath", VAR_STRING), VV_RO }, - { VV_NAME("command_output", VAR_STRING), 0 }, - { VV_NAME("completed_item", VAR_DICT), VV_RO }, - { VV_NAME("option_new", VAR_STRING), VV_RO }, - { VV_NAME("option_old", VAR_STRING), VV_RO }, - { VV_NAME("option_type", VAR_STRING), VV_RO }, - { VV_NAME("errors", VAR_LIST), 0 }, - { VV_NAME("msgpack_types", VAR_DICT), VV_RO }, - { VV_NAME("event", VAR_DICT), VV_RO }, + char *vv_name; ///< Name of the variable, without v:. + dictitem_T vv_di; ///< Value of the variable, with name. + char vv_filler[16]; ///< Space for longest name from below. + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. +} vimvars[] = +{ + // VV_ tails differing from upcased string literals: + // VV_CC_FROM "charconvert_from" + // VV_CC_TO "charconvert_to" + // VV_SEND_SERVER "servername" + // VV_REG "register" + // VV_OP "operator" + VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_FALSE, "false", VAR_SPECIAL, VV_RO), + VV(VV_TRUE, "true", VAR_SPECIAL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), }; +#undef VV /* shorthand */ #define vv_type vv_di.di_tv.v_type #define vv_nr vv_di.di_tv.vval.v_number +#define vv_special vv_di.di_tv.vval.v_special #define vv_float vv_di.di_tv.vval.v_float #define vv_str vv_di.di_tv.vval.v_string #define vv_list vv_di.di_tv.vval.v_list @@ -419,29 +420,6 @@ typedef struct dict_watcher { bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; -/// Structure representing current VimL to messagepack conversion state -typedef struct { - enum { - kMPConvDict, ///< Convert dict_T *dictionary. - kMPConvList, ///< Convert list_T *list. - kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. - } type; - union { - struct { - dict_T *dict; ///< Currently converted dictionary. - hashitem_T *hi; ///< Currently converted dictionary item. - size_t todo; ///< Amount of items left to process. - } d; ///< State of dictionary conversion. - struct { - list_T *list; ///< Currently converted list. - listitem_T *li; ///< Currently converted list item. - } l; ///< State of list or generic mapping conversion. - } data; ///< Data to convert. -} MPConvStackVal; - -/// Stack used to convert VimL values to messagepack. -typedef kvec_t(MPConvStackVal) MPConvStack; - typedef struct { TerminalJobData *data; ufunc_T *callback; @@ -460,17 +438,6 @@ typedef struct { static uint64_t current_job_id = 1; static PMap(uint64_t) *jobs = NULL; -typedef enum { - kMPNil, - kMPBoolean, - kMPInteger, - kMPFloat, - kMPString, - kMPBinary, - kMPArray, - kMPMap, - kMPExt, -} MessagePackType; static const char *const msgpack_type_names[] = { [kMPNil] = "nil", [kMPBoolean] = "boolean", @@ -482,7 +449,7 @@ static const char *const msgpack_type_names[] = { [kMPMap] = "map", [kMPExt] = "ext", }; -static const list_T *msgpack_type_lists[] = { +const list_T *eval_msgpack_type_lists[] = { [kMPNil] = NULL, [kMPBoolean] = NULL, [kMPInteger] = NULL, @@ -499,8 +466,9 @@ static const list_T *msgpack_type_lists[] = { */ void eval_init(void) { + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + jobs = pmap_new(uint64_t)(); - int i; struct vimvar *p; init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); @@ -509,7 +477,7 @@ void eval_init(void) hash_init(&compat_hashtab); hash_init(&func_hashtab); - for (i = 0; i < VV_LEN; ++i) { + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) @@ -538,7 +506,7 @@ void eval_init(void) .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; - msgpack_type_lists[i] = type_list; + eval_msgpack_type_lists[i] = type_list; if (dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); @@ -555,7 +523,12 @@ void eval_init(void) set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); - set_reg_var(0); /* default for v:register is not 0 but '"' */ + + set_vim_var_special(VV_FALSE, kSpecialVarFalse); + set_vim_var_special(VV_TRUE, kSpecialVarTrue); + set_vim_var_special(VV_NULL, kSpecialVarNull); + + set_reg_var(0); // default for v:register is not 0 but '"' } #if defined(EXITFREE) @@ -563,7 +536,7 @@ void eval_clear(void) { struct vimvar *p; - for (int i = 0; i < VV_LEN; ++i) { + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; if (p->vv_di.di_tv.v_type == VAR_STRING) { xfree(p->vv_str); @@ -798,45 +771,50 @@ void var_redir_stop(void) redir_varname = NULL; } -int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to) +int eval_charconvert(const char *const enc_from, const char *const enc_to, + const char *const fname_from, const char *const fname_to) { - int err = FALSE; + int err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); set_vim_var_string(VV_FNAME_IN, fname_from, -1); set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - if (eval_to_bool(p_ccv, &err, NULL, FALSE)) - err = TRUE; + if (eval_to_bool(p_ccv, &err, NULL, false)) { + err = true; + } set_vim_var_string(VV_CC_FROM, NULL, -1); set_vim_var_string(VV_CC_TO, NULL, -1); set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); - if (err) + if (err) { return FAIL; + } return OK; } -int eval_printexpr(char_u *fname, char_u *args) +int eval_printexpr(const char *const fname, const char *const args) { - int err = FALSE; + int err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) - err = TRUE; + if (eval_to_bool(p_pexpr, &err, NULL, false)) { + err = true; + } set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_CMDARG, NULL, -1); if (err) { - os_remove((char *)fname); + os_remove(fname); return FAIL; } return OK; } -void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile) +void eval_diff(const char *const origfile, const char *const newfile, + const char *const outfile) { int err = FALSE; @@ -849,7 +827,8 @@ void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile) set_vim_var_string(VV_FNAME_OUT, NULL, -1); } -void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile) +void eval_patch(const char *const origfile, const char *const difffile, + const char *const outfile) { int err; @@ -1733,7 +1712,7 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) } else { int c; - char_u *s = (char_u *) echo_string(&tv, NULL); + char_u *s = (char_u *) encode_tv2echo(&tv, NULL); c = *arg; *arg = NUL; list_one_var_a((char_u *)"", @@ -2417,11 +2396,12 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) char_u numbuf[NUMBUFLEN]; char_u *s; - /* Can't do anything with a Funcref or a Dict on the right. */ + // Can't do anything with a Funcref, a Dict or special value on the right. if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { switch (tv1->v_type) { case VAR_DICT: case VAR_FUNC: + case VAR_SPECIAL: break; case VAR_LIST: @@ -2489,6 +2469,9 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) tv1->vval.v_float -= f; } return OK; + + case VAR_UNKNOWN: + assert(false); } } @@ -3126,6 +3109,15 @@ static void item_lock(typval_T *tv, int deep, int lock) } } } + break; + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_SPECIAL: + break; + case VAR_UNKNOWN: + assert(false); } --recurse; } @@ -3204,7 +3196,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) static size_t bdone; static size_t wdone; static size_t tdone; - static int vidx; + static size_t vidx; static hashitem_T *hi; hashtab_T *ht; @@ -3266,9 +3258,10 @@ char_u *get_user_var_name(expand_T *xp, int idx) return cat_prefix_varname('t', hi->hi_key); } - /* v: variables */ - if (vidx < VV_LEN) + // v: variables + if (vidx < ARRAY_SIZE(vimvars)) { return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name); + } xfree(varnamebuf); varnamebuf = NULL; @@ -4158,7 +4151,7 @@ static int eval7( if (get_float) { float_T f; - *arg += string2float(*arg, &f); + *arg += string2float((char *) *arg, &f); if (evaluate) { rettv->v_type = VAR_FLOAT; rettv->vval.v_float = f; @@ -4347,14 +4340,37 @@ eval_index ( char_u *s; char_u *key = NULL; - if (rettv->v_type == VAR_FUNC) { - if (verbose) - EMSG(_("E695: Cannot index a Funcref")); - return FAIL; - } else if (rettv->v_type == VAR_FLOAT) { - if (verbose) - EMSG(_(e_float_as_string)); - return FAIL; + switch (rettv->v_type) { + case VAR_FUNC: { + if (verbose) { + EMSG(_("E695: Cannot index a Funcref")); + } + return FAIL; + } + case VAR_FLOAT: { + if (verbose) { + EMSG(_(e_float_as_string)); + } + return FAIL; + } + case VAR_SPECIAL: { + if (verbose) { + EMSG(_("E909: Cannot index a special variable")); + } + return FAIL; + } + case VAR_UNKNOWN: { + if (evaluate) { + return FAIL; + } + // fallthrough + } + case VAR_STRING: + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: { + break; + } } init_tv(&var1); @@ -4545,6 +4561,11 @@ eval_index ( *rettv = var1; } break; + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; // Not evaluating, skipping over subscript } } @@ -5090,10 +5111,18 @@ tv_equal ( s1 = get_tv_string_buf(tv1, buf1); s2 = get_tv_string_buf(tv2, buf2); return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; + + case VAR_SPECIAL: + return tv1->vval.v_special == tv2->vval.v_special; + + case VAR_UNKNOWN: + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does + // not equal anything, not even self. + return false; } - EMSG2(_(e_intern2), "tv_equal()"); - return TRUE; + assert(false); + return false; } /* @@ -5494,9 +5523,10 @@ static int list_join_inner(garray_T *const gap, list_T *const l, for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { char *s; size_t len; - s = echo_string(&item->li_tv, &len); - if (s == NULL) + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { return FAIL; + } sumlen += (int) len; @@ -5504,9 +5534,6 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *) s; line_breakcheck(); - if (did_echo_string_emsg) { // recursion error, bail out - break; - } } /* Allocate result buffer with its total size, avoid re-allocation and @@ -5558,6 +5585,22 @@ static int list_join(garray_T *const gap, list_T *const l, return retval; } +/// Get next (unique) copy ID +/// +/// Used for traversing nested structures e.g. when serializing them or garbage +/// collecting. +int get_copyID(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // CopyID for recursively traversing lists and dicts + // + // This value is needed to avoid endless recursiveness. Last bit is used for + // previous_funccal and normally ignored when comparing. + static int current_copyID = 0; + current_copyID += COPYID_INC; + return current_copyID; +} + /* * Garbage collection for lists and dictionaries. * @@ -5593,8 +5636,7 @@ bool garbage_collect(void) // We advance by two because we add one for items referenced through // previous_funccal. - current_copyID += COPYID_INC; - int copyID = current_copyID; + const int copyID = get_copyID(); // 1. Go through all accessible variables and mark all lists and dicts // with copyID. @@ -5939,6 +5981,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, } break; } + + case VAR_FUNC: + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_NUMBER: + case VAR_STRING: { + break; + } } return abort; } @@ -6530,596 +6581,22 @@ failret: return OK; } -#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ - do { \ - if ((val)->copyID_attr == copyID) { \ - CONV_RECURSE((val), conv_type); \ - } \ - (val)->copyID_attr = copyID; \ - } while (0) - -/// Define functions which convert VimL value to something else -/// -/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const -/// tv)` which returns OK or FAIL and helper functions. -/// -/// @param firstargtype Type of the first argument. It will be used to return -/// the results. -/// @param firstargname Name of the first argument. -/// @param name Name of the target converter. -#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ -static int name##_convert_one_value(firstargtype firstargname, \ - MPConvStack *const mpstack, \ - typval_T *const tv, \ - const int copyID, \ - const char *const objname) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - switch (tv->v_type) { \ - case VAR_STRING: { \ - CONV_STRING(tv->vval.v_string, STRLEN(tv->vval.v_string)); \ - break; \ - } \ - case VAR_NUMBER: { \ - CONV_NUMBER(tv->vval.v_number); \ - break; \ - } \ - case VAR_FLOAT: { \ - CONV_FLOAT(tv->vval.v_float); \ - break; \ - } \ - case VAR_FUNC: { \ - CONV_FUNC(tv->vval.v_string); \ - break; \ - } \ - case VAR_LIST: { \ - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ - CONV_EMPTY_LIST(); \ - break; \ - } \ - CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ - CONV_LIST_START(tv->vval.v_list); \ - kv_push( \ - MPConvStackVal, \ - *mpstack, \ - ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = tv->vval.v_list, \ - .li = tv->vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case VAR_DICT: { \ - if (tv->vval.v_dict == NULL \ - || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - const dictitem_T *type_di; \ - const dictitem_T *val_di; \ - if (CONV_ALLOW_SPECIAL \ - && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ - && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_TYPE", -1)) != NULL \ - && type_di->di_tv.v_type == VAR_LIST \ - && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_VAL", -1)) != NULL) { \ - size_t i; \ - for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) { \ - if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) { \ - break; \ - } \ - } \ - if (i == ARRAY_SIZE(msgpack_type_lists)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - switch ((MessagePackType) i) { \ - case kMPNil: { \ - CONV_SPECIAL_NIL(); \ - break; \ - } \ - case kMPBoolean: { \ - if (val_di->di_tv.v_type != VAR_NUMBER) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_SPECIAL_BOOL(val_di->di_tv.vval.v_number); \ - break; \ - } \ - case kMPInteger: { \ - const list_T *val_list; \ - varnumber_T sign; \ - varnumber_T highest_bits; \ - varnumber_T high_bits; \ - varnumber_T low_bits; \ - /* List of 4 integers; first is signed (should be 1 or -1, but */ \ - /* this is not checked), second is unsigned and have at most */ \ - /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ - /* bits is not checked), other unsigned and have at most 31 */ \ - /* non-zero bits (number of bits is not checked).*/ \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 4 \ - || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ - || (highest_bits = \ - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ - || (high_bits = \ - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ - | (uint64_t) (((uint64_t) high_bits) << 31) \ - | (uint64_t) low_bits); \ - if (sign > 0) { \ - CONV_UNSIGNED_NUMBER(number); \ - } else { \ - CONV_NUMBER(-number); \ - } \ - break; \ - } \ - case kMPFloat: { \ - if (val_di->di_tv.v_type != VAR_FLOAT) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_FLOAT(val_di->di_tv.vval.v_float); \ - break; \ - } \ - case kMPString: \ - case kMPBinary: { \ - const bool is_string = ((MessagePackType) i == kMPString); \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - if (is_string) { \ - CONV_STR_STRING(buf, len); \ - } else { \ - CONV_STRING(buf, len); \ - } \ - xfree(buf); \ - break; \ - } \ - case kMPArray: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ - kMPConvList); \ - CONV_LIST_START(val_di->di_tv.vval.v_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = val_di->di_tv.vval.v_list, \ - .li = val_di->di_tv.vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPMap: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - if (val_di->di_tv.vval.v_list == NULL) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - list_T *const val_list = val_di->di_tv.vval.v_list; \ - for (const listitem_T *li = val_list->lv_first; li != NULL; \ - li = li->li_next) { \ - if (li->li_tv.v_type != VAR_LIST \ - || li->li_tv.vval.v_list->lv_len != 2) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - } \ - CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ - CONV_SPECIAL_MAP_START(val_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvPairs, \ - .data = { \ - .l = { \ - .list = val_list, \ - .li = val_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPExt: { \ - const list_T *val_list; \ - varnumber_T type; \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 2 \ - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ - || type < INT8_MIN \ - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ - &len, &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_EXT_STRING(buf, len, type); \ - xfree(buf); \ - break; \ - } \ - } \ - break; \ - } \ -name##_convert_one_value_regular_dict: \ - CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ - CONV_DICT_START(tv->vval.v_dict); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvDict, \ - .data = { \ - .d = { \ - .dict = tv->vval.v_dict, \ - .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ - .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ - }, \ - }, \ - })); \ - break; \ - } \ - default: { \ - EMSG2(_(e_intern2), #name "_convert_one_value()"); \ - return FAIL; \ - } \ - } \ - return OK; \ -} \ -\ -scope int vim_to_##name(firstargtype firstargname, typval_T *const tv, \ - const char *const objname) \ - FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - current_copyID += COPYID_INC; \ - const int copyID = current_copyID; \ - MPConvStack mpstack; \ - kv_init(mpstack); \ - if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ - == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - while (kv_size(mpstack)) { \ - MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ - typval_T *cur_tv = NULL; \ - switch (cur_mpsv->type) { \ - case kMPConvDict: { \ - if (!cur_mpsv->data.d.todo) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ - CONV_DICT_END(cur_mpsv->data.d.dict); \ - continue; \ - } else if (cur_mpsv->data.d.todo \ - != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ - CONV_DICT_BETWEEN_ITEMS(cur_mpsv->data.d.dict); \ - } \ - while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ - cur_mpsv->data.d.hi++; \ - } \ - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ - cur_mpsv->data.d.todo--; \ - cur_mpsv->data.d.hi++; \ - CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ - CONV_DICT_AFTER_KEY(cur_mpsv->data.d.dict); \ - cur_tv = &di->di_tv; \ - break; \ - } \ - case kMPConvList: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - CONV_LIST_END(cur_mpsv->data.l.list); \ - continue; \ - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ - CONV_LIST_BETWEEN_ITEMS(cur_mpsv->data.l.list); \ - } \ - cur_tv = &cur_mpsv->data.l.li->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - case kMPConvPairs: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - continue; \ - } \ - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ - if (name##_convert_one_value(firstargname, &mpstack, \ - &kv_pair->lv_first->li_tv, copyID, \ - objname) == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - cur_tv = &kv_pair->lv_last->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - } \ - if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ - objname) == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - } \ - kv_destroy(mpstack); \ - return OK; \ -vim_to_msgpack_error_ret: \ - kv_destroy(mpstack); \ - return FAIL; \ -} - -#define CONV_STRING(buf, len) \ - do { \ - const char *const buf_ = (const char *) buf; \ - if (buf == NULL) { \ - ga_concat(gap, (char_u *) "''"); \ - } else { \ - const size_t len_ = (len); \ - size_t num_quotes = 0; \ - for (size_t i = 0; i < len_; i++) { \ - if (buf_[i] == '\'') { \ - num_quotes++; \ - } \ - } \ - ga_grow(gap, 2 + len_ + num_quotes); \ - ga_append(gap, '\''); \ - for (size_t i = 0; i < len_; i++) { \ - if (buf_[i] == '\'') { \ - num_quotes++; \ - ga_append(gap, '\''); \ - } \ - ga_append(gap, buf_[i]); \ - } \ - ga_append(gap, '\''); \ - } \ - } while (0) - -#define CONV_STR_STRING(buf, len) \ - CONV_STRING(buf, len) - -#define CONV_EXT_STRING(buf, len, type) - -#define CONV_NUMBER(num) \ - do { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%" PRId64, (int64_t) (num)); \ - ga_concat(gap, (char_u *) numbuf); \ - } while (0) - -#define CONV_FLOAT(flt) \ - do { \ - const float_T flt_ = (flt); \ - switch (fpclassify(flt_)) { \ - case FP_NAN: { \ - ga_concat(gap, (char_u *) "str2float('nan')"); \ - break; \ - } \ - case FP_INFINITE: { \ - if (flt_ < 0) { \ - ga_append(gap, '-'); \ - } \ - ga_concat(gap, (char_u *) "str2float('inf')"); \ - break; \ - } \ - default: { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \ - ga_concat(gap, (char_u *) numbuf); \ - } \ - } \ - } while (0) - -#define CONV_FUNC(fun) \ - do { \ - ga_concat(gap, (char_u *) "function("); \ - CONV_STRING(fun, STRLEN(fun)); \ - ga_append(gap, ')'); \ - } while (0) - -#define CONV_EMPTY_LIST() \ - ga_concat(gap, (char_u *) "[]") - -#define CONV_LIST_START(lst) \ - ga_append(gap, '[') - -#define CONV_EMPTY_DICT() \ - ga_concat(gap, (char_u *) "{}") - -#define CONV_SPECIAL_NIL() - -#define CONV_SPECIAL_BOOL(num) - -#define CONV_UNSIGNED_NUMBER(num) - -#define CONV_SPECIAL_MAP_START(lst) - -#define CONV_DICT_START(dct) \ - ga_append(gap, '{') - -#define CONV_DICT_END(dct) \ - ga_append(gap, '}') - -#define CONV_DICT_AFTER_KEY(dct) \ - ga_concat(gap, (char_u *) ": ") - -#define CONV_DICT_BETWEEN_ITEMS(dct) \ - ga_concat(gap, (char_u *) ", ") - -#define CONV_LIST_END(lst) \ - ga_append(gap, ']') - -#define CONV_LIST_BETWEEN_ITEMS(lst) \ - CONV_DICT_BETWEEN_ITEMS(NULL) - -#define CONV_RECURSE(val, conv_type) \ - do { \ - if (!did_echo_string_emsg) { \ - /* Only give this message once for a recursive call to avoid */ \ - /* flooding the user with errors. */ \ - did_echo_string_emsg = true; \ - EMSG(_("E724: unable to correctly dump variable " \ - "with self-referencing container")); \ - } \ - char ebuf[NUMBUFLEN + 7]; \ - size_t backref = 0; \ - for (; backref < kv_size(*mpstack); backref++) { \ - const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict) { \ - if ((void *) mpval.data.d.dict == (void *) (val)) { \ - break; \ - } \ - } else if (conv_type == kMPConvList) { \ - if ((void *) mpval.data.l.list == (void *) (val)) { \ - break; \ - } \ - } \ - } \ - } \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "{E724@%zu}", backref); \ - ga_concat(gap, (char_u *) &ebuf[0]); \ - return OK; \ - } while (0) - -#define CONV_ALLOW_SPECIAL false - -DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) - -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ - do { \ - char ebuf[NUMBUFLEN + 7]; \ - size_t backref = 0; \ - for (; backref < kv_size(*mpstack); backref++) { \ - const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict) { \ - if ((void *) mpval.data.d.dict == (void *) val) { \ - break; \ - } \ - } else if (conv_type == kMPConvList) { \ - if ((void *) mpval.data.l.list == (void *) val) { \ - break; \ - } \ - } \ - } \ - } \ - if (conv_type == kMPConvDict) { \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "{...@%zu}", backref); \ - } else { \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "[...@%zu]", backref); \ - } \ - ga_concat(gap, (char_u *) &ebuf[0]); \ - return OK; \ - } while (0) - -DEFINE_VIML_CONV_FUNCTIONS(static, echo, garray_T *const, gap) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_SPECIAL_NIL -#undef CONV_SPECIAL_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_SPECIAL_MAP_START -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL - -/// Return a string with the string representation of a variable. -/// Puts quotes around strings, so that they can be parsed back by eval(). -/// -/// @param[in] tv typval_T to convert. -/// @param[out] len Location where length of the result will be saved. +/// Convert the string to a floating point number /// -/// @return String representation of the variable or NULL. -static char *tv2string(typval_T *tv, size_t *len) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC -{ - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - vim_to_string(&ga, tv, "tv2string() argument"); - did_echo_string_emsg = false; - if (len != NULL) { - *len = (size_t) ga.ga_len; - } - ga_append(&ga, '\0'); - return (char *) ga.ga_data; -} - -/// Return a string with the string representation of a variable. -/// Does not put quotes around strings, as ":echo" displays values. +/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to +/// make sure this always uses a decimal point. /// -/// @param[in] tv typval_T to convert. -/// @param[out] len Location where length of the result will be saved. +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. /// -/// @return String representation of the variable or NULL. -static char *echo_string(typval_T *tv, size_t *len) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC -{ - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) { - if (tv->vval.v_string != NULL) { - ga_concat(&ga, tv->vval.v_string); - } - } else { - vim_to_echo(&ga, tv, ":echo argument"); - did_echo_string_emsg = false; - } - if (len != NULL) { - *len = (size_t) ga.ga_len; - } - ga_append(&ga, '\0'); - return (char *) ga.ga_data; -} - -/* - * Convert the string "text" to a floating point number. - * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure - * this always uses a decimal point. - * Returns the length of the text that was consumed. - */ -static int -string2float ( - char_u *text, - float_T *value /* result stored here */ -) +/// @return Length of the text that was consumed. +size_t string2float(const char *const text, float_T *const ret_value) + FUNC_ATTR_NONNULL_ALL { - char *s = (char *)text; - float_T f; + char *s = NULL; - f = strtod(s, &s); - *value = f; - return (int)((char_u *)s - text); + *ret_value = strtod(text, &s); + return (size_t) (s - text); } /// Get the value of an environment variable. @@ -7179,7 +6656,7 @@ static struct fst { } functions[] = { { "abs", 1, 1, f_abs }, - { "acos", 1, 1, f_acos }, // WJMc + { "acos", 1, 1, f_acos }, // WJMc { "add", 2, 2, f_add }, { "and", 2, 2, f_and }, { "append", 2, 2, f_append }, @@ -7196,9 +6673,9 @@ static struct fst { { "browse", 4, 4, f_browse }, { "browsedir", 2, 2, f_browsedir }, { "bufexists", 1, 1, f_bufexists }, - { "buffer_exists", 1, 1, f_bufexists }, // obsolete - { "buffer_name", 1, 1, f_bufname }, // obsolete - { "buffer_number", 1, 1, f_bufnr }, // obsolete + { "buffer_exists", 1, 1, f_bufexists }, // obsolete + { "buffer_name", 1, 1, f_bufname }, // obsolete + { "buffer_number", 1, 1, f_bufnr }, // obsolete { "buflisted", 1, 1, f_buflisted }, { "bufloaded", 1, 1, f_bufloaded }, { "bufname", 1, 1, f_bufname }, @@ -7225,7 +6702,7 @@ static struct fst { { "cscope_connection", 0, 3, f_cscope_connection }, { "cursor", 1, 3, f_cursor }, { "deepcopy", 1, 2, f_deepcopy }, - { "delete", 1, 1, f_delete }, + { "delete", 1, 2, f_delete }, { "dictwatcheradd", 3, 3, f_dictwatcheradd }, { "dictwatcherdel", 3, 3, f_dictwatcherdel }, { "did_filetype", 0, 0, f_did_filetype }, @@ -7242,7 +6719,7 @@ static struct fst { { "expand", 1, 3, f_expand }, { "extend", 2, 3, f_extend }, { "feedkeys", 1, 2, f_feedkeys }, - { "file_readable", 1, 1, f_filereadable }, // obsolete + { "file_readable", 1, 1, f_filereadable }, // obsolete { "filereadable", 1, 1, f_filereadable }, { "filewritable", 1, 1, f_filewritable }, { "filter", 2, 2, f_filter }, @@ -7272,7 +6749,7 @@ static struct fst { { "getcmdtype", 0, 0, f_getcmdtype }, { "getcmdwintype", 0, 0, f_getcmdwintype }, { "getcurpos", 0, 0, f_getcurpos }, - { "getcwd", 0, 0, f_getcwd }, + { "getcwd", 0, 2, f_getcwd }, { "getfontname", 0, 1, f_getfontname }, { "getfperm", 1, 1, f_getfperm }, { "getfsize", 1, 1, f_getfsize }, @@ -7296,7 +6773,7 @@ static struct fst { { "globpath", 2, 5, f_globpath }, { "has", 1, 1, f_has }, { "has_key", 2, 2, f_has_key }, - { "haslocaldir", 0, 0, f_haslocaldir }, + { "haslocaldir", 0, 2, f_haslocaldir }, { "hasmapto", 1, 3, f_hasmapto }, { "highlightID", 1, 1, f_hlID }, // obsolete { "highlight_exists", 1, 1, f_hlexists }, // obsolete @@ -7329,6 +6806,8 @@ static struct fst { { "jobstop", 1, 1, f_jobstop }, { "jobwait", 1, 2, f_jobwait }, { "join", 1, 2, f_join }, + { "json_decode", 1, 1, f_json_decode }, + { "json_encode", 1, 1, f_json_encode }, { "keys", 1, 1, f_keys }, { "last_buffer_nr", 0, 0, f_last_buffer_nr }, // obsolete { "len", 1, 1, f_len }, @@ -8103,19 +7582,19 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *tofree; if (opt_msg_tv->v_type != VAR_UNKNOWN) { - tofree = (char_u *) tv2string(opt_msg_tv, NULL); + tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL); ga_concat(gap, tofree); xfree(tofree); } else { ga_concat(gap, (char_u *)"Expected "); if (exp_str == NULL) { - tofree = (char_u *) tv2string(exp_tv, NULL); + tofree = (char_u *) encode_tv2string(exp_tv, NULL); ga_concat(gap, tofree); xfree(tofree); } else { ga_concat(gap, exp_str); } - tofree = (char_u *) tv2string(got_tv, NULL); + tofree = (char_u *) encode_tv2string(got_tv, NULL); ga_concat(gap, (char_u *)" but got "); ga_concat(gap, tofree); xfree(tofree); @@ -8150,16 +7629,21 @@ static void f_assert_equal(typval_T *argvars, typval_T *rettv) } // Common for assert_true() and assert_false(). -static void assert_bool(typval_T *argvars, bool isTrue) +static void assert_bool(typval_T *argvars, bool is_true) { int error = (int)false; garray_T ga; - if (argvars[0].v_type != VAR_NUMBER || - (get_tv_number_chk(&argvars[0], &error) == 0) == isTrue || error) { + if ((argvars[0].v_type != VAR_NUMBER || + (get_tv_number_chk(&argvars[0], &error) == 0) == is_true || error) + && (argvars[0].v_type != VAR_SPECIAL + || (argvars[0].vval.v_special + != (SpecialVarValue) (is_true + ? kSpecialVarTrue + : kSpecialVarFalse)))) { prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[1], - (char_u *)(isTrue ? "True" : "False"), + (char_u *)(is_true ? "True" : "False"), NULL, &argvars[0]); assert_error(&ga); ga_clear(&ga); @@ -8821,12 +8305,12 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = cs_connection(num, dbpath, prepend); } -/* - * "cursor(lnum, col)" function - * - * Moves the cursor to the specified line and column. - * Returns 0 when the position could be set, -1 otherwise. - */ +/// "cursor(lnum, col)" function, or +/// "cursor(list)" +/// +/// Moves the cursor to the specified line and column. +/// +/// @returns 0 when the position could be set, -1 otherwise. static void f_cursor(typval_T *argvars, typval_T *rettv) { long line, col; @@ -8838,8 +8322,10 @@ static void f_cursor(typval_T *argvars, typval_T *rettv) colnr_T curswant = -1; if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) { + EMSG(_(e_invarg)); return; } + line = pos.lnum; col = pos.col; coladd = pos.coladd; @@ -8881,25 +8367,51 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_UNKNOWN) noref = get_tv_number_chk(&argvars[1], NULL); - if (noref < 0 || noref > 1) + if (noref < 0 || noref > 1) { EMSG(_(e_invarg)); - else { - current_copyID += COPYID_INC; + } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 - ? current_copyID + ? get_copyID() : 0)); } } -/* - * "delete()" function - */ +// "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv) { - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0])); + char_u nbuf[NUMBUFLEN]; + char_u *name; + char_u *flags; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) { + return; + } + + name = get_tv_string(&argvars[0]); + if (name == NULL || *name == NUL) { + EMSG(_(e_invarg)); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + flags = get_tv_string_buf(&argvars[1], nbuf); + } else { + flags = (char_u *)""; + } + + if (*flags == NUL) { + // delete a file + rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "d") == 0) { + // delete an empty directory + rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "rf") == 0) { + // delete a directory recursively + rettv->vval.v_number = delete_recursive(name); + } else { + EMSG2(_(e_invexpr2), flags); + } } // dictwatcheradd(dict, key, funcref) function @@ -9076,7 +8588,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv) */ static void f_empty(typval_T *argvars, typval_T *rettv) { - int n; + bool n = true; switch (argvars[0].v_type) { case VAR_STRING: @@ -9098,9 +8610,12 @@ static void f_empty(typval_T *argvars, typval_T *rettv) n = argvars[0].vval.v_dict == NULL || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; break; - default: - EMSG2(_(e_intern2), "f_empty()"); - n = 0; + case VAR_SPECIAL: + n = argvars[0].vval.v_special != kSpecialVarTrue; + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); + break; } rettv->vval.v_number = n; @@ -10272,22 +9787,143 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv) rettv->vval.v_string[0] = cmdwin_type; } -/* - * "getcwd()" function - */ +/// `getcwd([{win}[, {tab}]])` function +/// +/// Every scope not specified implies the currently selected scope object. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be a string. static void f_getcwd(typval_T *argvars, typval_T *rettv) { - char_u *cwd; + // Possible scope of working directory to return. + CdScope scope = kCdScopeWindow; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + char_u *cwd = NULL; // Current working directory to print + char_u *from = NULL; // The original string to copy + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + + // Pre-conditions and scope extraction together + for (int i = 0; i < kCdScopeGlobal; i++) { + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // Allocate and initialize the string to return. cwd = xmalloc(MAXPATHL); - if (os_dirname(cwd, MAXPATHL) != FAIL) { - rettv->vval.v_string = vim_strsave(cwd); + + // Get the scope and numbers from the arguments + for (int i = 0; i < MAX_CD_SCOPE; i++) { + // If there is no argument there are no more scopes after it, break out. + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + // It is an error for the scope number to be less than `-1`. + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + goto end; + } + } + + // If the deepest scope number is `-1` advance the scope. + if (scope_number[scope] < 0) { + scope++; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("E5000: Cannot find tab number.")); + goto end; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + goto end; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + goto end; + } + } + } + + switch (scope) { + case kCdScopeWindow: + from = win->w_localdir; + if (from) { + break; + } + case kCdScopeTab: // FALLTHROUGH + from = tp->localdir; + if (from) { + break; + } + case kCdScopeGlobal: // FALLTHROUGH + // The `globaldir` variable is not always set. + if (globaldir) { + from = globaldir; + } else { + // Copy the OS path directly into output string and jump to the end. + if (os_dirname(cwd, MAXPATHL) == FAIL) { + EMSG(_("E41: Could not display path.")); + goto end; + } + } + break; + } + + if (from) { + xstrlcpy((char *)cwd, (char *)from, MAXPATHL); + } + + rettv->vval.v_string = vim_strsave(cwd); #ifdef BACKSLASH_IN_FILENAME - slash_adjust(rettv->vval.v_string); + slash_adjust(rettv->vval.v_string); #endif - } + +end: xfree(cwd); } @@ -10610,9 +10246,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_LIST; rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); - if (rettv->vval.v_list != NULL) { - rettv->vval.v_list->lv_refcount++; + if (rettv->vval.v_list == NULL) { + rettv->vval.v_list = list_alloc(); } + rettv->vval.v_list->lv_refcount++; } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -11106,12 +10743,98 @@ static void f_has_key(typval_T *argvars, typval_T *rettv) get_tv_string(&argvars[1]), -1) != NULL; } -/* - * "haslocaldir()" function - */ +/// `haslocaldir([{win}[, {tab}]])` function +/// +/// Returns `1` if the scope object has a local directory, `0` otherwise. If a +/// scope object is not specified the current one is implied. This function +/// share a lot of code with `f_getcwd`. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be either the number `1` or `0`. static void f_haslocaldir(typval_T *argvars, typval_T *rettv) { - rettv->vval.v_number = (curwin->w_localdir != NULL); + // Possible scope of working directory to return. + CdScope scope = kCdScopeWindow; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + // Pre-conditions and scope extraction together + for (int i = 0; i < kCdScopeGlobal; i++) { + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // It the deepest scope number is `-1` advance the scope by one. + if (scope_number[scope] < 0) { + ++scope; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("5000: Cannot find tab number.")); + return; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + return; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + return; + } + } + } + + switch (scope) { + case kCdScopeWindow: + rettv->vval.v_number = win->w_localdir ? 1 : 0; + break; + case kCdScopeTab: + rettv->vval.v_number = tp->localdir ? 1 : 0; + break; + case kCdScopeGlobal: + assert(0); + break; + } } /* @@ -12124,6 +11847,47 @@ static void f_join(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = NULL; } +/// json_decode() function +static void f_json_decode(typval_T *argvars, typval_T *rettv) +{ + char numbuf[NUMBUFLEN]; + char *s = NULL; + char *tofree = NULL; + size_t len; + if (argvars[0].v_type == VAR_LIST) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + EMSG(_("E474: Failed to convert list to string")); + return; + } + tofree = s; + if (s == NULL) { + assert(len == 0); + s = ""; + } + } else { + s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + if (s) { + len = strlen(s); + } else { + return; + } + } + if (json_decode_string(s, len, rettv) == FAIL) { + emsgf(_("E474: Failed to parse %.*s"), (int) len, s); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } + assert(rettv->v_type != VAR_UNKNOWN); + xfree(tofree); +} + +/// json_encode() function +static void f_json_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *) encode_tv2json(&argvars[0], NULL); +} + /* * "keys()" function */ @@ -12165,7 +11929,10 @@ static void f_len(typval_T *argvars, typval_T *rettv) case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); break; - default: + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_FUNC: EMSG(_("E701: Invalid type for len()")); break; } @@ -12489,9 +12256,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) break; } xfree(tofree); - tofree = str = (char_u *) echo_string(&li->li_tv, NULL); - if (str == NULL) + tofree = str = (char_u *) encode_tv2echo(&li->li_tv, NULL); + if (str == NULL) { break; + } } match = vim_regexec_nl(®match, str, (colnr_T)startcol); @@ -12889,289 +12657,6 @@ static void f_mode(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; } -/// Msgpack callback for writing to readfile()-style list -static int msgpack_list_write(void *data, const char *buf, size_t len) -{ - if (len == 0) { - return 0; - } - list_T *const list = (list_T *) data; - const char *const end = buf + len; - const char *line_end = buf; - if (list->lv_last == NULL) { - list_append_string(list, NULL, 0); - } - listitem_T *li = list->lv_last; - do { - const char *line_start = line_end; - line_end = xmemscan(line_start, NL, end - line_start); - if (line_end == line_start) { - list_append_allocated_string(list, NULL); - } else { - const size_t line_length = line_end - line_start; - char *str; - if (li == NULL) { - str = xmemdupz(line_start, line_length); - } else { - const size_t li_len = (li->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(li->li_tv.vval.v_string)); - li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string, - li_len + line_length + 1); - str = (char *) li->li_tv.vval.v_string + li_len; - memmove(str, line_start, line_length); - str[line_length] = 0; - } - for (size_t i = 0; i < line_length; i++) { - if (str[i] == NUL) { - str[i] = NL; - } - } - if (li == NULL) { - list_append_allocated_string(list, str); - } else { - li = NULL; - } - if (line_end == end - 1) { - list_append_allocated_string(list, NULL); - } - } - line_end++; - } while (line_end < end); - return 0; -} - -/// Convert readfile()-style list to a char * buffer with length -/// -/// @param[in] list Converted list. -/// @param[out] ret_len Resulting buffer length. -/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is -/// zero. -/// -/// @return true in case of success, false in case of failure. -static inline bool vim_list_to_buf(const list_T *const list, - size_t *const ret_len, char **const ret_buf) - FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT -{ - size_t len = 0; - if (list != NULL) { - for (const listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { - return false; - } - len++; - if (li->li_tv.vval.v_string != 0) { - len += STRLEN(li->li_tv.vval.v_string); - } - } - if (len) { - len--; - } - } - *ret_len = len; - if (len == 0) { - *ret_buf = NULL; - return true; - } - ListReaderState lrstate = init_lrstate(list); - char *const buf = xmalloc(len); - size_t read_bytes; - if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) { - assert(false); - } - assert(len == read_bytes); - *ret_buf = buf; - return true; -} - -/// Show a error message when converting to msgpack value -/// -/// @param[in] msg Error message to dump. Must contain exactly two %s that -/// will be replaced with what was being dumped: first with -/// something like “F†or “function argumentâ€, second with path -/// to the failed value. -/// @param[in] mpstack Path to the failed value. -/// @param[in] objname Dumped object name. -/// -/// @return FAIL. -static int conv_error(const char *const msg, const MPConvStack *const mpstack, - const char *const objname) - FUNC_ATTR_NONNULL_ALL -{ - garray_T msg_ga; - ga_init(&msg_ga, (int)sizeof(char), 80); - char *const key_msg = _("key %s"); - char *const key_pair_msg = _("key %s at index %i from special map"); - char *const idx_msg = _("index %i"); - for (size_t i = 0; i < kv_size(*mpstack); i++) { - if (i != 0) { - ga_concat(&msg_ga, (char_u *) ", "); - } - MPConvStackVal v = kv_A(*mpstack, i); - switch (v.type) { - case kMPConvDict: { - typval_T key_tv = { - .v_type = VAR_STRING, - .vval = { .v_string = (v.data.d.hi == NULL - ? v.data.d.dict->dv_hashtab.ht_array - : (v.data.d.hi - 1))->hi_key }, - }; - char *const key = tv2string(&key_tv, NULL); - vim_snprintf((char *) IObuff, IOSIZE, key_msg, key); - xfree(key); - ga_concat(&msg_ga, IObuff); - break; - } - case kMPConvPairs: - case kMPConvList: { - int idx = 0; - const listitem_T *li; - for (li = v.data.l.list->lv_first; - li != NULL && li->li_next != v.data.l.li; - li = li->li_next) { - idx++; - } - if (v.type == kMPConvList - || li == NULL - || (li->li_tv.v_type != VAR_LIST - && li->li_tv.vval.v_list->lv_len <= 0)) { - vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); - ga_concat(&msg_ga, IObuff); - } else { - typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; - char *const key = echo_string(&key_tv, NULL); - vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); - xfree(key); - ga_concat(&msg_ga, IObuff); - } - break; - } - } - } - EMSG3(msg, objname, (kv_size(*mpstack) == 0 - ? _("itself") - : (char *) msg_ga.ga_data)); - ga_clear(&msg_ga); - return FAIL; -} - -#define CONV_STRING(buf, len) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_bin(packer, 0); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_bin(packer, len_); \ - msgpack_pack_bin_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_STR_STRING(buf, len) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_str(packer, 0); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_str(packer, len_); \ - msgpack_pack_str_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_EXT_STRING(buf, len, type) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_ext(packer, 0, type); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_ext(packer, len_, (int8_t) type); \ - msgpack_pack_ext_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_NUMBER(num) \ - msgpack_pack_int64(packer, (int64_t) (num)) - -#define CONV_FLOAT(flt) \ - msgpack_pack_double(packer, (double) (flt)) - -#define CONV_FUNC(fun) \ - return conv_error(_("E951: Error while dumping %s, %s: " \ - "attempt to dump function reference"), \ - mpstack, objname) - -#define CONV_EMPTY_LIST() \ - msgpack_pack_array(packer, 0) - -#define CONV_LIST_START(lst) \ - msgpack_pack_array(packer, (lst)->lv_len) - -#define CONV_EMPTY_DICT() \ - msgpack_pack_map(packer, 0) - -#define CONV_SPECIAL_NIL() \ - msgpack_pack_nil(packer) - -#define CONV_SPECIAL_BOOL(num) \ - do { \ - if ((num)) { \ - msgpack_pack_true(packer); \ - } else { \ - msgpack_pack_false(packer); \ - } \ - } while (0) - -#define CONV_UNSIGNED_NUMBER(num) \ - msgpack_pack_uint64(packer, (num)) - -#define CONV_SPECIAL_MAP_START(lst) \ - msgpack_pack_map(packer, (lst)->lv_len) - -#define CONV_DICT_START(dct) \ - msgpack_pack_map(packer, (dct)->dv_hashtab.ht_used) - -#define CONV_DICT_END(dct) - -#define CONV_DICT_AFTER_KEY(dct) - -#define CONV_DICT_BETWEEN_ITEMS(dct) - -#define CONV_LIST_END(lst) - -#define CONV_LIST_BETWEEN_ITEMS(lst) - -#define CONV_RECURSE(val, conv_type) \ - return conv_error(_("E952: Unable to dump %s: " \ - "container references itself in %s"), \ - mpstack, objname) - -#define CONV_ALLOW_SPECIAL true - -DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_SPECIAL_NIL -#undef CONV_SPECIAL_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_SPECIAL_MAP_START -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL - /// "msgpackdump()" function static void f_msgpackdump(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -13185,7 +12670,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv) if (list == NULL) { return; } - msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write); + msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; @@ -13193,307 +12678,13 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv) for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); idx++; - if (vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { + if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { break; } } msgpack_packer_free(lpacker); } -/// Read bytes from list -/// -/// @param[in,out] state Structure describing position in list from which -/// reading should start. Is updated to reflect position -/// at which reading ended. -/// @param[out] buf Buffer to write to. -/// @param[in] nbuf Buffer length. -/// @param[out] read_bytes Is set to amount of bytes read. -/// -/// @return OK when reading was finished, FAIL in case of error (i.e. list item -/// was not a string), NOTDONE if reading was successfull, but there are -/// more bytes to read. -static int read_from_list(ListReaderState *const state, char *const buf, - const size_t nbuf, size_t *const read_bytes) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - char *const buf_end = buf + nbuf; - char *p = buf; - while (p < buf_end) { - for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { - const char ch = state->li->li_tv.vval.v_string[state->offset++]; - *p++ = (ch == NL ? NUL : ch); - } - if (p < buf_end) { - state->li = state->li->li_next; - if (state->li == NULL) { - *read_bytes = (size_t) (p - buf); - return OK; - } - *p++ = NL; - if (state->li->li_tv.v_type != VAR_STRING) { - *read_bytes = (size_t) (p - buf); - return FAIL; - } - state->offset = 0; - state->li_length = (state->li->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(state->li->li_tv.vval.v_string)); - } - } - *read_bytes = nbuf; - return (state->offset < state->li_length || state->li->li_next != NULL - ? NOTDONE - : OK); -} - -/// Initialize ListReaderState structure -static inline ListReaderState init_lrstate(const list_T *const list) - FUNC_ATTR_NONNULL_ALL -{ - return (ListReaderState) { - .li = list->lv_first, - .offset = 0, - .li_length = (list->lv_first->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(list->lv_first->li_tv.vval.v_string)), - }; -} - -/// Convert msgpack object to a VimL one -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) \ - do { \ - dict_T *const dict = dict_alloc(); \ - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \ - type_di->di_tv.v_type = VAR_LIST; \ - type_di->di_tv.v_lock = 0; \ - type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \ - type_di->di_tv.vval.v_list->lv_refcount++; \ - dict_add(dict, type_di); \ - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \ - val_di->di_tv = val; \ - dict_add(dict, val_di); \ - tv->v_type = VAR_DICT; \ - dict->dv_refcount++; \ - tv->vval.v_dict = dict; \ - } while (0) - switch (mobj.type) { - case MSGPACK_OBJECT_NIL: { - INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = 0 }, - })); - break; - } - case MSGPACK_OBJECT_BOOLEAN: { - INIT_SPECIAL_DICT(rettv, kMPBoolean, - ((typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { - .v_number = (varnumber_T) mobj.via.boolean, - }, - })); - break; - } - case MSGPACK_OBJECT_POSITIVE_INTEGER: { - if (mobj.via.u64 <= VARNUMBER_MAX) { - *rettv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = (varnumber_T) mobj.via.u64 }, - }; - } else { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPInteger, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); - } - break; - } - case MSGPACK_OBJECT_NEGATIVE_INTEGER: { - if (mobj.via.i64 >= VARNUMBER_MIN) { - *rettv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = (varnumber_T) mobj.via.i64 }, - }; - } else { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPInteger, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); - } - break; - } - case MSGPACK_OBJECT_FLOAT: { - *rettv = (typval_T) { - .v_type = VAR_FLOAT, - .v_lock = 0, - .vval = { .v_float = mobj.via.f64 }, - }; - break; - } - case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPString, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) - == -1) { - return FAIL; - } - break; - } - case MSGPACK_OBJECT_BIN: { - if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { - *rettv = (typval_T) { - .v_type = VAR_STRING, - .v_lock = 0, - .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, - }; - break; - } - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPBinary, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) - == -1) { - return FAIL; - } - break; - } - case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); - list->lv_refcount++; - *rettv = (typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - }; - for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { - return FAIL; - } - } - break; - } - case MSGPACK_OBJECT_MAP: { - for (size_t i = 0; i < mobj.via.map.size; i++) { - if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR - || mobj.via.map.ptr[i].key.via.str.size == 0 - || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL, - mobj.via.map.ptr[i].key.via.str.size) != NULL) { - goto msgpack_to_vim_generic_map; - } - } - dict_T *const dict = dict_alloc(); - dict->dv_refcount++; - *rettv = (typval_T) { - .v_type = VAR_DICT, - .v_lock = 0, - .vval = { .v_dict = dict }, - }; - for (size_t i = 0; i < mobj.via.map.size; i++) { - dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) - + mobj.via.map.ptr[i].key.via.str.size); - memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, - mobj.via.map.ptr[i].key.via.str.size); - di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { - // Duplicate key: fallback to generic map - clear_tv(rettv); - xfree(di); - goto msgpack_to_vim_generic_map; - } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) { - return FAIL; - } - } - break; -msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPMap, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); - key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); - val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { - return FAIL; - } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { - return FAIL; - } - } - break; - } - case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); - list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); - INIT_SPECIAL_DICT(rettv, kMPExt, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr, - mobj.via.ext.size) == -1) { - return FAIL; - } - break; - } - } -#undef INIT_SPECIAL_DICT - return OK; -} - /// "msgpackparse" function static void f_msgpackparse(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -13511,7 +12702,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv) EMSG2(_(e_invarg2), "List item is not a string"); return; } - ListReaderState lrstate = init_lrstate(list); + ListReaderState lrstate = encode_init_lrstate(list); msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE); if (unpacker == NULL) { EMSG(_(e_outofmem)); @@ -13525,7 +12716,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv) goto f_msgpackparse_exit; } size_t read_bytes; - const int rlret = read_from_list( + const int rlret = encode_read_from_list( &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes); if (rlret == FAIL) { EMSG2(_(e_invarg2), "List item is not a string"); @@ -14354,14 +13545,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) } } -#define SP_NOMOVE 0x01 /* don't move cursor */ -#define SP_REPEAT 0x02 /* repeat to find outer pair */ -#define SP_RETCOUNT 0x04 /* return matchcount */ -#define SP_SETPCMARK 0x08 /* set previous context mark */ -#define SP_START 0x10 /* accept match at start position */ -#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */ -#define SP_END 0x40 /* leave cursor at end of match */ - +#define SP_NOMOVE 0x01 ///< don't move cursor +#define SP_REPEAT 0x02 ///< repeat to find outer pair +#define SP_RETCOUNT 0x04 ///< return matchcount +#define SP_SETPCMARK 0x08 ///< set previous context mark +#define SP_START 0x10 ///< accept match at start position +#define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern +#define SP_END 0x40 ///< leave cursor at end of match +#define SP_COLUMN 0x80 ///< start at cursor column /* * Get flags for a search function. @@ -14387,13 +13578,14 @@ static int get_search_arg(typval_T *varp, int *flagsp) default: mask = 0; if (flagsp != NULL) switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; } if (mask == 0) { EMSG2(_(e_invarg2), flags); @@ -14409,9 +13601,7 @@ static int get_search_arg(typval_T *varp, int *flagsp) return dir; } -/* - * Shared by search() and searchpos() functions - */ +// Shared by search() and searchpos() functions. static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; @@ -14432,10 +13622,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (dir == 0) goto theend; flags = *flagsp; - if (flags & SP_START) + if (flags & SP_START) { options |= SEARCH_START; - if (flags & SP_END) + } + if (flags & SP_END) { options |= SEARCH_END; + } + if (flags & SP_COLUMN) { + options |= SEARCH_COL; + } /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { @@ -15765,6 +14960,8 @@ typedef struct { static int item_compare_ic; static bool item_compare_numeric; +static bool item_compare_numbers; +static bool item_compare_float; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; @@ -15786,9 +14983,24 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) si2 = (sortItem_T *)s2; typval_T *tv1 = &si1->item->li_tv; typval_T *tv2 = &si2->item->li_tv; - // tv2string() puts quotes around a string and allocates memory. Don't do - // that for string variables. Use a single quote when comparing with a - // non-string to do what the docs promise. + + if (item_compare_numbers) { + long v1 = get_tv_number(tv1); + long v2 = get_tv_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + if (item_compare_float) { + float_T v1 = get_tv_float(tv1); + float_T v2 = get_tv_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || item_compare_numeric) { p1 = (char_u *)"'"; @@ -15796,7 +15008,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) p1 = tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) tv2string(tv1, NULL); + tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || item_compare_numeric) { @@ -15805,7 +15017,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) p2 = tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) tv2string(tv2, NULL); + tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); } if (p1 == NULL) p1 = (char_u *)""; @@ -15932,6 +15144,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_ic = FALSE; item_compare_numeric = false; + item_compare_numbers = false; + item_compare_float = false; item_compare_func = NULL; item_compare_selfdict = NULL; @@ -15953,6 +15167,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (STRCMP(item_compare_func, "n") == 0) { item_compare_func = NULL; item_compare_numeric = true; + } else if (STRCMP(item_compare_func, "N") == 0) { + item_compare_func = NULL; + item_compare_numbers = true; + } else if (STRCMP(item_compare_func, "f") == 0) { + item_compare_func = NULL; + item_compare_float = true; } else if (STRCMP(item_compare_func, "i") == 0) { item_compare_func = NULL; item_compare_ic = TRUE; @@ -16241,7 +15461,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv) if (*p == '+') p = skipwhite(p + 1); - (void)string2float(p, &rettv->vval.v_float); + (void) string2float((char *) p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } @@ -16372,7 +15592,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv) static void f_string(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char_u *) tv2string(&argvars[0], NULL); + rettv->vval.v_string = (char_u *) encode_tv2string(&argvars[0], NULL); } /* @@ -17105,9 +16325,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(rettv->vval.v_number), &err); + INTEGER_OBJ(rettv->vval.v_number), false, &err); dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), &err); + INTEGER_OBJ(pid), false, &err); Terminal *term = terminal_open(topts); data->term = term; @@ -17293,16 +16513,33 @@ static void f_trunc(typval_T *argvars, typval_T *rettv) */ static void f_type(typval_T *argvars, typval_T *rettv) { - int n; + int n = -1; switch (argvars[0].v_type) { - case VAR_NUMBER: n = 0; break; - case VAR_STRING: n = 1; break; - case VAR_FUNC: n = 2; break; - case VAR_LIST: n = 3; break; - case VAR_DICT: n = 4; break; - case VAR_FLOAT: n = 5; break; - default: EMSG2(_(e_intern2), "f_type()"); n = 0; break; + case VAR_NUMBER: n = 0; break; + case VAR_STRING: n = 1; break; + case VAR_FUNC: n = 2; break; + case VAR_LIST: n = 3; break; + case VAR_DICT: n = 4; break; + case VAR_FLOAT: n = 5; break; + case VAR_SPECIAL: { + switch (argvars[0].vval.v_special) { + case kSpecialVarTrue: + case kSpecialVarFalse: { + n = 6; + break; + } + case kSpecialVarNull: { + n = 7; + break; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + break; + } } rettv->vval.v_number = n; } @@ -18021,9 +17258,9 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, } } else if (br_nest == 0 && mb_nest == 0 && *p == ':') { // "s:" is start of "s:var", but "n:" is not and can be used in - // slice "[n:]". Also "xx:" is not a namespace. + // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. */ len = (int)(p - arg); - if (len > 1 + if ((len > 1 && p[-1] != '}') || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) { break; } @@ -18129,14 +17366,6 @@ static int eval_isnamec1(int c) } /* - * Set number v: variable to "val". - */ -void set_vim_var_nr(int idx, long val) -{ - vimvars[idx].vv_nr = val; -} - -/* * Get number v: variable value. */ long get_vim_var_nr(int idx) FUNC_ATTR_PURE @@ -18173,11 +17402,11 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE */ void set_vim_var_char(int c) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { + if (has_mbyte) { + buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL; + } else { buf[0] = c; buf[1] = NUL; } @@ -18196,47 +17425,68 @@ void set_vcount(long count, long count1, int set_prevcount) vimvars[VV_COUNT1].vv_nr = count1; } -/* - * Set string v: variable to a copy of "val". - */ -void set_vim_var_string ( - int idx, - char_u *val, - int len /* length of "val" to use or -1 (whole string) */ -) +/// Set number v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - /* Need to do this (at least) once, since we can't initialize a union. - * Will always be invoked when "v:progname" is set. */ - vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + vimvars[idx].vv_nr = val; +} +/// Set special v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) +{ + vimvars[idx].vv_special = val; +} + +/// Set string v: variable to the given string +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. Will be copied. +/// @param[in] len Legth of that value or -1 in which case strlen() will be +/// used. +void set_vim_var_string(const VimVarIndex idx, const char *const val, + const ptrdiff_t len) +{ xfree(vimvars[idx].vv_str); - if (val == NULL) + if (val == NULL) { vimvars[idx].vv_str = NULL; - else if (len == -1) - vimvars[idx].vv_str = vim_strsave(val); - else - vimvars[idx].vv_str = vim_strnsave(val, len); + } else if (len == -1) { + vimvars[idx].vv_str = (char_u *) xstrdup(val); + } else { + vimvars[idx].vv_str = (char_u *) xstrndup(val, (size_t) len); + } } -/* - * Set List v: variable to "val". - */ -void set_vim_var_list(int idx, list_T *val) +/// Set list v: variable to the given list +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +void set_vim_var_list(const VimVarIndex idx, list_T *const val) { list_unref(vimvars[idx].vv_list); vimvars[idx].vv_list = val; - if (val != NULL) - ++val->lv_refcount; + if (val != NULL) { + val->lv_refcount++; + } } -/// Set Dictionary v: variable to "val". -void set_vim_var_dict(int idx, dict_T *val) +/// Set Dictionary v: variable to the given dictionary +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +/// Also keys of the dictionary will be made read-only. +void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { dict_unref(vimvars[idx].vv_dict); vimvars[idx].vv_dict = val; if (val != NULL) { - ++val->dv_refcount; + val->dv_refcount++; // Set readonly dict_set_keys_readonly(val); } @@ -18247,15 +17497,17 @@ void set_vim_var_dict(int idx, dict_T *val) */ void set_reg_var(int c) { - char_u regname; + char regname; - if (c == 0 || c == ' ') + if (c == 0 || c == ' ') { regname = '"'; - else + } else { regname = c; - /* Avoid free/alloc when the value is already right. */ - if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) + } + // Avoid free/alloc when the value is already right. + if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) { set_vim_var_string(VV_REG, ®name, 1); + } } /* @@ -18485,25 +17737,23 @@ void free_tv(typval_T *varp) { if (varp != NULL) { switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - /*FALLTHROUGH*/ - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - break; - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - default: - EMSG2(_(e_intern2), "free_tv()"); - break; + case VAR_FUNC: + func_unref(varp->vval.v_string); + // FALLTHROUGH + case VAR_STRING: + xfree(varp->vval.v_string); + break; + case VAR_LIST: + list_unref(varp->vval.v_list); + break; + case VAR_DICT: + dict_unref(varp->vval.v_dict); + break; + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; } xfree(varp); } @@ -18541,10 +17791,11 @@ void clear_tv(typval_T *varp) case VAR_FLOAT: varp->vval.v_float = 0.0; break; + case VAR_SPECIAL: + varp->vval.v_special = kSpecialVarFalse; + break; case VAR_UNKNOWN: break; - default: - EMSG2(_(e_intern2), "clear_tv()"); } varp->v_lock = 0; } @@ -18599,8 +17850,19 @@ long get_tv_number_chk(typval_T *varp, int *denote) case VAR_DICT: EMSG(_("E728: Using a Dictionary as a Number")); break; - default: - EMSG2(_(e_intern2), "get_tv_number()"); + case VAR_SPECIAL: + switch (varp->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); break; } if (denote == NULL) { @@ -18612,6 +17874,33 @@ long get_tv_number_chk(typval_T *varp, int *denote) return n; } +static float_T get_tv_float(typval_T *varp) +{ + switch (varp->v_type) { + case VAR_NUMBER: + return (float_T)(varp->vval.v_number); + case VAR_FLOAT: + return varp->vval.v_float; + break; + case VAR_FUNC: + EMSG(_("E891: Using a Funcref as a Float")); + break; + case VAR_STRING: + EMSG(_("E892: Using a String as a Float")); + break; + case VAR_LIST: + EMSG(_("E893: Using a List as a Float")); + break; + case VAR_DICT: + EMSG(_("E894: Using a Dictionary as a Float")); + break; + default: + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + return 0; +} + /* * Get the lnum from the first argument. * Also accepts ".", "$", etc., but that only works for the current buffer. @@ -18705,8 +17994,11 @@ static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) if (varp->vval.v_string != NULL) return varp->vval.v_string; return (char_u *)""; - default: - EMSG2(_(e_intern2), "get_tv_string_buf()"); + case VAR_SPECIAL: + STRCPY(buf, encode_special_var_names[varp->vval.v_special]); + return buf; + case VAR_UNKNOWN: + EMSG(_("E908: using an invalid value as a String")); break; } return NULL; @@ -18977,7 +18269,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) */ static void list_one_var(dictitem_T *v, char_u *prefix, int *first) { - char_u *s = (char_u *) echo_string(&v->di_tv, NULL); + char_u *s = (char_u *) encode_tv2echo(&v->di_tv, NULL); list_one_var_a(prefix, v->di_key, v->di_tv.v_type, s == NULL ? (char_u *)"" : s, first); xfree(s); @@ -19254,42 +18546,34 @@ void copy_tv(typval_T *from, typval_T *to) { to->v_type = from->v_type; to->v_lock = 0; + memmove(&to->vval, &from->vval, sizeof(to->vval)); switch (from->v_type) { - case VAR_NUMBER: - to->vval.v_number = from->vval.v_number; - break; - case VAR_FLOAT: - to->vval.v_float = from->vval.v_float; - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string == NULL) - to->vval.v_string = NULL; - else { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) - func_ref(to->vval.v_string); - } - break; - case VAR_LIST: - if (from->vval.v_list == NULL) - to->vval.v_list = NULL; - else { - to->vval.v_list = from->vval.v_list; - ++to->vval.v_list->lv_refcount; - } - break; - case VAR_DICT: - if (from->vval.v_dict == NULL) - to->vval.v_dict = NULL; - else { - to->vval.v_dict = from->vval.v_dict; - ++to->vval.v_dict->dv_refcount; - } - break; - default: - EMSG2(_(e_intern2), "copy_tv()"); - break; + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: + break; + case VAR_STRING: + case VAR_FUNC: + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + case VAR_LIST: + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + case VAR_DICT: + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); + break; } } @@ -19329,6 +18613,7 @@ int var_item_copy(const vimconv_T *const conv, case VAR_NUMBER: case VAR_FLOAT: case VAR_FUNC: + case VAR_SPECIAL: copy_tv(from, to); break; case VAR_STRING: @@ -19375,8 +18660,8 @@ int var_item_copy(const vimconv_T *const conv, if (to->vval.v_dict == NULL) ret = FAIL; break; - default: - EMSG2(_(e_intern2), "var_item_copy()"); + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); ret = FAIL; } --recurse; @@ -19431,7 +18716,7 @@ void ex_echo(exarg_T *eap) } } else if (eap->cmdidx == CMD_echo) msg_puts_attr((char_u *)" ", echo_attr); - char_u *tofree = p = (char_u *) echo_string(&rettv, NULL); + char_u *tofree = p = (char_u *) encode_tv2echo(&rettv, NULL); if (p != NULL) { for (; *p != NUL && !got_int; ++p) { if (*p == '\n' || *p == '\r' || *p == TAB) { @@ -21083,10 +20368,10 @@ call_user_func ( msg_outnum((long)argvars[i].vval.v_number); } else { // Do not want errors such as E724 here. - ++emsg_off; - char_u *s = (char_u *) tv2string(&argvars[i], NULL); + emsg_off++; + char_u *s = (char_u *) encode_tv2string(&argvars[i], NULL); char_u *tofree = s; - --emsg_off; + emsg_off--; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -21177,10 +20462,10 @@ call_user_func ( // The value may be very long. Skip the middle part, so that we // have some idea how it starts and ends. smsg() would always // truncate it at the end. Don't want errors such as E724 here. - ++emsg_off; - char_u *s = (char_u *) tv2string(fc->rettv, NULL); + emsg_off++; + char_u *s = (char_u *) encode_tv2string(fc->rettv, NULL); char_u *tofree = s; - --emsg_off; + emsg_off--; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -21451,7 +20736,7 @@ char_u *get_return_cmd(void *rettv) char_u *tofree = NULL; if (rettv != NULL) { - tofree = s = (char_u *) echo_string((typval_T *) rettv, NULL); + tofree = s = (char_u *) encode_tv2echo((typval_T *) rettv, NULL); } if (s == NULL) { s = (char_u *)""; @@ -22614,4 +21899,3 @@ static bool is_watched(dict_T *d) { return d && !QUEUE_EMPTY(&d->watchers); } - diff --git a/src/nvim/eval.h b/src/nvim/eval.h index f51b0f4921..d6800afd52 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,12 +1,17 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include <msgpack.h> - #include "nvim/profile.h" +#include "nvim/hashtab.h" // For hashtab_T +#include "nvim/garray.h" // For garray_T +#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/ex_cmds_defs.h" // For exarg_T + +#define COPYID_INC 2 +#define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. -EXTERN hashtab_T func_hashtab; +extern hashtab_T func_hashtab; // Structure to hold info for a user function. typedef struct ufunc ufunc_T; @@ -46,8 +51,8 @@ EXTERN ufunc_T dumuf; #define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) -/* Defines for Vim variables. These must match vimvars[] in eval.c! */ -enum { +/// Defines for Vim variables +typedef enum { VV_COUNT, VV_COUNT1, VV_PREVCOUNT, @@ -114,15 +119,35 @@ enum { VV_ERRORS, VV_MSGPACK_TYPES, VV_EVENT, - VV_LEN, // number of v: vars -}; + VV_FALSE, + VV_TRUE, + VV_NULL, + VV__NULL_LIST, // List with NULL value. For test purposes only. + VV__NULL_DICT, // Dictionary with NULL value. For test purposes only. +} VimVarIndex; + +/// All recognized msgpack types +typedef enum { + kMPNil, + kMPBoolean, + kMPInteger, + kMPFloat, + kMPString, + kMPBinary, + kMPArray, + kMPMap, + kMPExt, +#define LAST_MSGPACK_TYPE kMPExt +} MessagePackType; + +/// Array mapping values from MessagePackType to corresponding list pointers +extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1]; + +#undef LAST_MSGPACK_TYPE /// Maximum number of function arguments #define MAX_FUNC_ARGS 20 -int vim_to_msgpack(msgpack_packer *const, typval_T *const, - const char *const objname); - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.h.generated.h" #endif diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c new file mode 100644 index 0000000000..0774ef515f --- /dev/null +++ b/src/nvim/eval/decode.c @@ -0,0 +1,1116 @@ +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval_defs.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/ascii.h" +#include "nvim/message.h" +#include "nvim/charset.h" // vim_str2nr +#include "nvim/lib/kvec.h" +#include "nvim/vim.h" // OK, FAIL + +/// Helper structure for container_struct +typedef struct { + size_t stack_index; ///< Index of current container in stack. + list_T *special_val; ///< _VAL key contents for special maps. + ///< When container is not a special dictionary it is + ///< NULL. + const char *s; ///< Location where container starts. + typval_T container; ///< Container. Either VAR_LIST, VAR_DICT or VAR_LIST + ///< which is _VAL from special dictionary. +} ContainerStackItem; + +/// Helper structure for values struct +typedef struct { + bool is_special_string; ///< Indicates that current value is a special + ///< dictionary with string. + bool didcomma; ///< True if previous token was comma. + bool didcolon; ///< True if previous token was colon. + typval_T val; ///< Actual value. +} ValuesStackItem; + +/// Vector containing values not yet saved in any container +typedef kvec_t(ValuesStackItem) ValuesStack; + +/// Vector containing containers, each next container is located inside previous +typedef kvec_t(ContainerStackItem) ContainerStack; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/decode.c.generated.h" +#endif + +/// Create special dictionary +/// +/// @param[out] rettv Location where created dictionary will be saved. +/// @param[in] type Type of the dictionary. +/// @param[in] val Value associated with the _VAL key. +static inline void create_special_dict(typval_T *const rettv, + const MessagePackType type, + typval_T val) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const dict = dict_alloc(); + dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + type_di->di_tv.v_type = VAR_LIST; + type_di->di_tv.v_lock = VAR_UNLOCKED; + type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; + type_di->di_tv.vval.v_list->lv_refcount++; + dict_add(dict, type_di); + dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + val_di->di_tv = val; + dict_add(dict, val_di); + dict->dv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; +} + +#define DICT_LEN(dict) (dict)->dv_hashtab.ht_used + +/// Helper function used for working with stack vectors used by JSON decoder +/// +/// @param[in,out] obj New object. Will either be put into the stack (and, +/// probably, also inside container) or freed. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[in,out] pp Position in string which is currently being parsed. Used +/// for error reporting and is also set when decoding is +/// restarted due to the necessity of converting regular +/// dictionary to a special map. +/// @param[out] next_map_special Is set to true when dictionary needs to be +/// converted to a special map, otherwise not +/// touched. Indicates that decoding has been +/// restarted. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int json_decoder_pop(ValuesStackItem obj, + ValuesStack *const stack, + ContainerStack *const container_stack, + const char **const pp, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL +{ + if (kv_size(*container_stack) == 0) { + kv_push(ValuesStackItem, *stack, obj); + return OK; + } + ContainerStackItem last_container = kv_last(*container_stack); + const char *val_location = *pp; + if (obj.val.v_type == last_container.container.v_type + // vval.v_list and vval.v_dict should have the same size and offset + && ((void *) obj.val.vval.v_list + == (void *) last_container.container.vval.v_list)) { + (void) kv_pop(*container_stack); + val_location = last_container.s; + last_container = kv_last(*container_stack); + } + if (last_container.container.v_type == VAR_LIST) { + if (last_container.container.vval.v_list->lv_len != 0 + && !obj.didcomma) { + EMSG2(_("E474: Expected comma before list item: %s"), val_location); + clear_tv(&obj.val); + return FAIL; + } + assert(last_container.special_val == NULL); + listitem_T *obj_li = listitem_alloc(); + obj_li->li_tv = obj.val; + list_append(last_container.container.vval.v_list, obj_li); + } else if (last_container.stack_index == kv_size(*stack) - 2) { + if (!obj.didcolon) { + EMSG2(_("E474: Expected colon before dictionary value: %s"), + val_location); + clear_tv(&obj.val); + return FAIL; + } + ValuesStackItem key = kv_pop(*stack); + if (last_container.special_val == NULL) { + // These cases should have already been handled. + assert(!(key.is_special_string + || key.val.vval.v_string == NULL + || *key.val.vval.v_string == NUL)); + dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); + clear_tv(&key.val); + if (dict_add(last_container.container.vval.v_dict, obj_di) + == FAIL) { + assert(false); + } + obj_di->di_tv = obj.val; + } else { + list_T *const kv_pair = list_alloc(); + list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = listitem_alloc(); + key_li->li_tv = key.val; + list_append(kv_pair, key_li); + listitem_T *const val_li = listitem_alloc(); + val_li->li_tv = obj.val; + list_append(kv_pair, val_li); + } + } else { + // Object with key only + if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { + EMSG2(_("E474: Expected string key: %s"), *pp); + clear_tv(&obj.val); + return FAIL; + } else if (!obj.didcomma + && (last_container.special_val == NULL + && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { + EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); + clear_tv(&obj.val); + return FAIL; + } + // Handle empty key and key represented as special dictionary + if (last_container.special_val == NULL + && (obj.is_special_string + || obj.val.vval.v_string == NULL + || *obj.val.vval.v_string == NUL + || dict_find(last_container.container.vval.v_dict, + obj.val.vval.v_string, -1))) { + clear_tv(&obj.val); + + // Restart + (void) kv_pop(*container_stack); + ValuesStackItem last_container_val = + kv_A(*stack, last_container.stack_index); + while (kv_size(*stack) > last_container.stack_index) { + clear_tv(&(kv_pop(*stack).val)); + } + *pp = last_container.s; + *didcomma = last_container_val.didcomma; + *didcolon = last_container_val.didcolon; + *next_map_special = true; + return OK; + } + kv_push(ValuesStackItem, *stack, obj); + } + return OK; +} + +#define LENP(p, e) \ + ((int) ((e) - (p))), (p) +#define OBJ(obj_tv, is_sp_string, didcomma_, didcolon_) \ + ((ValuesStackItem) { \ + .is_special_string = (is_sp_string), \ + .val = (obj_tv), \ + .didcomma = (didcomma_), \ + .didcolon = (didcolon_), \ + }) + +#define POP(obj_tv, is_sp_string) \ + do { \ + if (json_decoder_pop(OBJ(obj_tv, is_sp_string, *didcomma, *didcolon), \ + stack, container_stack, \ + &p, next_map_special, didcomma, didcolon) \ + == FAIL) { \ + goto parse_json_string_fail; \ + } \ + if (*next_map_special) { \ + goto parse_json_string_ret; \ + } \ + } while (0) + +/// Parse JSON double-quoted string +/// +/// @param[in] conv Defines conversion necessary to convert UTF-8 string to +/// &encoding. +/// @param[in] buf Buffer being converted. +/// @param[in] buf_len Length of the buffer. +/// @param[in,out] pp Pointer to the start of the string. Must point to '"'. +/// Is advanced to the closing '"'. Also see +/// json_decoder_pop(), it may set pp to another location +/// and alter next_map_special, didcomma and didcolon. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[out] next_map_special Is set to true when dictionary is converted +/// to a special map, otherwise not touched. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int parse_json_string(vimconv_T *const conv, + const char *const buf, const size_t buf_len, + const char **const pp, + ValuesStack *const stack, + ContainerStack *const container_stack, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + const char *const e = buf + buf_len; + const char *p = *pp; + size_t len = 0; + const char *const s = ++p; + int ret = OK; + while (p < e && *p != '"') { + if (*p == '\\') { + p++; + if (p == e) { + emsgf(_("E474: Unfinished escape sequence: %.*s"), + (int) buf_len, buf); + goto parse_json_string_fail; + } + switch (*p) { + case 'u': { + if (p + 4 >= e) { + emsgf(_("E474: Unfinished unicode escape sequence: %.*s"), + (int) buf_len, buf); + goto parse_json_string_fail; + } else if (!ascii_isxdigit(p[1]) + || !ascii_isxdigit(p[2]) + || !ascii_isxdigit(p[3]) + || !ascii_isxdigit(p[4])) { + emsgf(_("E474: Expected four hex digits after \\u: %.*s"), + LENP(p - 1, e)); + goto parse_json_string_fail; + } + // One UTF-8 character below U+10000 can take up to 3 bytes, + // above up to 6, but they are encoded using two \u escapes. + len += 3; + p += 5; + break; + } + case '\\': + case '/': + case '"': + case 't': + case 'b': + case 'n': + case 'r': + case 'f': { + len++; + p++; + break; + } + default: { + emsgf(_("E474: Unknown escape sequence: %.*s"), LENP(p - 1, e)); + goto parse_json_string_fail; + } + } + } else { + uint8_t p_byte = (uint8_t) *p; + // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (p_byte < 0x20) { + emsgf(_("E474: ASCII control characters cannot be present " + "inside string: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } + const int ch = utf_ptr2char((char_u *) p); + // All characters above U+007F are encoded using two or more bytes + // and thus cannot possibly be equal to *p. But utf_ptr2char({0xFF, + // 0}) will return 0xFF, even though 0xFF cannot start any UTF-8 + // code point at all. + // + // The only exception is U+00C3 which is represented as 0xC3 0x83. + if (ch >= 0x80 && p_byte == ch + && !(ch == 0xC3 && p + 1 < e && (uint8_t) p[1] == 0x83)) { + emsgf(_("E474: Only UTF-8 strings allowed: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } else if (ch > 0x10FFFF) { + emsgf(_("E474: Only UTF-8 code points up to U+10FFFF " + "are allowed to appear unescaped: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } + const size_t ch_len = (size_t) utf_char2len(ch); + assert(ch_len == (size_t) (ch ? utf_ptr2len((char_u *) p) : 1)); + len += ch_len; + p += ch_len; + } + } + if (p == e || *p != '"') { + emsgf(_("E474: Expected string end: %.*s"), (int) buf_len, buf); + goto parse_json_string_fail; + } + if (len == 0) { + POP(((typval_T) { + .v_type = VAR_STRING, + .vval = { .v_string = NULL }, + }), false); + goto parse_json_string_ret; + } + char *str = xmalloc(len + 1); + int fst_in_pair = 0; + char *str_end = str; + bool hasnul = false; +#define PUT_FST_IN_PAIR(fst_in_pair, str_end) \ + do { \ + if (fst_in_pair != 0) { \ + str_end += utf_char2bytes(fst_in_pair, (char_u *) str_end); \ + fst_in_pair = 0; \ + } \ + } while (0) + for (const char *t = s; t < p; t++) { + if (t[0] != '\\' || t[1] != 'u') { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + } + if (*t == '\\') { + t++; + switch (*t) { + case 'u': { + const char ubuf[] = { t[1], t[2], t[3], t[4] }; + t += 4; + unsigned long ch; + vim_str2nr((char_u *) ubuf, NULL, NULL, + STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); + if (ch == 0) { + hasnul = true; + } + if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + fst_in_pair = (int) ch; + } else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END + && fst_in_pair != 0) { + const int full_char = ( + (int) (ch - SURROGATE_LO_START) + + ((fst_in_pair - SURROGATE_HI_START) << 10) + + SURROGATE_FIRST_CHAR); + str_end += utf_char2bytes(full_char, (char_u *) str_end); + fst_in_pair = 0; + } else { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + str_end += utf_char2bytes((int) ch, (char_u *) str_end); + } + break; + } + case '\\': + case '/': + case '"': + case 't': + case 'b': + case 'n': + case 'r': + case 'f': { + static const char escapes[] = { + ['\\'] = '\\', + ['/'] = '/', + ['"'] = '"', + ['t'] = TAB, + ['b'] = BS, + ['n'] = NL, + ['r'] = CAR, + ['f'] = FF, + }; + *str_end++ = escapes[(int) *t]; + break; + } + default: { + assert(false); + } + } + } else { + *str_end++ = *t; + } + } + PUT_FST_IN_PAIR(fst_in_pair, str_end); +#undef PUT_FST_IN_PAIR + if (conv->vc_type != CONV_NONE) { + size_t str_len = (size_t) (str_end - str); + char *const new_str = (char *) string_convert(conv, (char_u *) str, + &str_len); + if (new_str == NULL) { + emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"), + (int) str_len, str); + xfree(str); + goto parse_json_string_fail; + } + xfree(str); + str = new_str; + str_end = new_str + str_len; + } + if (hasnul) { + typval_T obj; + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(&obj, kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, str, (size_t) (str_end - str)) + == -1) { + clear_tv(&obj); + goto parse_json_string_fail; + } + xfree(str); + POP(obj, true); + } else { + *str_end = NUL; + POP(((typval_T) { + .v_type = VAR_STRING, + .vval = { .v_string = (char_u *) str }, + }), false); + } + goto parse_json_string_ret; +parse_json_string_fail: + ret = FAIL; +parse_json_string_ret: + *pp = p; + return ret; +} + +#undef POP + +/// Parse JSON number: both floating-point and integer +/// +/// Number format: `-?\d+(?:.\d+)?(?:[eE][+-]?\d+)?`. +/// +/// @param[in] buf Buffer being converted. +/// @param[in] buf_len Length of the buffer. +/// @param[in,out] pp Pointer to the start of the number. Must point to +/// a digit or a minus sign. Is advanced to the last +/// character of the number. Also see json_decoder_pop(), it +/// may set pp to another location and alter +/// next_map_special, didcomma and didcolon. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[out] next_map_special Is set to true when dictionary is converted +/// to a special map, otherwise not touched. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int parse_json_number(const char *const buf, const size_t buf_len, + const char **const pp, + ValuesStack *const stack, + ContainerStack *const container_stack, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + const char *const e = buf + buf_len; + const char *p = *pp; + int ret = OK; + const char *const s = p; + const char *ints = NULL; + const char *fracs = NULL; + const char *exps = NULL; + const char *exps_s = NULL; + if (*p == '-') { + p++; + } + ints = p; + if (p >= e) { + goto parse_json_number_check; + } + while (p < e && ascii_isdigit(*p)) { + p++; + } + if (p != ints + 1 && *ints == '0') { + emsgf(_("E474: Leading zeroes are not allowed: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } + if (p >= e || p == ints) { + goto parse_json_number_check; + } + if (*p == '.') { + p++; + fracs = p; + while (p < e && ascii_isdigit(*p)) { + p++; + } + if (p >= e || p == fracs) { + goto parse_json_number_check; + } + } + if (*p == 'e' || *p == 'E') { + p++; + exps_s = p; + if (p < e && (*p == '-' || *p == '+')) { + p++; + } + exps = p; + while (p < e && ascii_isdigit(*p)) { + p++; + } + } +parse_json_number_check: + if (p == ints) { + emsgf(_("E474: Missing number after minus sign: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } else if (p == fracs || exps_s == fracs + 1) { + emsgf(_("E474: Missing number after decimal dot: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } else if (p == exps) { + emsgf(_("E474: Missing exponent: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } + typval_T tv = { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + }; + const size_t exp_num_len = (size_t) (p - s); + if (fracs || exps) { + // Convert floating-point number + const size_t num_len = string2float(s, &tv.vval.v_float); + if (exp_num_len != num_len) { + emsgf(_("E685: internal error: while converting number \"%.*s\" " + "to float string2float consumed %zu bytes in place of %zu"), + (int) exp_num_len, s, num_len, exp_num_len); + } + tv.v_type = VAR_FLOAT; + } else { + // Convert integer + long nr; + int num_len; + vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); + if ((int) exp_num_len != num_len) { + emsgf(_("E685: internal error: while converting number \"%.*s\" " + "to integer vim_str2nr consumed %i bytes in place of %zu"), + (int) exp_num_len, s, num_len, exp_num_len); + } + tv.vval.v_number = (varnumber_T) nr; + } + if (json_decoder_pop(OBJ(tv, false, *didcomma, *didcolon), + stack, container_stack, + &p, next_map_special, didcomma, didcolon) == FAIL) { + goto parse_json_number_fail; + } + if (*next_map_special) { + goto parse_json_number_ret; + } + p--; + goto parse_json_number_ret; +parse_json_number_fail: + ret = FAIL; +parse_json_number_ret: + *pp = p; + return ret; +} + +#define POP(obj_tv, is_sp_string) \ + do { \ + if (json_decoder_pop(OBJ(obj_tv, is_sp_string, didcomma, didcolon), \ + &stack, &container_stack, \ + &p, &next_map_special, &didcomma, &didcolon) \ + == FAIL) { \ + goto json_decode_string_fail; \ + } \ + if (next_map_special) { \ + goto json_decode_string_cycle_start; \ + } \ + } while (0) + +/// Convert JSON string into VimL object +/// +/// @param[in] buf String to convert. UTF-8 encoding is assumed. +/// @param[in] buf_len Length of the string. +/// @param[out] rettv Location where to save results. +/// +/// @return OK in case of success, FAIL otherwise. +int json_decode_string(const char *const buf, const size_t buf_len, + typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *p = buf; + const char *const e = buf + buf_len; + while (p < e && (*p == ' ' || *p == TAB || *p == NL || *p == CAR)) { + p++; + } + if (p == e) { + EMSG(_("E474: Attempt to decode a blank string")); + return FAIL; + } + vimconv_T conv = { .vc_type = CONV_NONE }; + convert_setup(&conv, (char_u *) "utf-8", p_enc); + conv.vc_fail = true; + int ret = OK; + ValuesStack stack; + kv_init(stack); + ContainerStack container_stack; + kv_init(container_stack); + rettv->v_type = VAR_UNKNOWN; + bool didcomma = false; + bool didcolon = false; + bool next_map_special = false; + for (; p < e; p++) { +json_decode_string_cycle_start: + assert(*p == '{' || next_map_special == false); + switch (*p) { + case '}': + case ']': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: No container to close: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (*p == '}' && last_container.container.v_type != VAR_DICT) { + emsgf(_("E474: Closing list with curly bracket: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (*p == ']' && last_container.container.v_type != VAR_LIST) { + emsgf(_("E474: Closing dictionary with square bracket: %.*s"), + LENP(p, e)); + goto json_decode_string_fail; + } else if (didcomma) { + emsgf(_("E474: Trailing comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Expected value after colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.stack_index != kv_size(stack) - 1) { + assert(last_container.stack_index < kv_size(stack) - 1); + emsgf(_("E474: Expected value: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + if (kv_size(stack) == 1) { + p++; + (void) kv_pop(container_stack); + goto json_decode_string_after_cycle; + } else { + if (json_decoder_pop(kv_pop(stack), &stack, &container_stack, &p, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + goto json_decode_string_fail; + } + assert(!next_map_special); + break; + } + } + case ',': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: Comma not inside container: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (didcomma) { + emsgf(_("E474: Duplicate comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Comma after colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.container.v_type == VAR_DICT + && last_container.stack_index != kv_size(stack) - 1) { + emsgf(_("E474: Using comma in place of colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.special_val == NULL + ? (last_container.container.v_type == VAR_DICT + ? (DICT_LEN(last_container.container.vval.v_dict) == 0) + : (last_container.container.vval.v_list->lv_len == 0)) + : (last_container.special_val->lv_len == 0)) { + emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + didcomma = true; + continue; + } + case ':': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: Colon not inside container: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (last_container.container.v_type != VAR_DICT) { + emsgf(_("E474: Using colon not in dictionary: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.stack_index != kv_size(stack) - 2) { + emsgf(_("E474: Unexpected colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcomma) { + emsgf(_("E474: Colon after comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Duplicate colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + didcolon = true; + continue; + } + case ' ': + case TAB: + case NL: + case CAR: { + continue; + } + case 'n': { + if ((p + 3) >= e || strncmp(p + 1, "ull", 3) != 0) { + emsgf(_("E474: Expected null: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 3; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarNull }, + }), false); + break; + } + case 't': { + if ((p + 3) >= e || strncmp(p + 1, "rue", 3) != 0) { + emsgf(_("E474: Expected true: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 3; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarTrue }, + }), false); + break; + } + case 'f': { + if ((p + 4) >= e || strncmp(p + 1, "alse", 4) != 0) { + emsgf(_("E474: Expected false: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 4; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarFalse }, + }), false); + break; + } + case '"': { + if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + // Error message was already given + goto json_decode_string_fail; + } + if (next_map_special) { + goto json_decode_string_cycle_start; + } + break; + } + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + if (parse_json_number(buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + // Error message was already given + goto json_decode_string_fail; + } + if (next_map_special) { + goto json_decode_string_cycle_start; + } + break; + } + case '[': { + list_T *list = list_alloc(); + list->lv_refcount++; + typval_T tv = { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + }; + kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = NULL, + })); + kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + break; + } + case '{': { + typval_T tv; + list_T *val_list = NULL; + if (next_map_special) { + next_map_special = false; + val_list = list_alloc(); + val_list->lv_refcount++; + create_special_dict(&tv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = val_list }, + })); + } else { + dict_T *dict = dict_alloc(); + dict->dv_refcount++; + tv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; + } + kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = val_list, + })); + kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + break; + } + default: { + emsgf(_("E474: Unidentified byte: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + } + didcomma = false; + didcolon = false; + if (kv_size(container_stack) == 0) { + p++; + break; + } + } +json_decode_string_after_cycle: + for (; p < e; p++) { + switch (*p) { + case NL: + case ' ': + case TAB: + case CAR: { + break; + } + default: { + emsgf(_("E474: Trailing characters: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + } + } + if (kv_size(stack) == 1 && kv_size(container_stack) == 0) { + *rettv = kv_pop(stack).val; + goto json_decode_string_ret; + } + emsgf(_("E474: Unexpected end of input: %.*s"), (int) buf_len, buf); +json_decode_string_fail: + ret = FAIL; + while (kv_size(stack)) { + clear_tv(&(kv_pop(stack).val)); + } +json_decode_string_ret: + kv_destroy(stack); + kv_destroy(container_stack); + return ret; +} + +#undef LENP +#undef POP + +#undef OBJ + +#undef DICT_LEN + +/// Convert msgpack object to a VimL one +int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (mobj.type) { + case MSGPACK_OBJECT_NIL: { + *rettv = (typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarNull }, + }; + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + *rettv = (typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { + .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse + }, + }; + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + if (mobj.via.u64 <= VARNUMBER_MAX) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = (varnumber_T) mobj.via.u64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPInteger, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + uint64_t n = mobj.via.u64; + list_append_number(list, 1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + if (mobj.via.i64 >= VARNUMBER_MIN) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = (varnumber_T) mobj.via.i64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPInteger, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + uint64_t n = -((uint64_t) mobj.via.i64); + list_append_number(list, -1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_FLOAT: { + *rettv = (typval_T) { + .v_type = VAR_FLOAT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_float = mobj.via.f64 }, + }; + break; + } + case MSGPACK_OBJECT_STR: { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_BIN: { + if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { + *rettv = (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, + }; + break; + } + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPBinary, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_ARRAY: { + list_T *const list = list_alloc(); + list->lv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + }; + for (size_t i = 0; i < mobj.via.array.size; i++) { + listitem_T *const li = listitem_alloc(); + li->li_tv.v_type = VAR_UNKNOWN; + list_append(list, li); + if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_MAP: { + for (size_t i = 0; i < mobj.via.map.size; i++) { + if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR + || mobj.via.map.ptr[i].key.via.str.size == 0 + || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL, + mobj.via.map.ptr[i].key.via.str.size) != NULL) { + goto msgpack_to_vim_generic_map; + } + } + dict_T *const dict = dict_alloc(); + dict->dv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; + for (size_t i = 0; i < mobj.via.map.size; i++) { + dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) + + mobj.via.map.ptr[i].key.via.str.size); + memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, + mobj.via.map.ptr[i].key.via.str.size); + di->di_tv.v_type = VAR_UNKNOWN; + if (dict_add(dict, di) == FAIL) { + // Duplicate key: fallback to generic map + clear_tv(rettv); + xfree(di); + goto msgpack_to_vim_generic_map; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) { + return FAIL; + } + } + break; +msgpack_to_vim_generic_map: {} + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + for (size_t i = 0; i < mobj.via.map.size; i++) { + list_T *const kv_pair = list_alloc(); + list_append_list(list, kv_pair); + listitem_T *const key_li = listitem_alloc(); + key_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, key_li); + listitem_T *const val_li = listitem_alloc(); + val_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, val_li); + if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + return FAIL; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_EXT: { + list_T *const list = list_alloc(); + list->lv_refcount++; + list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = list_alloc(); + list_append_list(list, ext_val_list); + create_special_dict(rettv, kMPExt, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) ext_val_list, mobj.via.ext.ptr, + mobj.via.ext.size) == -1) { + return FAIL; + } + break; + } + } + return OK; +} diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h new file mode 100644 index 0000000000..5c25a64f7a --- /dev/null +++ b/src/nvim/eval/decode.h @@ -0,0 +1,13 @@ +#ifndef NVIM_EVAL_DECODE_H +#define NVIM_EVAL_DECODE_H + +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/decode.h.generated.h" +#endif +#endif // NVIM_EVAL_DECODE_H diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c new file mode 100644 index 0000000000..c651a50be9 --- /dev/null +++ b/src/nvim/eval/encode.c @@ -0,0 +1,1296 @@ +/// @file encode.c +/// +/// File containing functions for encoding and decoding VimL values. +/// +/// Split out from eval.c. + +#include <msgpack.h> +#include <inttypes.h> +#include <assert.h> +#include <math.h> + +#include "nvim/eval/encode.h" +#include "nvim/buffer_defs.h" // vimconv_T +#include "nvim/eval.h" +#include "nvim/eval_defs.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" +#include "nvim/memory.h" +#include "nvim/charset.h" // vim_isprintc() +#include "nvim/macros.h" +#include "nvim/ascii.h" +#include "nvim/vim.h" // For _() +#include "nvim/lib/kvec.h" + +#define ga_concat(a, b) ga_concat(a, (char_u *)b) +#define utf_ptr2char(b) utf_ptr2char((char_u *)b) +#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) +#define utf_char2len(b) ((size_t)utf_char2len(b)) +#define string_convert(a, b, c) \ + ((char *)string_convert((vimconv_T *)a, (char_u *)b, c)) +#define convert_setup(vcp, from, to) \ + (convert_setup(vcp, (char_u *)from, (char_u *)to)) + +/// Structure representing current VimL to messagepack conversion state +typedef struct { + enum { + kMPConvDict, ///< Convert dict_T *dictionary. + kMPConvList, ///< Convert list_T *list. + kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. + } type; + union { + struct { + dict_T *dict; ///< Currently converted dictionary. + hashitem_T *hi; ///< Currently converted dictionary item. + size_t todo; ///< Amount of items left to process. + } d; ///< State of dictionary conversion. + struct { + list_T *list; ///< Currently converted list. + listitem_T *li; ///< Currently converted list item. + } l; ///< State of list or generic mapping conversion. + } data; ///< Data to convert. +} MPConvStackVal; + +/// Stack used to convert VimL values to messagepack. +typedef kvec_t(MPConvStackVal) MPConvStack; + +const char *const encode_special_var_names[] = { + [kSpecialVarNull] = "null", + [kSpecialVarTrue] = "true", + [kSpecialVarFalse] = "false", +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/encode.c.generated.h" +#endif + +/// Msgpack callback for writing to readfile()-style list +int encode_list_write(void *data, const char *buf, size_t len) +{ + if (len == 0) { + return 0; + } + list_T *const list = (list_T *) data; + const char *const end = buf + len; + const char *line_end = buf; + listitem_T *li = list->lv_last; + + // Continue the last list element + if (li != NULL) { + line_end = xmemscan(buf, NL, len); + if (line_end != buf) { + const size_t line_length = (size_t)(line_end - buf); + char *str = (char *)li->li_tv.vval.v_string; + const size_t li_len = (str == NULL ? 0 : strlen(str)); + li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); + str = (char *)li->li_tv.vval.v_string + li_len; + memcpy(str, buf, line_length); + str[line_length] = 0; + memchrsub(str, NUL, NL, line_length); + } + line_end++; + } + + while (line_end < end) { + const char *line_start = line_end; + line_end = xmemscan(line_start, NL, (size_t) (end - line_start)); + char *str = NULL; + if (line_end != line_start) { + const size_t line_length = (size_t)(line_end - line_start); + str = xmemdupz(line_start, line_length); + memchrsub(str, NUL, NL, line_length); + } + list_append_allocated_string(list, str); + line_end++; + } + if (line_end == end) { + list_append_allocated_string(list, NULL); + } + return 0; +} + +/// Abort conversion to string after a recursion error. +static bool did_echo_string_emsg = false; + +/// Show a error message when converting to msgpack value +/// +/// @param[in] msg Error message to dump. Must contain exactly two %s that +/// will be replaced with what was being dumped: first with +/// something like “F†or “function argumentâ€, second with path +/// to the failed value. +/// @param[in] mpstack Path to the failed value. +/// @param[in] objname Dumped object name. +/// +/// @return FAIL. +static int conv_error(const char *const msg, const MPConvStack *const mpstack, + const char *const objname) + FUNC_ATTR_NONNULL_ALL +{ + garray_T msg_ga; + ga_init(&msg_ga, (int)sizeof(char), 80); + char *const key_msg = _("key %s"); + char *const key_pair_msg = _("key %s at index %i from special map"); + char *const idx_msg = _("index %i"); + for (size_t i = 0; i < kv_size(*mpstack); i++) { + if (i != 0) { + ga_concat(&msg_ga, ", "); + } + MPConvStackVal v = kv_A(*mpstack, i); + switch (v.type) { + case kMPConvDict: { + typval_T key_tv = { + .v_type = VAR_STRING, + .vval = { .v_string = (v.data.d.hi == NULL + ? v.data.d.dict->dv_hashtab.ht_array + : (v.data.d.hi - 1))->hi_key }, + }; + char *const key = encode_tv2string(&key_tv, NULL); + vim_snprintf((char *) IObuff, IOSIZE, key_msg, key); + xfree(key); + ga_concat(&msg_ga, IObuff); + break; + } + case kMPConvPairs: + case kMPConvList: { + int idx = 0; + const listitem_T *li; + for (li = v.data.l.list->lv_first; + li != NULL && li->li_next != v.data.l.li; + li = li->li_next) { + idx++; + } + if (v.type == kMPConvList + || li == NULL + || (li->li_tv.v_type != VAR_LIST + && li->li_tv.vval.v_list->lv_len <= 0)) { + vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); + ga_concat(&msg_ga, IObuff); + } else { + typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; + char *const key = encode_tv2echo(&key_tv, NULL); + vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); + xfree(key); + ga_concat(&msg_ga, IObuff); + } + break; + } + } + } + EMSG3(msg, objname, (kv_size(*mpstack) == 0 + ? _("itself") + : (char *) msg_ga.ga_data)); + ga_clear(&msg_ga); + return FAIL; +} + +/// Convert readfile()-style list to a char * buffer with length +/// +/// @param[in] list Converted list. +/// @param[out] ret_len Resulting buffer length. +/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is +/// zero. +/// +/// @return true in case of success, false in case of failure. +bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, + char **const ret_buf) + FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t len = 0; + if (list != NULL) { + for (const listitem_T *li = list->lv_first; + li != NULL; + li = li->li_next) { + if (li->li_tv.v_type != VAR_STRING) { + return false; + } + len++; + if (li->li_tv.vval.v_string != 0) { + len += STRLEN(li->li_tv.vval.v_string); + } + } + if (len) { + len--; + } + } + *ret_len = len; + if (len == 0) { + *ret_buf = NULL; + return true; + } + ListReaderState lrstate = encode_init_lrstate(list); + char *const buf = xmalloc(len); + size_t read_bytes; + if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) { + assert(false); + } + assert(len == read_bytes); + *ret_buf = buf; + return true; +} + +/// Read bytes from list +/// +/// @param[in,out] state Structure describing position in list from which +/// reading should start. Is updated to reflect position +/// at which reading ended. +/// @param[out] buf Buffer to write to. +/// @param[in] nbuf Buffer length. +/// @param[out] read_bytes Is set to amount of bytes read. +/// +/// @return OK when reading was finished, FAIL in case of error (i.e. list item +/// was not a string), NOTDONE if reading was successfull, but there are +/// more bytes to read. +int encode_read_from_list(ListReaderState *const state, char *const buf, + const size_t nbuf, size_t *const read_bytes) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *const buf_end = buf + nbuf; + char *p = buf; + while (p < buf_end) { + for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { + const char ch = (char) state->li->li_tv.vval.v_string[state->offset++]; + *p++ = (char) ((char) ch == (char) NL ? (char) NUL : (char) ch); + } + if (p < buf_end) { + state->li = state->li->li_next; + if (state->li == NULL) { + *read_bytes = (size_t) (p - buf); + return OK; + } + *p++ = NL; + if (state->li->li_tv.v_type != VAR_STRING) { + *read_bytes = (size_t) (p - buf); + return FAIL; + } + state->offset = 0; + state->li_length = (state->li->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(state->li->li_tv.vval.v_string)); + } + } + *read_bytes = nbuf; + return (state->offset < state->li_length || state->li->li_next != NULL + ? NOTDONE + : OK); +} + +/// Code for checking whether container references itself +/// +/// @param[in,out] val Container to check. +/// @param copyID_attr Name of the container attribute that holds copyID. +/// After checking whether value of this attribute is +/// copyID (variable) it is set to copyID. +#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ + do { \ + if ((val)->copyID_attr == copyID) { \ + CONV_RECURSE((val), conv_type); \ + } \ + (val)->copyID_attr = copyID; \ + } while (0) + +#define TV_STRLEN(tv) \ + (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string)) + +/// Define functions which convert VimL value to something else +/// +/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const +/// tv)` which returns OK or FAIL and helper functions. +/// +/// @param firstargtype Type of the first argument. It will be used to return +/// the results. +/// @param firstargname Name of the first argument. +/// @param name Name of the target converter. +#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ +static int name##_convert_one_value(firstargtype firstargname, \ + MPConvStack *const mpstack, \ + typval_T *const tv, \ + const int copyID, \ + const char *const objname) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + switch (tv->v_type) { \ + case VAR_STRING: { \ + CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \ + break; \ + } \ + case VAR_NUMBER: { \ + CONV_NUMBER(tv->vval.v_number); \ + break; \ + } \ + case VAR_FLOAT: { \ + CONV_FLOAT(tv->vval.v_float); \ + break; \ + } \ + case VAR_FUNC: { \ + CONV_FUNC(tv->vval.v_string); \ + break; \ + } \ + case VAR_LIST: { \ + if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ + CONV_EMPTY_LIST(); \ + break; \ + } \ + CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ + CONV_LIST_START(tv->vval.v_list); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvList, \ + .data = { \ + .l = { \ + .list = tv->vval.v_list, \ + .li = tv->vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_SPECIAL: { \ + switch (tv->vval.v_special) { \ + case kSpecialVarNull: { \ + CONV_NIL(); \ + break; \ + } \ + case kSpecialVarTrue: \ + case kSpecialVarFalse: { \ + CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ + break; \ + } \ + } \ + break; \ + } \ + case VAR_DICT: { \ + if (tv->vval.v_dict == NULL \ + || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ + CONV_EMPTY_DICT(); \ + break; \ + } \ + const dictitem_T *type_di; \ + const dictitem_T *val_di; \ + if (CONV_ALLOW_SPECIAL \ + && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ + && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_TYPE", -1)) != NULL \ + && type_di->di_tv.v_type == VAR_LIST \ + && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_VAL", -1)) != NULL) { \ + size_t i; \ + for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ + if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ + break; \ + } \ + } \ + if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + switch ((MessagePackType) i) { \ + case kMPNil: { \ + CONV_NIL(); \ + break; \ + } \ + case kMPBoolean: { \ + if (val_di->di_tv.v_type != VAR_NUMBER) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_BOOL(val_di->di_tv.vval.v_number); \ + break; \ + } \ + case kMPInteger: { \ + const list_T *val_list; \ + varnumber_T sign; \ + varnumber_T highest_bits; \ + varnumber_T high_bits; \ + varnumber_T low_bits; \ + /* List of 4 integers; first is signed (should be 1 or -1, but */ \ + /* this is not checked), second is unsigned and have at most */ \ + /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ + /* bits is not checked), other unsigned and have at most 31 */ \ + /* non-zero bits (number of bits is not checked).*/ \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 4 \ + || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ + || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ + || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ + || (highest_bits = \ + val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ + || (high_bits = \ + val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ + || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ + | (uint64_t) (((uint64_t) high_bits) << 31) \ + | (uint64_t) low_bits); \ + if (sign > 0) { \ + CONV_UNSIGNED_NUMBER(number); \ + } else { \ + CONV_NUMBER(-number); \ + } \ + break; \ + } \ + case kMPFloat: { \ + if (val_di->di_tv.v_type != VAR_FLOAT) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_FLOAT(val_di->di_tv.vval.v_float); \ + break; \ + } \ + case kMPString: \ + case kMPBinary: { \ + const bool is_string = ((MessagePackType) i == kMPString); \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ + &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + if (is_string) { \ + CONV_STR_STRING(buf, len); \ + } else { \ + CONV_STRING(buf, len); \ + } \ + xfree(buf); \ + break; \ + } \ + case kMPArray: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ + kMPConvList); \ + CONV_LIST_START(val_di->di_tv.vval.v_list); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvList, \ + .data = { \ + .l = { \ + .list = val_di->di_tv.vval.v_list, \ + .li = val_di->di_tv.vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPMap: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + list_T *const val_list = val_di->di_tv.vval.v_list; \ + if (val_list == NULL || val_list->lv_len == 0) { \ + CONV_EMPTY_DICT(); \ + break; \ + } \ + for (const listitem_T *li = val_list->lv_first; li != NULL; \ + li = li->li_next) { \ + if (li->li_tv.v_type != VAR_LIST \ + || li->li_tv.vval.v_list->lv_len != 2) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + } \ + CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ + CONV_DICT_START(val_list->lv_len); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvPairs, \ + .data = { \ + .l = { \ + .list = val_list, \ + .li = val_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPExt: { \ + const list_T *val_list; \ + varnumber_T type; \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 2 \ + || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ + || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ + || type < INT8_MIN \ + || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ + &len, &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_EXT_STRING(buf, len, type); \ + xfree(buf); \ + break; \ + } \ + } \ + break; \ + } \ +name##_convert_one_value_regular_dict: \ + CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ + CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvDict, \ + .data = { \ + .d = { \ + .dict = tv->vval.v_dict, \ + .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ + .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_UNKNOWN: { \ + EMSG2(_(e_intern2), #name "_convert_one_value()"); \ + return FAIL; \ + } \ + } \ + return OK; \ +} \ +\ +scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ + const char *const objname) \ + FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + const int copyID = get_copyID(); \ + MPConvStack mpstack; \ + kv_init(mpstack); \ + if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ + == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + while (kv_size(mpstack)) { \ + MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ + typval_T *cur_tv = NULL; \ + switch (cur_mpsv->type) { \ + case kMPConvDict: { \ + if (!cur_mpsv->data.d.todo) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ + CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.d.todo \ + != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ + CONV_DICT_BETWEEN_ITEMS(); \ + } \ + while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ + cur_mpsv->data.d.hi++; \ + } \ + dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ + cur_mpsv->data.d.todo--; \ + cur_mpsv->data.d.hi++; \ + CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ + CONV_DICT_AFTER_KEY(); \ + cur_tv = &di->di_tv; \ + break; \ + } \ + case kMPConvList: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + CONV_LIST_END(cur_mpsv->data.l.list); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + CONV_LIST_BETWEEN_ITEMS(); \ + } \ + cur_tv = &cur_mpsv->data.l.li->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + case kMPConvPairs: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + CONV_DICT_BETWEEN_ITEMS(); \ + } \ + const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ + CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \ + if (name##_convert_one_value(firstargname, &mpstack, \ + &kv_pair->lv_first->li_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + CONV_DICT_AFTER_KEY(); \ + cur_tv = &kv_pair->lv_last->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + } \ + assert(cur_tv != NULL); \ + if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + } \ + kv_destroy(mpstack); \ + return OK; \ +encode_vim_to_##name##_error_ret: \ + kv_destroy(mpstack); \ + return FAIL; \ +} + +#define CONV_STRING(buf, len) \ + do { \ + const char *const buf_ = (const char *) buf; \ + if (buf == NULL) { \ + ga_concat(gap, "''"); \ + } else { \ + const size_t len_ = (len); \ + ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \ + ga_append(gap, '\''); \ + for (size_t i = 0; i < len_; i++) { \ + if (buf_[i] == '\'') { \ + ga_append(gap, '\''); \ + } \ + ga_append(gap, buf_[i]); \ + } \ + ga_append(gap, '\''); \ + } \ + } while (0) + +#define CONV_STR_STRING(buf, len) \ + CONV_STRING(buf, len) + +#define CONV_EXT_STRING(buf, len, type) + +#define CONV_NUMBER(num) \ + do { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \ + ga_concat(gap, numbuf); \ + } while (0) + +#define CONV_FLOAT(flt) \ + do { \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + ga_concat(gap, (char_u *) "str2float('nan')"); \ + break; \ + } \ + case FP_INFINITE: { \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, (char_u *) "str2float('inf')"); \ + break; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + } \ + } \ + } while (0) + +#define CONV_FUNC(fun) \ + do { \ + ga_concat(gap, "function("); \ + CONV_STRING(fun, STRLEN(fun)); \ + ga_append(gap, ')'); \ + } while (0) + +#define CONV_EMPTY_LIST() \ + ga_concat(gap, "[]") + +#define CONV_LIST_START(lst) \ + ga_append(gap, '[') + +#define CONV_EMPTY_DICT() \ + ga_concat(gap, "{}") + +#define CONV_NIL() \ + ga_concat(gap, "v:null") + +#define CONV_BOOL(num) \ + ga_concat(gap, ((num)? "v:true": "v:false")) + +#define CONV_UNSIGNED_NUMBER(num) + +#define CONV_DICT_START(len) \ + ga_append(gap, '{') + +#define CONV_DICT_END() \ + ga_append(gap, '}') + +#define CONV_DICT_AFTER_KEY() \ + ga_concat(gap, ": ") + +#define CONV_DICT_BETWEEN_ITEMS() \ + ga_concat(gap, ", ") + +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) + +#define CONV_LIST_END(lst) \ + ga_append(gap, ']') + +#define CONV_LIST_BETWEEN_ITEMS() \ + CONV_DICT_BETWEEN_ITEMS() + +#define CONV_RECURSE(val, conv_type) \ + do { \ + if (!did_echo_string_emsg) { \ + /* Only give this message once for a recursive call to avoid */ \ + /* flooding the user with errors. */ \ + did_echo_string_emsg = true; \ + EMSG(_("E724: unable to correctly dump variable " \ + "with self-referencing container")); \ + } \ + char ebuf[NUMBUFLEN + 7]; \ + size_t backref = 0; \ + for (; backref < kv_size(*mpstack); backref++) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict) { \ + if ((void *) mpval.data.d.dict == (void *) (val)) { \ + break; \ + } \ + } else if (conv_type == kMPConvList) { \ + if ((void *) mpval.data.l.list == (void *) (val)) { \ + break; \ + } \ + } \ + } \ + } \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \ + ga_concat(gap, &ebuf[0]); \ + return OK; \ + } while (0) + +#define CONV_ALLOW_SPECIAL false + +DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) + +#undef CONV_RECURSE +#define CONV_RECURSE(val, conv_type) \ + do { \ + char ebuf[NUMBUFLEN + 7]; \ + size_t backref = 0; \ + for (; backref < kv_size(*mpstack); backref++) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict) { \ + if ((void *) mpval.data.d.dict == (void *) val) { \ + break; \ + } \ + } else if (conv_type == kMPConvList) { \ + if ((void *) mpval.data.l.list == (void *) val) { \ + break; \ + } \ + } \ + } \ + } \ + if (conv_type == kMPConvDict) { \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \ + } else { \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \ + } \ + ga_concat(gap, &ebuf[0]); \ + return OK; \ + } while (0) + +DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap) + +#undef CONV_RECURSE +#define CONV_RECURSE(val, conv_type) \ + do { \ + if (!did_echo_string_emsg) { \ + /* Only give this message once for a recursive call to avoid */ \ + /* flooding the user with errors. */ \ + did_echo_string_emsg = true; \ + EMSG(_("E724: unable to correctly dump variable " \ + "with self-referencing container")); \ + } \ + return OK; \ + } while (0) + +#undef CONV_ALLOW_SPECIAL +#define CONV_ALLOW_SPECIAL true + +#undef CONV_NIL +#define CONV_NIL() \ + ga_concat(gap, "null") + +#undef CONV_BOOL +#define CONV_BOOL(num) \ + ga_concat(gap, ((num)? "true": "false")) + +#undef CONV_UNSIGNED_NUMBER +#define CONV_UNSIGNED_NUMBER(num) \ + do { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \ + ga_concat(gap, numbuf); \ + } while (0) + +#undef CONV_FLOAT +#define CONV_FLOAT(flt) \ + do { \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + EMSG(_("E474: Unable to represent NaN value in JSON")); \ + return FAIL; \ + } \ + case FP_INFINITE: { \ + EMSG(_("E474: Unable to represent infinity in JSON")); \ + return FAIL; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + break; \ + } \ + } \ + } while (0) + +/// Last used p_enc value +/// +/// Generic pointer: it is not used as a string, only pointer comparisons are +/// performed. Must not be freed. +static const void *last_p_enc = NULL; + +/// Conversion setup for converting from last_p_enc to UTF-8 +static vimconv_T p_enc_conv = { + .vc_type = CONV_NONE, +}; + +/// Escape sequences used in JSON +static const char escapes[][3] = { + [BS] = "\\b", + [TAB] = "\\t", + [NL] = "\\n", + [CAR] = "\\r", + ['"'] = "\\\"", + ['\\'] = "\\\\", + [FF] = "\\f", +}; + +static const char xdigits[] = "0123456789ABCDEF"; + +/// Convert given string to JSON string +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] buf Converted string. +/// @param[in] len Converted string length. +/// +/// @return OK in case of success, FAIL otherwise. +static inline int convert_to_json_string(garray_T *const gap, + const char *const buf, + const size_t len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE +{ + const char *utf_buf = buf; + if (utf_buf == NULL) { + ga_concat(gap, "\"\""); + } else { + size_t utf_len = len; + char *tofree = NULL; + if (last_p_enc != (const void *) p_enc) { + p_enc_conv.vc_type = CONV_NONE; + convert_setup(&p_enc_conv, p_enc, "utf-8"); + p_enc_conv.vc_fail = true; + last_p_enc = p_enc; + } + if (p_enc_conv.vc_type != CONV_NONE) { + tofree = string_convert(&p_enc_conv, buf, &utf_len); + if (tofree == NULL) { + emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"), + utf_len, utf_buf); + return FAIL; + } + utf_buf = tofree; + } + size_t str_len = 0; + // Encode character as \u0000 if + // 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F). + // 2. &encoding is not UTF-8 and code point is above 0x7F. + // 3. &encoding is UTF-8 and code point is not printable according to + // utf_printable(). + // This is done to make it possible to :echo values when &encoding is not + // UTF-8. +#define ENCODE_RAW(p_enc_conv, ch) \ + (ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \ + ? utf_printable(ch) \ + : ch < 0x7F)) + for (size_t i = 0; i < utf_len;) { + const int ch = utf_ptr2char(utf_buf + i); + const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i)); + assert(shift > 0); + i += shift; + switch (ch) { + case BS: + case TAB: + case NL: + case FF: + case CAR: + case '"': + case '\\': { + str_len += 2; + break; + } + default: { + if (ch > 0x7F && shift == 1) { + emsgf(_("E474: String \"%.*s\" contains byte that does not start " + "any UTF-8 character"), + utf_len - (i - shift), utf_buf + i - shift); + xfree(tofree); + return FAIL; + } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) + || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) { + emsgf(_("E474: UTF-8 string contains code point which belongs " + "to a surrogate pair: %.*s"), + utf_len - (i - shift), utf_buf + i - shift); + xfree(tofree); + return FAIL; + } else if (ENCODE_RAW(p_enc_conv, ch)) { + str_len += shift; + } else { + str_len += ((sizeof("\\u1234") - 1) + * (size_t) (1 + (ch >= SURROGATE_FIRST_CHAR))); + } + break; + } + } + } + ga_append(gap, '"'); + ga_grow(gap, (int) str_len); + for (size_t i = 0; i < utf_len;) { + const int ch = utf_ptr2char(utf_buf + i); + const size_t shift = (ch == 0? 1: utf_char2len(ch)); + assert(shift > 0); + // Is false on invalid unicode, but this should already be handled. + assert(ch == 0 || shift == utf_ptr2len(utf_buf + i)); + switch (ch) { + case BS: + case TAB: + case NL: + case FF: + case CAR: + case '"': + case '\\': { + ga_concat_len(gap, escapes[ch], 2); + break; + } + default: { + if (ENCODE_RAW(p_enc_conv, ch)) { + ga_concat_len(gap, utf_buf + i, shift); + } else if (ch < SURROGATE_FIRST_CHAR) { + ga_concat_len(gap, ((const char[]) { + '\\', 'u', + xdigits[(ch >> (4 * 3)) & 0xF], + xdigits[(ch >> (4 * 2)) & 0xF], + xdigits[(ch >> (4 * 1)) & 0xF], + xdigits[(ch >> (4 * 0)) & 0xF], + }), sizeof("\\u1234") - 1); + } else { + const int tmp = ch - SURROGATE_FIRST_CHAR; + const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1)); + const int lo = SURROGATE_LO_END + ((tmp >> 0) & ((1 << 10) - 1)); + ga_concat_len(gap, ((const char[]) { + '\\', 'u', + xdigits[(hi >> (4 * 3)) & 0xF], + xdigits[(hi >> (4 * 2)) & 0xF], + xdigits[(hi >> (4 * 1)) & 0xF], + xdigits[(hi >> (4 * 0)) & 0xF], + '\\', 'u', + xdigits[(lo >> (4 * 3)) & 0xF], + xdigits[(lo >> (4 * 2)) & 0xF], + xdigits[(lo >> (4 * 1)) & 0xF], + xdigits[(lo >> (4 * 0)) & 0xF], + }), (sizeof("\\u1234") - 1) * 2); + } + break; + } + } + i += shift; + } + ga_append(gap, '"'); + xfree(tofree); + } + return OK; +} + +#undef CONV_STRING +#define CONV_STRING(buf, len) \ + do { \ + if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \ + return FAIL; \ + } \ + } while (0) + +#undef CONV_EXT_STRING +#define CONV_EXT_STRING(buf, len, type) \ + do { \ + xfree(buf); \ + EMSG(_("E474: Unable to convert EXT string to JSON")); \ + return FAIL; \ + } while (0) + +#undef CONV_FUNC +#define CONV_FUNC(fun) \ + return conv_error(_("E474: Error while dumping %s, %s: " \ + "attempt to dump function reference"), \ + mpstack, objname) + +/// Check whether given key can be used in json_encode() +/// +/// @param[in] tv Key to check. +static inline bool check_json_key(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv->v_type == VAR_STRING) { + return true; + } + if (tv->v_type != VAR_DICT) { + return false; + } + const dict_T *const spdict = tv->vval.v_dict; + if (spdict->dv_hashtab.ht_used != 2) { + return false; + } + const dictitem_T *type_di; + const dictitem_T *val_di; + if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + || type_di->di_tv.v_type != VAR_LIST + || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] + && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) + || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || val_di->di_tv.v_type != VAR_LIST) { + return false; + } + if (val_di->di_tv.vval.v_list == NULL) { + return true; + } + for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; + li != NULL; li = li->li_next) { + if (li->li_tv.v_type != VAR_STRING) { + return false; + } + } + return true; +} + +#undef CONV_SPECIAL_DICT_KEY_CHECK +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \ + do { \ + if (!check_json_key(&kv_pair->lv_first->li_tv)) { \ + EMSG(_("E474: Invalid key in special dictionary")); \ + goto encode_vim_to_##name##_error_ret; \ + } \ + } while (0) + +DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap) + +#undef CONV_STRING +#undef CONV_STR_STRING +#undef CONV_EXT_STRING +#undef CONV_NUMBER +#undef CONV_FLOAT +#undef CONV_FUNC +#undef CONV_EMPTY_LIST +#undef CONV_LIST_START +#undef CONV_EMPTY_DICT +#undef CONV_NIL +#undef CONV_BOOL +#undef CONV_UNSIGNED_NUMBER +#undef CONV_DICT_START +#undef CONV_DICT_END +#undef CONV_DICT_AFTER_KEY +#undef CONV_DICT_BETWEEN_ITEMS +#undef CONV_SPECIAL_DICT_KEY_CHECK +#undef CONV_LIST_END +#undef CONV_LIST_BETWEEN_ITEMS +#undef CONV_RECURSE +#undef CONV_ALLOW_SPECIAL + +/// Return a string with the string representation of a variable. +/// Puts quotes around strings, so that they can be parsed back by eval(). +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2string(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + encode_vim_to_string(&ga, tv, "encode_tv2string() argument"); + did_echo_string_emsg = false; + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +/// Return a string with the string representation of a variable. +/// Does not put quotes around strings, as ":echo" displays values. +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2echo(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) { + if (tv->vval.v_string != NULL) { + ga_concat(&ga, tv->vval.v_string); + } + } else { + encode_vim_to_echo(&ga, tv, ":echo argument"); + } + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +/// Return a string with the string representation of a variable. +/// Puts quotes around strings, so that they can be parsed back by eval(). +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2json(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + encode_vim_to_json(&ga, tv, "encode_tv2json() argument"); + did_echo_string_emsg = false; + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +#define CONV_STRING(buf, len) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_bin(packer, 0); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_bin(packer, len_); \ + msgpack_pack_bin_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_STR_STRING(buf, len) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_str(packer, 0); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_str(packer, len_); \ + msgpack_pack_str_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_EXT_STRING(buf, len, type) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_ext(packer, 0, (int8_t) type); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_ext(packer, len_, (int8_t) type); \ + msgpack_pack_ext_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_NUMBER(num) \ + msgpack_pack_int64(packer, (int64_t) (num)) + +#define CONV_FLOAT(flt) \ + msgpack_pack_double(packer, (double) (flt)) + +#define CONV_FUNC(fun) \ + return conv_error(_("E951: Error while dumping %s, %s: " \ + "attempt to dump function reference"), \ + mpstack, objname) + +#define CONV_EMPTY_LIST() \ + msgpack_pack_array(packer, 0) + +#define CONV_LIST_START(lst) \ + msgpack_pack_array(packer, (size_t) (lst)->lv_len) + +#define CONV_EMPTY_DICT() \ + msgpack_pack_map(packer, 0) + +#define CONV_NIL() \ + msgpack_pack_nil(packer) + +#define CONV_BOOL(num) \ + do { \ + if ((num)) { \ + msgpack_pack_true(packer); \ + } else { \ + msgpack_pack_false(packer); \ + } \ + } while (0) + +#define CONV_UNSIGNED_NUMBER(num) \ + msgpack_pack_uint64(packer, (num)) + +#define CONV_DICT_START(len) \ + msgpack_pack_map(packer, (size_t) (len)) + +#define CONV_DICT_END() + +#define CONV_DICT_AFTER_KEY() + +#define CONV_DICT_BETWEEN_ITEMS() + +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) + +#define CONV_LIST_END(lst) + +#define CONV_LIST_BETWEEN_ITEMS() + +#define CONV_RECURSE(val, conv_type) \ + return conv_error(_("E952: Unable to dump %s: " \ + "container references itself in %s"), \ + mpstack, objname) + +#define CONV_ALLOW_SPECIAL true + +DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) + +#undef CONV_STRING +#undef CONV_STR_STRING +#undef CONV_EXT_STRING +#undef CONV_NUMBER +#undef CONV_FLOAT +#undef CONV_FUNC +#undef CONV_EMPTY_LIST +#undef CONV_LIST_START +#undef CONV_EMPTY_DICT +#undef CONV_NIL +#undef CONV_BOOL +#undef CONV_UNSIGNED_NUMBER +#undef CONV_DICT_START +#undef CONV_DICT_END +#undef CONV_DICT_AFTER_KEY +#undef CONV_DICT_BETWEEN_ITEMS +#undef CONV_SPECIAL_DICT_KEY_CHECK +#undef CONV_LIST_END +#undef CONV_LIST_BETWEEN_ITEMS +#undef CONV_RECURSE +#undef CONV_ALLOW_SPECIAL diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h new file mode 100644 index 0000000000..9bc665253b --- /dev/null +++ b/src/nvim/eval/encode.h @@ -0,0 +1,75 @@ +#ifndef NVIM_EVAL_ENCODE_H +#define NVIM_EVAL_ENCODE_H + +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval.h" +#include "nvim/garray.h" +#include "nvim/vim.h" // For STRLEN + +/// Convert VimL value to msgpack string +/// +/// @param[out] packer Packer to save results in. +/// @param[in] tv Dumped value. +/// @param[in] objname Object name, used for error message. +/// +/// @return OK in case of success, FAIL otherwise. +int encode_vim_to_msgpack(msgpack_packer *const packer, + typval_T *const tv, + const char *const objname); + +/// Convert VimL value to :echo output +/// +/// @param[out] packer Packer to save results in. +/// @param[in] tv Dumped value. +/// @param[in] objname Object name, used for error message. +/// +/// @return OK in case of success, FAIL otherwise. +int encode_vim_to_echo(garray_T *const packer, + typval_T *const tv, + const char *const objname); + +/// Structure defining state for read_from_list() +typedef struct { + const listitem_T *li; ///< Item currently read. + size_t offset; ///< Byte offset inside the read item. + size_t li_length; ///< Length of the string inside the read item. +} ListReaderState; + +/// Initialize ListReaderState structure +static inline ListReaderState encode_init_lrstate(const list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + return (ListReaderState) { + .li = list->lv_first, + .offset = 0, + .li_length = (list->lv_first->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(list->lv_first->li_tv.vval.v_string)), + }; +} + +/// Array mapping values from SpecialVarValue enum to names +extern const char *const encode_special_var_names[]; + +/// First codepoint in high surrogates block +#define SURROGATE_HI_START 0xD800 + +/// Last codepoint in high surrogates block +#define SURROGATE_HI_END 0xDBFF + +/// First codepoint in low surrogates block +#define SURROGATE_LO_START 0xDC00 + +/// Last codepoint in low surrogates block +#define SURROGATE_LO_END 0xDFFF + +/// First character that needs to be encoded as surrogate pair +#define SURROGATE_FIRST_CHAR 0x10000 + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/encode.h.generated.h" +#endif +#endif // NVIM_EVAL_ENCODE_H diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index cdad1f3197..8ffc0c98ce 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -16,39 +16,52 @@ typedef double float_T; typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; -/* - * Structure to hold an internal variable without a name. - */ +/// Special variable values +typedef enum { + kSpecialVarFalse, ///< v:false + kSpecialVarTrue, ///< v:true + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED, ///< User lock, can be unlocked. + VAR_FIXED, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function referene, .v_string is used for function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_SPECIAL, ///< Special value (true, false, null), .v_special + ///< is used. +} VarType; + +/// Structure that holds an internal variable value typedef struct { - char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */ - char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */ + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. union { - varnumber_T v_number; /* number value */ - float_T v_float; /* floating number value */ - char_u *v_string; /* string value (can be NULL!) */ - list_T *v_list; /* list value (can be NULL!) */ - dict_T *v_dict; /* dict value (can be NULL!) */ - } vval; + varnumber_T v_number; ///< Number, for VAR_NUMBER. + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + } vval; ///< Actual value. } typval_T; -/* Values for "v_type". */ -#define VAR_UNKNOWN 0 -#define VAR_NUMBER 1 /* "v_number" is used */ -#define VAR_STRING 2 /* "v_string" is used */ -#define VAR_FUNC 3 /* "v_string" is function name */ -#define VAR_LIST 4 /* "v_list" is used */ -#define VAR_DICT 5 /* "v_dict" is used */ -#define VAR_FLOAT 6 /* "v_float" is used */ - /* Values for "dv_scope". */ #define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ #define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not allowed to mask existing functions */ -/* Values for "v_lock". */ -#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */ -#define VAR_FIXED 2 /* locked forever */ - /* * Structure to hold an item of a list: an internal variable without a name. */ @@ -107,19 +120,18 @@ typedef struct dictitem_S dictitem_T; #define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable #define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated -/* - * Structure to hold info about a Dictionary. - */ +/// Structure representing a Dictionary struct dictvar_S { - char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ - char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */ - int dv_refcount; /* reference count */ - int dv_copyID; /* ID used by deepcopy() */ - hashtab_T dv_hashtab; /* hashtab that refers to the items */ - dict_T *dv_copydict; /* copied dict used by deepcopy() */ - dict_T *dv_used_next; /* next dict in used dicts list */ - dict_T *dv_used_prev; /* previous dict in used dicts list */ - QUEUE watchers; // dictionary key watchers set by user code + VarLockStatus dv_lock; ///< Whole dictionary lock status. + char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. }; // structure used for explicit stack while garbage collecting hash tables diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5ea5beb478..d020bc8f20 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3,6 +3,7 @@ */ #include <assert.h> +#include <float.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> @@ -50,7 +51,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -274,17 +274,26 @@ static int linelen(int *has_tab) static char_u *sortbuf1; static char_u *sortbuf2; -static int sort_ic; /* ignore case */ -static int sort_nr; /* sort on number */ -static int sort_rx; /* sort on regex instead of skipping it */ +static int sort_ic; ///< ignore case +static int sort_nr; ///< sort on number +static int sort_rx; ///< sort on regex instead of skipping it +static int sort_flt; ///< sort on floating number -static int sort_abort; /* flag to indicate if sorting has been interrupted */ +static int sort_abort; ///< flag to indicate if sorting has been interrupted -/* Struct to store info to be sorted. */ +/// Struct to store info to be sorted. typedef struct { - linenr_T lnum; /* line number */ - long start_col_nr; /* starting column number or number */ - long end_col_nr; /* ending column number */ + linenr_T lnum; ///< line number + long start_col_nr; ///< starting column number or number + long end_col_nr; ///< ending column number + union { + struct { + long start_col_nr; ///< starting column number + long end_col_nr; ///< ending column number + } line; + long value; ///< value if sorting by integer + float_T value_flt; ///< value if sorting by float + } st_u; } sorti_T; @@ -303,21 +312,26 @@ static int sort_compare(const void *s1, const void *s2) if (got_int) sort_abort = TRUE; - /* When sorting numbers "start_col_nr" is the number, not the column - * number. */ - if (sort_nr) - result = l1.start_col_nr == l2.start_col_nr ? 0 - : l1.start_col_nr > l2.start_col_nr ? 1 : -1; - else { - /* We need to copy one line into "sortbuf1", because there is no - * guarantee that the first pointer becomes invalid when obtaining the - * second one. */ - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, - l1.end_col_nr - l1.start_col_nr + 1); - sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, - l2.end_col_nr - l2.start_col_nr + 1); - sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0; + // When sorting numbers "start_col_nr" is the number, not the column + // number. + if (sort_nr) { + result = l1.st_u.value == l2.st_u.value + ? 0 : l1.st_u.value > l2.st_u.value + ? 1 : -1; + } else if (sort_flt) { + result = l1.st_u.value_flt == l2.st_u.value_flt + ? 0 : l1.st_u.value_flt > l2.st_u.value_flt + ? 1 : -1; + } else { + // We need to copy one line into "sortbuf1", because there is no + // guarantee that the first pointer becomes invalid when obtaining the + // second one. + STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; + STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0; result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2); @@ -361,7 +375,7 @@ void ex_sort(exarg_T *eap) regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_rx = sort_nr = 0; + sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0; size_t format_found = 0; for (p = eap->arg; *p != NUL; ++p) { @@ -371,7 +385,10 @@ void ex_sort(exarg_T *eap) } else if (*p == 'r') { sort_rx = true; } else if (*p == 'n') { - sort_nr = 2; + sort_nr = 1; + format_found++; + } else if (*p == 'f') { + sort_flt = 1; format_found++; } else if (*p == 'b') { sort_what = STR2NR_BIN + STR2NR_FORCE; @@ -424,7 +441,8 @@ void ex_sort(exarg_T *eap) goto sortend; } - // From here on "sort_nr" is used as a flag for any number sorting. + // From here on "sort_nr" is used as a flag for any integer number + // sorting. sort_nr += sort_what; // Make an array with all line numbers. This avoids having to copy all @@ -453,7 +471,7 @@ void ex_sort(exarg_T *eap) end_col = 0; } - if (sort_nr) { + if (sort_nr || sort_flt) { // Make sure vim_str2nr doesn't read any digits past the end // of the match, by temporarily terminating the string there s2 = s + end_col; @@ -461,29 +479,42 @@ void ex_sort(exarg_T *eap) *s2 = NUL; // Sorting on number: Store the number itself. p = s + start_col; - if (sort_what & STR2NR_HEX) { - s = skiptohex(p); - } else if (sort_what & STR2NR_BIN) { - s = (char_u*) skiptobin((char*) p); - } else { - s = skiptodigit(p); - } - if (s > p && s[-1] == '-') { - // include preceding negative sign - s--; - } - if (*s == NUL) { - // empty line should sort before any number - nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; + if (sort_nr) { + if (sort_what & STR2NR_HEX) { + s = skiptohex(p); + } else if (sort_what & STR2NR_BIN) { + s = (char_u *)skiptobin((char *)p); + } else { + s = skiptodigit(p); + } + if (s > p && s[-1] == '-') { + s--; // include preceding negative sign + } + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value = -MAXLNUM; + } else { + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].st_u.value, NULL, 0); + } } else { - vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].start_col_nr, NULL, 0); + s = skipwhite(p); + if (*s == '+') { + s = skipwhite(s + 1); + } + + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX; + } else { + nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL); + } } *s2 = c; } else { // Store the column to sort at. - nrs[lnum - eap->line1].start_col_nr = start_col; - nrs[lnum - eap->line1].end_col_nr = end_col; + nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col; + nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col; } nrs[lnum - eap->line1].lnum = lnum; @@ -1326,15 +1357,17 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #endif size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. - + len += is_fish_shell ? sizeof("begin; ""; end") - 1 : sizeof("("")") - 1; - if (itmp != NULL) + if (itmp != NULL) { len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1; - if (otmp != NULL) + } + if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), - char_u *buf = xmalloc(len); + } + char *const buf = xmalloc(len); #if defined(UNIX) // Put delimiters around the command (for concatenated commands) when @@ -1342,19 +1375,19 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; - vim_snprintf((char *)buf, len, fmt, (char *)cmd); + vim_snprintf(buf, len, fmt, (char *)cmd); } else { - STRCPY(buf, cmd); + strncpy(buf, (char *) cmd, len); } if (itmp != NULL) { - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - STRCPY(buf, cmd); + strncpy(buf, cmd, len); if (itmp != NULL) { char_u *p; @@ -1362,55 +1395,56 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) // Don't do this when 'shellquote' is not empty, otherwise the // redirection would be inside the quotes. if (*p_shq == NUL) { - p = vim_strchr(buf, '|'); - if (p != NULL) + p = strchr(buf, '|'); + if (p != NULL) { *p = NUL; + } } - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); if (*p_shq == NUL) { - p = vim_strchr(cmd, '|'); + p = strchr(cmd, '|'); if (p != NULL) { - STRCAT(buf, " "); // Insert a space before the '|' for DOS - STRCAT(buf, p); + strncat(buf, " ", len); // Insert a space before the '|' for DOS + strncat(buf, p, len); } } } #endif if (otmp != NULL) { - append_redir(buf, len, p_srr, otmp); + append_redir(buf, len, (char *) p_srr, (char *) otmp); } - return buf; + return (char_u *) buf; } -/* - * Append output redirection for file "fname" to the end of string buffer - * "buf[buflen]" - * Works with the 'shellredir' and 'shellpipe' options. - * The caller should make sure that there is enough room: - * STRLEN(opt) + STRLEN(fname) + 3 - */ -void append_redir(char_u *buf, size_t buflen, char_u *opt, char_u *fname) +/// Append output redirection for the given file to the end of the buffer +/// +/// @param[out] buf Buffer to append to. +/// @param[in] buflen Buffer length. +/// @param[in] opt Separator or format string to append: will append +/// `printf(' ' . opt, fname)` if `%s` is found in `opt` or +/// a space, opt, a space and then fname if `%s` is not found +/// there. +/// @param[in] fname File name to append. +void append_redir(char *const buf, const size_t buflen, + const char *const opt, const char *const fname) { - char_u *p; - char_u *end; - - end = buf + STRLEN(buf); - /* find "%s" */ - for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p) { - if (p[1] == 's') /* found %s */ + char *const end = buf + strlen(buf); + // find "%s" + const char *p = opt; + for (; (p = strchr(p, '%')) != NULL; p++) { + if (p[1] == 's') { // found %s break; - if (p[1] == '%') /* skip %% */ - ++p; + } else if (p[1] == '%') { // skip %% + p++; + } } if (p != NULL) { - *end = ' '; /* not really needed? Not with sh, ksh or bash */ - vim_snprintf((char *)end + 1, (size_t)(buflen - (end + 1 - buf)), - (char *)opt, (char *)fname); - } else - vim_snprintf((char *)end, (size_t)(buflen - (end - buf)), - " %s %s", - (char *)opt, (char *)fname); + *end = ' '; // not really needed? Not with sh, ksh or bash + vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname); + } else { + vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname); + } } void print_line_no_prefix(linenr_T lnum, int use_number, int list) @@ -2093,15 +2127,13 @@ do_ecmd ( if ((command != NULL || newlnum > (linenr_T)0) && *get_vim_var_str(VV_SWAPCOMMAND) == NUL) { - char_u *p; - - /* Set v:swapcommand for the SwapExists autocommands. */ - size_t len = (command != NULL) ? STRLEN(command) + 3 : 30; - p = xmalloc(len); + // Set v:swapcommand for the SwapExists autocommands. + const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30; + char *const p = xmalloc(len); if (command != NULL) { - vim_snprintf((char *)p, len, ":%s\r", command); + vim_snprintf(p, len, ":%s\r", command); } else { - vim_snprintf((char *)p, len, "%" PRId64 "G", (int64_t)newlnum); + vim_snprintf(p, len, "%" PRId64 "G", (int64_t)newlnum); } set_vim_var_string(VV_SWAPCOMMAND, p, -1); did_set_swapcommand = TRUE; @@ -5016,7 +5048,9 @@ helptags_one ( /* * Sort the tags. */ - sort_strings((char_u **)ga.ga_data, ga.ga_len); + if (ga.ga_data != NULL) { + sort_strings((char_u **)ga.ga_data, ga.ga_len); + } /* * Check for duplicates. diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 6c8835b5c5..04fd88cc8d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2575,6 +2575,18 @@ return { func='ex_copymove', }, { + command='tcd', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { + command='tchdir', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { command='tNext', flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 71ea170e1c..a0a0b9d3a5 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2373,8 +2373,9 @@ static FILE *fopen_noinh_readbin(char *filename) # ifdef HAVE_FD_CLOEXEC { int fdflags = fcntl(fd_tmp, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + } } # endif @@ -3168,27 +3169,27 @@ static char_u *get_mess_env(void) */ void set_lang_var(void) { - char_u *loc; + const char *loc; # ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_CTYPE); + loc = get_locale_val(LC_CTYPE); # else - /* setlocale() not supported: use the default value */ - loc = (char_u *)"C"; + // setlocale() not supported: use the default value + loc = "C"; # endif set_vim_var_string(VV_CTYPE, loc, -1); /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall * back to LC_CTYPE if it's empty. */ # ifdef HAVE_WORKING_LIBINTL - loc = get_mess_env(); + loc = (char *) get_mess_env(); # else - loc = (char_u *)get_locale_val(LC_MESSAGES); + loc = get_locale_val(LC_MESSAGES); # endif set_vim_var_string(VV_LANG, loc, -1); # ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_TIME); + loc = get_locale_val(LC_TIME); # endif set_vim_var_string(VV_LC_TIME, loc, -1); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ef108ec783..648a3a8487 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2958,8 +2958,11 @@ set_one_cmd_context ( case CMD_chdir: case CMD_lcd: case CMD_lchdir: - if (xp->xp_context == EXPAND_FILES) + case CMD_tcd: + case CMD_tchdir: + if (xp->xp_context == EXPAND_FILES) { xp->xp_context = EXPAND_DIRECTORIES; + } break; case CMD_help: xp->xp_context = EXPAND_HELP; @@ -6815,36 +6818,55 @@ void free_cd_dir(void) #endif -/* - * Deal with the side effects of changing the current directory. - * When "local" is TRUE then this was after an ":lcd" command. - */ -void post_chdir(int local) +/// Deal with the side effects of changing the current directory. +/// +/// @param scope Scope of the function call (global, tab or window). +void post_chdir(CdScope scope) { + // The local directory of the current window is always overwritten. xfree(curwin->w_localdir); curwin->w_localdir = NULL; - if (local) { - /* If still in global directory, need to remember current - * directory as global directory. */ - if (globaldir == NULL && prev_dir != NULL) + + // Overwrite the local directory of the current tab page for `cd` and `tcd` + if (scope >= kCdScopeTab) { + xfree(curtab->localdir); + curtab->localdir = NULL; + } + + if (scope < kCdScopeGlobal) { + // If still in global directory, need to remember current directory as + // global directory. + if (globaldir == NULL && prev_dir != NULL) { globaldir = vim_strsave(prev_dir); - /* Remember this local directory for the window. */ - if (os_dirname(NameBuff, MAXPATHL) == OK) - curwin->w_localdir = vim_strsave(NameBuff); - } else { - /* We are now in the global directory, no need to remember its - * name. */ + } + } + + switch (scope) { + case kCdScopeGlobal: + // We are now in the global directory, no need to remember its name. xfree(globaldir); globaldir = NULL; + break; + case kCdScopeTab: + // Remember this local directory for the tab page. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curtab->localdir = vim_strsave(NameBuff); + } + break; + case kCdScopeWindow: + // Remember this local directory for the window. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curwin->w_localdir = vim_strsave(NameBuff); + } + break; } shorten_fnames(TRUE); } -/* - * ":cd", ":lcd", ":chdir" and ":lchdir". - */ + +/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. void ex_cd(exarg_T *eap) { char_u *new_dir; @@ -6885,10 +6907,25 @@ void ex_cd(exarg_T *eap) new_dir = NameBuff; } #endif - if (new_dir == NULL || vim_chdir(new_dir)) + if (vim_chdir(new_dir)) { EMSG(_(e_failed)); - else { - post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir); + } else { + CdScope scope = kCdScopeGlobal; // Depends on command invoked + + switch (eap->cmdidx) { + case CMD_tcd: + case CMD_tchdir: + scope = kCdScopeTab; + break; + case CMD_lcd: + case CMD_lchdir: + scope = kCdScopeWindow; + break; + default: + break; + } + + post_chdir(scope); /* Echo the new current directory if the command was typed. */ if (KeyTyped || p_verbose >= 5) @@ -7422,10 +7459,10 @@ static int mksession_nl = FALSE; /* use NL only in put_eol() */ static void ex_mkrc(exarg_T *eap) { FILE *fd; - int failed = FALSE; - int view_session = FALSE; - int using_vdir = FALSE; /* using 'viewdir'? */ - char_u *viewFile = NULL; + int failed = false; + int view_session = false; + int using_vdir = false; // using 'viewdir'? + char *viewFile = NULL; unsigned *flagp; if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) { @@ -7436,32 +7473,34 @@ static void ex_mkrc(exarg_T *eap) * short file name when 'acd' is set, that is checked later. */ did_lcd = FALSE; - char_u *fname; - /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */ + char *fname; + // ":mkview" or ":mkview 9": generate file name with 'viewdir' if (eap->cmdidx == CMD_mkview && (*eap->arg == NUL || (ascii_isdigit(*eap->arg) && eap->arg[1] == NUL))) { - eap->forceit = TRUE; - fname = (char_u *)get_view_file(*eap->arg); - if (fname == NULL) + eap->forceit = true; + fname = get_view_file(*eap->arg); + if (fname == NULL) { return; + } viewFile = fname; - using_vdir = TRUE; - } else if (*eap->arg != NUL) - fname = eap->arg; - else if (eap->cmdidx == CMD_mkvimrc) - fname = (char_u *)VIMRC_FILE; - else if (eap->cmdidx == CMD_mksession) - fname = (char_u *)SESSION_FILE; - else - fname = (char_u *)EXRC_FILE; + using_vdir = true; + } else if (*eap->arg != NUL) { + fname = (char *) eap->arg; + } else if (eap->cmdidx == CMD_mkvimrc) { + fname = VIMRC_FILE; + } else if (eap->cmdidx == CMD_mksession) { + fname = SESSION_FILE; + } else { + fname = EXRC_FILE; + } /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { vim_mkdir_emsg(p_vdir, 0755); } - fd = open_exfile(fname, eap->forceit, WRITEBIN); + fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); if (fd != NULL) { if (eap->cmdidx == CMD_mkview) flagp = &vop_flags; @@ -7505,8 +7544,9 @@ static void ex_mkrc(exarg_T *eap) || os_chdir((char *)dirnow) != 0) *dirnow = NUL; if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) { - if (vim_chdirfile(fname) == OK) - shorten_fnames(TRUE); + if (vim_chdirfile((char_u *) fname) == OK) { + shorten_fnames(true); + } } else if (*dirnow != NUL && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) { if (os_chdir((char *)globaldir) == 0) @@ -7551,15 +7591,14 @@ static void ex_mkrc(exarg_T *eap) failed |= fclose(fd); - if (failed) + if (failed) { EMSG(_(e_write)); - else if (eap->cmdidx == CMD_mksession) { - /* successful session write - set this_session var */ - char_u *tbuf; - - tbuf = xmalloc(MAXPATHL); - if (vim_FullName((char *)fname, (char *)tbuf, MAXPATHL, FALSE) == OK) + } else if (eap->cmdidx == CMD_mksession) { + // successful session write - set this_session var + char *const tbuf = xmalloc(MAXPATHL); + if (vim_FullName(fname, tbuf, MAXPATHL, false) == OK) { set_vim_var_string(VV_THIS_SESSION, tbuf, -1); + } xfree(tbuf); } #ifdef MKSESSION_NL diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index a5a4edbbbf..7af3ee233c 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -19,6 +19,18 @@ #define EXMODE_NORMAL 1 #define EXMODE_VIM 2 +/// The scope of a command. +/// +/// The lower a number, the deeper the scope. +typedef enum { + kCdScopeWindow, ///< Affects one window. + kCdScopeTab, ///< Affects one tab page. + kCdScopeGlobal, ///< Affects the entire instance of NeoVim. +} CdScope; + +/// Last `:cd` scope defined. +#define MAX_CD_SCOPE kCdScopeGlobal + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_docmd.h.generated.h" #endif diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 6fc74c7ad0..41ad96a378 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -569,17 +569,19 @@ static void catch_exception(except_T *excp) { excp->caught = caught_stack; caught_stack = excp; - set_vim_var_string(VV_EXCEPTION, excp->value, -1); + set_vim_var_string(VV_EXCEPTION, (char *) excp->value, -1); if (*excp->throw_name != NUL) { - if (excp->throw_lnum != 0) + if (excp->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64), - excp->throw_name, (int64_t)excp->throw_lnum); - else + excp->throw_name, (int64_t)excp->throw_lnum); + } else { vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was typed. */ + } + set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1); + } else { + // throw_name not set on an exception from a command that was typed. set_vim_var_string(VV_THROWPOINT, NULL, -1); + } if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; @@ -614,20 +616,22 @@ static void finish_exception(except_T *excp) EMSG(_(e_internal)); caught_stack = caught_stack->caught; if (caught_stack != NULL) { - set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1); + set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1); if (*caught_stack->throw_name != NUL) { - if (caught_stack->throw_lnum != 0) + if (caught_stack->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, - _("%s, line %" PRId64), caught_stack->throw_name, - (int64_t)caught_stack->throw_lnum); - else + _("%s, line %" PRId64), caught_stack->throw_name, + (int64_t)caught_stack->throw_lnum); + } else { vim_snprintf((char *)IObuff, IOSIZE, "%s", - caught_stack->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was - * typed. */ + caught_stack->throw_name); + } + set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1); + } else { + // throw_name not set on an exception from a command that was + // typed. set_vim_var_string(VV_THROWPOINT, NULL, -1); + } } else { set_vim_var_string(VV_EXCEPTION, NULL, -1); set_vim_var_string(VV_THROWPOINT, NULL, -1); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 383cd47dbe..c7870b9f69 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -45,7 +45,6 @@ #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/undo.h" @@ -2139,9 +2138,10 @@ readfile_charconvert ( else { close(*fdp); /* close the input file, ignore errors */ *fdp = -1; - if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc, - fname, tmpname) == FAIL) + if (eval_charconvert((char *) fenc, enc_utf8 ? "utf-8" : (char *) p_enc, + (char *) fname, (char *) tmpname) == FAIL) { errmsg = (char_u *)_("Conversion with 'charconvert' failed"); + } if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) { errmsg = (char_u *)_("can't read output of 'charconvert'"); } @@ -3435,9 +3435,9 @@ restore_backup: * with 'charconvert' to (overwrite) the output file. */ if (end != 0) { - if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc, - wfname, fname) == FAIL) { - write_info.bw_conv_error = TRUE; + if (eval_charconvert(enc_utf8 ? "utf-8" : (char *) p_enc, (char *) fenc, + (char *) wfname, (char *) fname) == FAIL) { + write_info.bw_conv_error = true; end = 0; } } @@ -4740,7 +4740,6 @@ buf_check_timestamp ( { int retval = 0; char_u *path; - char_u *tbuf; char *mesg = NULL; char *mesg2 = ""; int helpmesg = FALSE; @@ -4810,19 +4809,17 @@ buf_check_timestamp ( else reason = "time"; - /* - * Only give the warning if there are no FileChangedShell - * autocommands. - * Avoid being called recursively by setting "busy". - */ - busy = TRUE; - set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1); - set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1); - ++allbuf_lock; + // Only give the warning if there are no FileChangedShell + // autocommands. + // Avoid being called recursively by setting "busy". + busy = true; + set_vim_var_string(VV_FCS_REASON, reason, -1); + set_vim_var_string(VV_FCS_CHOICE, "", -1); + allbuf_lock++; n = apply_autocmds(EVENT_FILECHANGEDSHELL, - buf->b_fname, buf->b_fname, FALSE, buf); - --allbuf_lock; - busy = FALSE; + buf->b_fname, buf->b_fname, false, buf); + allbuf_lock--; + busy = false; if (n) { if (!buf_valid(buf)) EMSG(_("E246: FileChangedShell autocommand deleted buffer")); @@ -4876,35 +4873,39 @@ buf_check_timestamp ( if (mesg != NULL) { path = home_replace_save(buf, buf->b_fname); - if (!helpmesg) + if (!helpmesg) { mesg2 = ""; - tbuf = xmalloc(STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2); - sprintf((char *)tbuf, mesg, path); - /* Set warningmsg here, before the unimportant and output-specific - * mesg2 has been appended. */ + } + const size_t tbuf_len = STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2; + char *const tbuf = xmalloc(tbuf_len); + snprintf(tbuf, tbuf_len, mesg, path); + // Set warningmsg here, before the unimportant and output-specific + // mesg2 has been appended. set_vim_var_string(VV_WARNINGMSG, tbuf, -1); if (can_reload) { if (*mesg2 != NUL) { - STRCAT(tbuf, "\n"); - STRCAT(tbuf, mesg2); + strncat(tbuf, "\n", tbuf_len); + strncat(tbuf, mesg2, tbuf_len); + } + if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf, + (char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) { + reload = true; } - if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf, - (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2) - reload = TRUE; } else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) { if (*mesg2 != NUL) { - STRCAT(tbuf, "; "); - STRCAT(tbuf, mesg2); + strncat(tbuf, "; ", tbuf_len); + strncat(tbuf, mesg2, tbuf_len); } EMSG(tbuf); retval = 2; } else { if (!autocmd_busy) { msg_start(); - msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST); - if (*mesg2 != NUL) + msg_puts_attr((char_u *) tbuf, hl_attr(HLF_E) + MSG_HIST); + if (*mesg2 != NUL) { msg_puts_attr((char_u *)mesg2, hl_attr(HLF_W) + MSG_HIST); + } msg_clr_eos(); (void)msg_end(); if (emsg_silent == 0) { @@ -5114,6 +5115,147 @@ void forward_slash(char_u *fname) } #endif +/// Name of Vim's own temp dir. Ends in a slash. +static char_u *vim_tempdir = NULL; + +/// Create a directory for private use by this instance of Neovim. +/// This is done once, and the same directory is used for all temp files. +/// This method avoids security problems because of symlink attacks et al. +/// It's also a bit faster, because we only need to check for an existing +/// file when creating the directory and not for each temp file. +static void vim_maketempdir(void) +{ + static const char *temp_dirs[] = TEMP_DIR_NAMES; + // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. + char_u template[TEMP_FILE_PATH_MAXLEN]; + char_u path[TEMP_FILE_PATH_MAXLEN]; + for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) { + // Expand environment variables, leave room for "/nvimXXXXXX/999999999" + expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); + if (!os_isdir(template)) { // directory doesn't exist + continue; + } + + add_pathsep((char *)template); + // Concatenate with temporary directory name pattern + STRCAT(template, "nvimXXXXXX"); + + if (os_mkdtemp((const char *)template, (char *)path) != 0) { + continue; + } + + if (vim_settempdir((char *)path)) { + // Successfully created and set temporary directory so stop trying. + break; + } else { + // Couldn't set `vim_tempdir` to `path` so remove created directory. + os_rmdir((char *)path); + } + } +} + +/// Delete "name" and everything in it, recursively. +/// @param name The path which should be deleted. +/// @return 0 for success, -1 if some file was not deleted. +int delete_recursive(char_u *name) +{ + int result = 0; + + if (os_isrealdir(name)) { + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT + + char_u **files; + int file_count; + char_u *exp = vim_strsave(NameBuff); + if (gen_expand_wildcards(1, &exp, &file_count, &files, + EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS + | EW_DODOT | EW_EMPTYOK) == OK) { + for (int i = 0; i < file_count; i++) { + if (delete_recursive(files[i]) != 0) { + result = -1; + } + } + FreeWild(file_count, files); + } else { + result = -1; + } + + xfree(exp); + os_rmdir((char *)name); + } else { + result = os_remove((char *)name) == 0 ? 0 : -1; + } + + return result; +} + +/// Delete the temp directory and all files it contains. +void vim_deltempdir(void) +{ + if (vim_tempdir != NULL) { + // remove the trailing path separator + path_tail(vim_tempdir)[-1] = NUL; + delete_recursive(vim_tempdir); + xfree(vim_tempdir); + vim_tempdir = NULL; + } +} + +/// Get the name of temp directory. This directory would be created on the first +/// call to this function. +char_u *vim_gettempdir(void) +{ + if (vim_tempdir == NULL) { + vim_maketempdir(); + } + + return vim_tempdir; +} + +/// Set Neovim own temporary directory name to `tempdir`. This directory should +/// be already created. Expand this name to a full path and put it in +/// `vim_tempdir`. This avoids that using `:cd` would confuse us. +/// +/// @param tempdir must be no longer than MAXPATHL. +/// +/// @return false if we run out of memory. +static bool vim_settempdir(char *tempdir) +{ + char *buf = verbose_try_malloc(MAXPATHL + 2); + if (!buf) { + return false; + } + vim_FullName(tempdir, buf, MAXPATHL, false); + add_pathsep(buf); + vim_tempdir = (char_u *)xstrdup(buf); + xfree(buf); + return true; +} + +/// Return a unique name that can be used for a temp file. +/// +/// @note The temp file is NOT created. +/// +/// @return pointer to the temp file name or NULL if Neovim can't create +/// temporary directory for its own temporary files. +char_u *vim_tempname(void) +{ + // Temp filename counter. + static uint32_t temp_count; + + char_u *tempdir = vim_gettempdir(); + if (!tempdir) { + return NULL; + } + + // There is no need to check if the file exists, because we own the directory + // and nobody else creates a file in it. + char_u template[TEMP_FILE_PATH_MAXLEN]; + snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, + "%s%" PRIu32, tempdir, temp_count++); + return vim_strsave(template); +} + /* * Code for automatic commands. diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 70ab4ced75..ac3cf959c8 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1699,14 +1699,14 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, did_emsg = FALSE; if (*wp->w_p_fdt != NUL) { - char_u dashes[MAX_LEVEL + 2]; + char dashes[MAX_LEVEL + 2]; win_T *save_curwin; int level; char_u *p; - /* Set "v:foldstart" and "v:foldend". */ - set_vim_var_nr(VV_FOLDSTART, lnum); - set_vim_var_nr(VV_FOLDEND, lnume); + // Set "v:foldstart" and "v:foldend". + set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum); + set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume); /* Set "v:folddashes" to a string of "level" dashes. */ /* Set "v:foldlevel" to "level". */ @@ -1716,7 +1716,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, memset(dashes, '-', (size_t)level); dashes[level] = NUL; set_vim_var_string(VV_FOLDDASHES, dashes, -1); - set_vim_var_nr(VV_FOLDLEVEL, (long)level); + set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T) level); /* skip evaluating foldtext on errors */ if (!got_fdt_error) { @@ -2110,10 +2110,11 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, */ if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level && flp->lvl > 0) { - foldFind(gap, startlnum - 1, &fp); + (void)foldFind(gap, startlnum - 1, &fp); if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len - || fp->fd_top >= startlnum) + || fp->fd_top >= startlnum) { fp = NULL; + } } /* @@ -2167,13 +2168,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, } } if (lvl < level + i) { - foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); - if (fp2 != NULL) + (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); + if (fp2 != NULL) { bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top; - } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) - finish = TRUE; - else + } + } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) { + finish = true; + } else { break; + } } /* At the start of the first nested fold and at the end of the current @@ -2676,7 +2679,7 @@ static void foldlevelExpr(fline_T *flp) win = curwin; curwin = flp->wp; curbuf = flp->wp->w_buffer; - set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T) lnum); flp->start = 0; flp->had_end = flp->end; diff --git a/src/nvim/garray.c b/src/nvim/garray.c index e6cbd9332b..98cec69b54 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -188,12 +188,23 @@ void ga_concat(garray_T *gap, const char_u *restrict s) return; } - int len = (int)strlen((char *) s); + ga_concat_len(gap, (const char *restrict) s, strlen((char *) s)); +} + +/// Concatenate a string to a growarray which contains characters +/// +/// @param[out] gap Growarray to modify. +/// @param[in] s String to concatenate. +/// @param[in] len String length. +void ga_concat_len(garray_T *const gap, const char *restrict s, + const size_t len) + FUNC_ATTR_NONNULL_ALL +{ if (len) { - ga_grow(gap, len); + ga_grow(gap, (int) len); char *data = gap->ga_data; - memcpy(data + gap->ga_len, s, (size_t)len); - gap->ga_len += len; + memcpy(data + gap->ga_len, s, len); + gap->ga_len += (int) len; } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 40b5718071..49d1de21d9 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -4,17 +4,7 @@ #include <stdbool.h> #include <inttypes.h> -// EXTERN is only defined in main.c. That's where global variables are -// actually defined and initialized. -#ifndef EXTERN -# define EXTERN extern -# define INIT(...) -#else -# ifndef INIT -# define INIT(...) __VA_ARGS__ -# endif -#endif - +#include "nvim/macros.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" #include "nvim/mbyte.h" diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index ab8959239b..6ce8954fef 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -32,7 +32,6 @@ #include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -2780,11 +2779,13 @@ void mch_print_end(prt_settings_T *psettings) } prt_message((char_u *)_("Sending to printer...")); - /* Not printing to a file: use 'printexpr' to print the file. */ - if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL) + // Not printing to a file: use 'printexpr' to print the file. + if (eval_printexpr((char *) prt_ps_file_name, (char *) psettings->arguments) + == FAIL) { EMSG(_("E365: Failed to print PostScript file")); - else + } else { prt_message((char_u *)_("Print job sent.")); + } } mch_print_cleanup(); diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index a143490356..2f9ec0b3ff 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -27,7 +27,6 @@ #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -1063,8 +1062,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m", *qfpos == '-', cmdline) > 0) { if (postponed_split != 0) { - win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); + (void)win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags); RESET_BINDING(curwin); postponed_split = 0; } diff --git a/src/nvim/indent.c b/src/nvim/indent.c index d3008185dc..f197669a97 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -529,7 +529,7 @@ int get_expr_indent(void) save_pos = curwin->w_cursor; save_curswant = curwin->w_curswant; save_set_curswant = curwin->w_set_curswant; - set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T) curwin->w_cursor.lnum); if (use_sandbox) { sandbox++; diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 53ecf232c6..b41ef0cc9f 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -60,6 +60,7 @@ int main() { #define kv_pop(v) ((v).items[--(v).size]) #define kv_size(v) ((v).size) #define kv_max(v) ((v).capacity) +#define kv_last(v) kv_A(v, kv_size(v) - 1) #define kv_resize(type, v, s) ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity)) diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 26ab5a7de7..5f69fa2f6a 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -1,6 +1,17 @@ #ifndef NVIM_MACROS_H #define NVIM_MACROS_H +// EXTERN is only defined in main.c. That's where global variables are +// actually defined and initialized. +#ifndef EXTERN +# define EXTERN extern +# define INIT(...) +#else +# ifndef INIT +# define INIT(...) __VA_ARGS__ +# endif +#endif + #ifndef MIN # define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif diff --git a/src/nvim/main.c b/src/nvim/main.c index 23ced5ebe5..71a972e8f6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -238,8 +238,8 @@ int main(int argc, char **argv) check_and_set_isatty(¶ms); // Get the name with which Nvim was invoked, with and without path. - set_vim_var_string(VV_PROGPATH, (char_u *)argv[0], -1); - set_vim_var_string(VV_PROGNAME, path_tail((char_u *)argv[0]), -1); + set_vim_var_string(VV_PROGPATH, argv[0], -1); + set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1); event_init(); /* @@ -1141,10 +1141,11 @@ scripterror: /* If there is a "+123" or "-c" command, set v:swapcommand to the first * one. */ if (parmp->n_commands > 0) { - p = xmalloc(STRLEN(parmp->commands[0]) + 3); - sprintf((char *)p, ":%s\r", parmp->commands[0]); - set_vim_var_string(VV_SWAPCOMMAND, p, -1); - xfree(p); + const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3; + char *const swcmd = xmalloc(swcmd_len); + snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]); + set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1); + xfree(swcmd); } TIME_MSG("parsing arguments"); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index b568279d7d..f82f88a88f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -65,7 +65,6 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" @@ -3194,7 +3193,7 @@ attention_message ( */ static int do_swapexists(buf_T *buf, char_u *fname) { - set_vim_var_string(VV_SWAPNAME, fname, -1); + set_vim_var_string(VV_SWAPNAME, (char *) fname, -1); set_vim_var_string(VV_SWAPCHOICE, NULL, -1); /* Trigger SwapExists autocommands with <afile> set to the file being diff --git a/src/nvim/message.c b/src/nvim/message.c index 1dd71baaa4..97b098c6d2 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -61,14 +61,8 @@ static int confirm_msg_used = FALSE; /* displaying confirm_msg */ static char_u *confirm_msg = NULL; /* ":confirm" message */ static char_u *confirm_msg_tail; /* tail of confirm_msg */ -struct msg_hist { - struct msg_hist *next; - char_u *msg; - int attr; -}; - -static struct msg_hist *first_msg_hist = NULL; -static struct msg_hist *last_msg_hist = NULL; +MessageHistoryEntry *first_msg_hist = NULL; +MessageHistoryEntry *last_msg_hist = NULL; static int msg_hist_len = 0; static FILE *verbose_fd = NULL; @@ -149,10 +143,11 @@ msg_attr_keep ( { static int entered = 0; int retval; - char_u *buf = NULL; + char_u *buf = NULL; - if (attr == 0) - set_vim_var_string(VV_STATUSMSG, s, -1); + if (attr == 0) { + set_vim_var_string(VV_STATUSMSG, (char *) s, -1); + } /* * It is possible that displaying a messages causes a problem (e.g., @@ -503,8 +498,8 @@ int emsg(char_u *s) return TRUE; } - /* set "v:errmsg", also when using ":silent! cmd" */ - set_vim_var_string(VV_ERRMSG, s, -1); + // set "v:errmsg", also when using ":silent! cmd" + set_vim_var_string(VV_ERRMSG, (char *) s, -1); /* * When using ":silent! cmd" ignore error messages. @@ -563,49 +558,23 @@ int emsg(char_u *s) return msg_attr(s, attr); } -/* - * Print an error message with one "%s" and one string argument. - */ -int emsg2(char_u *s, char_u *a1) -{ - return emsg3(s, a1, NULL); -} - void emsg_invreg(int name) { EMSG2(_("E354: Invalid register name: '%s'"), transchar(name)); } -/// Print an error message with one or two "%s" and one or two string arguments. -int emsg3(char_u *s, char_u *a1, char_u *a2) -{ - if (emsg_not_now()) { - return TRUE; // no error messages at the moment - } - - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, a1, a2); - return emsg(IObuff); -} - -/// Print an error message with one "%" PRId64 and one (int64_t) argument. -int emsgn(char_u *s, int64_t n) +/// Print an error message with unknown number of arguments +bool emsgf(const char *const fmt, ...) { if (emsg_not_now()) { - return TRUE; // no error messages at the moment + return true; } - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n); - return emsg(IObuff); -} - -/// Print an error message with one "%" PRIu64 and one (uint64_t) argument. -int emsgu(char_u *s, uint64_t n) -{ - if (emsg_not_now()) { - return TRUE; // no error messages at the moment - } + va_list ap; + va_start(ap, fmt); + vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL); + va_end(ap); - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n); return emsg(IObuff); } @@ -1787,25 +1756,24 @@ static void msg_scroll_up(void) static void inc_msg_scrolled(void) { if (*get_vim_var_str(VV_SCROLLSTART) == NUL) { - char_u *p = sourcing_name; - char_u *tofree = NULL; - int len; - - /* v:scrollstart is empty, set it to the script/function name and line - * number */ - if (p == NULL) - p = (char_u *)_("Unknown"); - else { - len = (int)STRLEN(p) + 40; + char *p = (char *) sourcing_name; + char *tofree = NULL; + + // v:scrollstart is empty, set it to the script/function name and line + // number + if (p == NULL) { + p = _("Unknown"); + } else { + size_t len = strlen(p) + 40; tofree = xmalloc(len); - vim_snprintf((char *)tofree, len, _("%s line %" PRId64), - p, (int64_t)sourcing_lnum); + vim_snprintf(tofree, len, _("%s line %" PRId64), + p, (int64_t) sourcing_lnum); p = tofree; } set_vim_var_string(VV_SCROLLSTART, p, -1); xfree(tofree); } - ++msg_scrolled; + msg_scrolled++; } static msgchunk_T *last_msgchunk = NULL; /* last displayed text */ @@ -2572,7 +2540,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) /* Don't want a hit-enter prompt here. */ ++no_wait_return; - set_vim_var_string(VV_WARNINGMSG, message, -1); + set_vim_var_string(VV_WARNINGMSG, (char *) message, -1); xfree(keep_msg); keep_msg = NULL; if (hl) @@ -3086,7 +3054,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...) return str_l; } -int vim_snprintf(char *str, size_t str_m, char *fmt, ...) +int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) { va_list ap; int str_l; @@ -3097,11 +3065,12 @@ int vim_snprintf(char *str, size_t str_m, char *fmt, ...) return str_l; } -int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) +int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, + typval_T *tvs) { size_t str_l = 0; bool str_avail = str_l < str_m; - char *p = fmt; + const char *p = fmt; int arg_idx = 1; if (!p) { @@ -3135,7 +3104,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) char tmp[TMP_LEN]; // string address in case of string argument - char *str_arg; + const char *str_arg; // natural field width of arg without padding and sign size_t str_arg_l; diff --git a/src/nvim/message.h b/src/nvim/message.h index 019c7bfb73..d3a16fff93 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include <stdarg.h> #include "nvim/eval_defs.h" // for typval_T +#include "nvim/ex_cmds_defs.h" // for exarg_T /* * Types of dialogs passed to do_dialog(). @@ -24,6 +25,56 @@ #define VIM_ALL 5 #define VIM_DISCARDALL 6 +/// Show plain message +#define MSG(s) msg((char_u *)(s)) + +/// Show message highlighted according to the attr +#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr)) + +/// Display error message +/// +/// Sets error flag in process, can be transformed into an exception. +#define EMSG(s) emsg((char_u *)(s)) + +/// Like #EMSG, but for messages with one "%s" inside +#define EMSG2(s, p) emsgf((const char *) (s), (p)) + +/// Like #EMSG, but for messages with two "%s" inside +#define EMSG3(s, p, q) emsgf((const char *) (s), (p), (q)) + +/// Like #EMSG, but for messages with one "%" PRId64 inside +#define EMSGN(s, n) emsgf((const char *) (s), (int64_t)(n)) + +/// Like #EMSG, but for messages with one "%" PRIu64 inside +#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n)) + +/// Display message at the recorded position +#define MSG_PUTS(s) msg_puts((char_u *)(s)) + +/// Display message at the recorded position, highlighted +#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a)) + +/// Like #MSG_PUTS, but highlight like title +#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s)) + +/// Like #MSG_PUTS, but if middle part of too long messages it will be replaced +#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0) + +/// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced +#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a)) + +/// Message history for `:messages` +typedef struct msg_hist { + struct msg_hist *next; ///< Next message. + char_u *msg; ///< Message text. + int attr; ///< Message highlighting. +} MessageHistoryEntry; + +/// First message +extern MessageHistoryEntry *first_msg_hist; +/// Last message +extern MessageHistoryEntry *last_msg_hist; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "message.h.generated.h" #endif diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index db303fd54a..43e0dd0c1a 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -43,7 +43,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -86,38 +85,32 @@ open_line ( int second_line_indent ) { - char_u *saved_line; /* copy of the original line */ - char_u *next_line = NULL; /* copy of the next line */ - char_u *p_extra = NULL; /* what goes to next line */ - int less_cols = 0; /* less columns for mark in new line */ - int less_cols_off = 0; /* columns to skip for mark adjust */ - pos_T old_cursor; /* old cursor position */ - int newcol = 0; /* new cursor column */ - int newindent = 0; /* auto-indent of the new line */ - int n; - int trunc_line = FALSE; /* truncate current line afterwards */ - int retval = FALSE; /* return value, default is FAIL */ - int extra_len = 0; /* length of p_extra string */ - int lead_len; /* length of comment leader */ - char_u *lead_flags; /* position in 'comments' for comment leader */ - char_u *leader = NULL; /* copy of comment leader */ - char_u *allocated = NULL; /* allocated memory */ - char_u *p; - int saved_char = NUL; /* init for GCC */ - pos_T *pos; - int do_si = (!p_paste && curbuf->b_p_si - && !curbuf->b_p_cin - ); - int no_si = FALSE; /* reset did_si afterwards */ - int first_char = NUL; /* init for GCC */ + char_u *next_line = NULL; // copy of the next line + char_u *p_extra = NULL; // what goes to next line + colnr_T less_cols = 0; // less columns for mark in new line + colnr_T less_cols_off = 0; // columns to skip for mark adjust + pos_T old_cursor; // old cursor position + colnr_T newcol = 0; // new cursor column + int newindent = 0; // auto-indent of the new line + bool trunc_line = false; // truncate current line afterwards + bool retval = false; // return value, default is false + int extra_len = 0; // length of p_extra string + int lead_len; // length of comment leader + char_u *lead_flags; // position in 'comments' for comment leader + char_u *leader = NULL; // copy of comment leader + char_u *allocated = NULL; // allocated memory + char_u *p; + char_u saved_char = NUL; // init for GCC + pos_T *pos; + bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin); + bool no_si = false; // reset did_si afterwards + int first_char = NUL; // init for GCC int vreplace_mode; - int did_append; /* appended a new line */ - int saved_pi = curbuf->b_p_pi; /* copy of preserveindent setting */ + bool did_append; // appended a new line + int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting - /* - * make a copy of the current line so we can mess with it - */ - saved_line = vim_strsave(get_cursor_line_ptr()); + // make a copy of the current line so we can mess with it + char_u *saved_line = vim_strsave(get_cursor_line_ptr()); if (State & VREPLACE_FLAG) { /* @@ -204,7 +197,7 @@ open_line ( char_u *ptr; char_u last_char; - old_cursor = curwin->w_cursor; + pos_T old_cursor = curwin->w_cursor; ptr = saved_line; if (flags & OPENLINE_DO_COM) lead_len = get_leader_len(ptr, NULL, FALSE, TRUE); @@ -401,7 +394,7 @@ open_line ( end_comment_pending = -1; /* means we want to set it */ ++p; } - n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); + size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); if (end_comment_pending == -1) /* we can set it now */ end_comment_pending = lead_end[n - 1]; @@ -498,10 +491,11 @@ open_line ( } } if (lead_len > 0) { - /* allocate buffer (may concatenate p_extra later) */ - leader = xmalloc(lead_len + lead_repl_len + extra_space + extra_len - + (second_line_indent > 0 ? second_line_indent : 0) + 1); - allocated = leader; /* remember to free it later */ + // allocate buffer (may concatenate p_extra later) + leader = xmalloc((size_t)(lead_len + lead_repl_len + extra_space + + extra_len + (second_line_indent > 0 + ? second_line_indent : 0) + 1)); + allocated = leader; // remember to free it later STRLCPY(leader, saved_line, lead_len + 1); @@ -598,9 +592,8 @@ open_line ( if (!ascii_iswhite(*p)) { /* Don't put a space before a TAB. */ if (p + 1 < leader + lead_len && p[1] == TAB) { - --lead_len; - memmove(p, p + 1, - (leader + lead_len) - p); + lead_len--; + memmove(p, p + 1, (size_t)(leader + lead_len - p)); } else { int l = (*mb_ptr2len)(p); @@ -611,8 +604,7 @@ open_line ( --l; *p++ = ' '; } - memmove(p + 1, p + l, - (leader + lead_len) - p); + memmove(p + 1, p + l, (size_t)(leader + lead_len - p)); lead_len -= l - 1; } *p = ' '; @@ -813,9 +805,11 @@ open_line ( * In REPLACE mode, for each character in the new indent, there must * be a NUL on the replace stack, for when it is deleted with BS */ - if (REPLACE_NORMAL(State)) - for (n = 0; n < (int)curwin->w_cursor.col; ++n) + if (REPLACE_NORMAL(State)) { + for (colnr_T n = 0; n < curwin->w_cursor.col; n++) { replace_push(NUL); + } + } newcol += curwin->w_cursor.col; if (no_si) did_si = FALSE; @@ -1281,11 +1275,13 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. */ width = wp->w_width - win_col_off(wp); - if (width <= 0) - return 32000; - if (col <= (unsigned int)width) + if (width <= 0) { + return 32000; // bigger than the number of lines of the screen + } + if (col <= (unsigned int)width) { return 1; - col -= width; + } + col -= (unsigned int)width; width += win_col_off2(wp); assert(col <= INT_MAX && (int)col < INT_MAX - (width -1)); return ((int)col + (width - 1)) / width + 1; @@ -1297,15 +1293,9 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) */ int plines_win_col(win_T *wp, linenr_T lnum, long column) { - long col; - char_u *s; - int lines = 0; - int width; - char_u *line; - - /* Check for filler lines above this buffer line. When folded the result - * is one line anyway. */ - lines = diff_check_fill(wp, lnum); + // Check for filler lines above this buffer line. When folded the result + // is one line anyway. + int lines = diff_check_fill(wp, lnum); if (!wp->w_p_wrap) return lines + 1; @@ -1313,30 +1303,29 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) if (wp->w_width == 0) return lines + 1; - line = s = ml_get_buf(wp->w_buffer, lnum, FALSE); + char_u *line = ml_get_buf(wp->w_buffer, lnum, false); + char_u *s = line; - col = 0; + colnr_T col = 0; while (*s != NUL && --column >= 0) { - col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL); + col += win_lbr_chartabsize(wp, line, s, col, NULL); mb_ptr_adv(s); } - /* - * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in - * INSERT mode, then col must be adjusted so that it represents the last - * screen position of the TAB. This only fixes an error when the TAB wraps - * from one screen line to the next (when 'columns' is not a multiple of - * 'ts') -- webb. - */ - if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) - col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1; + // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in + // INSERT mode, then col must be adjusted so that it represents the last + // screen position of the TAB. This only fixes an error when the TAB wraps + // from one screen line to the next (when 'columns' is not a multiple of + // 'ts') -- webb. + if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) { + col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1; + } - /* - * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. - */ - width = wp->w_width - win_col_off(wp); - if (width <= 0) + // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. + int width = wp->w_width - win_col_off(wp); + if (width <= 0) { return 9999; + } lines += 1; if (col > width) @@ -1349,11 +1338,9 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) int count = 0; while (first <= last) { - int x; - - /* Check if there are any really folded lines, but also included lines - * that are maybe folded. */ - x = foldedCount(wp, first, NULL); + // Check if there are any really folded lines, but also included lines + // that are maybe folded. + linenr_T x = foldedCount(wp, first, NULL); if (x > 0) { ++count; /* count 1 for "+-- folded" line */ first += x; @@ -1374,117 +1361,99 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) */ void ins_bytes(char_u *p) { - ins_bytes_len(p, (int)STRLEN(p)); + ins_bytes_len(p, STRLEN(p)); } -/* - * Insert string "p" with length "len" at the cursor position. - * Handles Replace mode and multi-byte characters. - */ -void ins_bytes_len(char_u *p, int len) +/// Insert string "p" with length "len" at the cursor position. +/// Handles Replace mode and multi-byte characters. +void ins_bytes_len(char_u *p, size_t len) { - int i; - int n; - - if (has_mbyte) - for (i = 0; i < len; i += n) { - if (enc_utf8) - /* avoid reading past p[len] */ - n = utfc_ptr2len_len(p + i, len - i); - else - n = (*mb_ptr2len)(p + i); + if (has_mbyte) { + size_t n; + for (size_t i = 0; i < len; i += n) { + if (enc_utf8) { + // avoid reading past p[len] + n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i)); + } else { + n = (size_t)(*mb_ptr2len)(p + i); + } ins_char_bytes(p + i, n); } - else - for (i = 0; i < len; ++i) + } else { + for (size_t i = 0; i < len; i++) { ins_char(p[i]); + } + } } -/* - * Insert or replace a single character at the cursor position. - * When in REPLACE or VREPLACE mode, replace any existing character. - * Caller must have prepared for undo. - * For multi-byte characters we get the whole character, the caller must - * convert bytes to a character. - */ +/// Insert or replace a single character at the cursor position. +/// When in REPLACE or VREPLACE mode, replace any existing character. +/// Caller must have prepared for undo. +/// For multi-byte characters we get the whole character, the caller must +/// convert bytes to a character. void ins_char(int c) { char_u buf[MB_MAXBYTES + 1]; - int n; + size_t n = (size_t)(*mb_char2bytes)(c, buf); - n = (*mb_char2bytes)(c, buf); - - /* When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte. - * Happens for CTRL-Vu9900. */ - if (buf[0] == 0) + // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte. + // Happens for CTRL-Vu9900. + if (buf[0] == 0) { buf[0] = '\n'; - + } ins_char_bytes(buf, n); } -void ins_char_bytes(char_u *buf, int charlen) +void ins_char_bytes(char_u *buf, size_t charlen) { - int c = buf[0]; - int newlen; /* nr of bytes inserted */ - int oldlen; /* nr of bytes deleted (0 when not replacing) */ - char_u *p; - char_u *newp; - char_u *oldp; - int linelen; /* length of old line including NUL */ - colnr_T col; - linenr_T lnum = curwin->w_cursor.lnum; - int i; - - /* Break tabs if needed. */ - if (virtual_active() && curwin->w_cursor.coladd > 0) + // Break tabs if needed. + if (virtual_active() && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); + } - col = curwin->w_cursor.col; - oldp = ml_get(lnum); - linelen = (int)STRLEN(oldp) + 1; + int c = buf[0]; + size_t col = (size_t)curwin->w_cursor.col; + linenr_T lnum = curwin->w_cursor.lnum; + char_u *oldp = ml_get(lnum); + size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL - /* The lengths default to the values for when not replacing. */ - oldlen = 0; - newlen = charlen; + // The lengths default to the values for when not replacing. + size_t oldlen = 0; // nr of bytes inserted + size_t newlen = charlen; // nr of bytes deleted (0 when not replacing) if (State & REPLACE_FLAG) { if (State & VREPLACE_FLAG) { - colnr_T new_vcol = 0; /* init for GCC */ + // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag. + // Returns the old value of list, so when finished, + // curwin->w_p_list should be set back to this. + int old_list = curwin->w_p_list; + if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { + curwin->w_p_list = false; + } + // In virtual replace mode each character may replace one or more + // characters (zero if it's a TAB). Count the number of bytes to + // be deleted to make room for the new character, counting screen + // cells. May result in adding spaces to fill a gap. colnr_T vcol; - int old_list; - - /* - * Disable 'list' temporarily, unless 'cpo' contains the 'L' flag. - * Returns the old value of list, so when finished, - * curwin->w_p_list should be set back to this. - */ - old_list = curwin->w_p_list; - if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) - curwin->w_p_list = FALSE; - - /* - * In virtual replace mode each character may replace one or more - * characters (zero if it's a TAB). Count the number of bytes to - * be deleted to make room for the new character, counting screen - * cells. May result in adding spaces to fill a gap. - */ getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL); - new_vcol = vcol + chartabsize(buf, vcol); + colnr_T new_vcol = vcol + chartabsize(buf, vcol); while (oldp[col + oldlen] != NUL && vcol < new_vcol) { vcol += chartabsize(oldp + col + oldlen, vcol); - /* Don't need to remove a TAB that takes us to the right - * position. */ - if (vcol > new_vcol && oldp[col + oldlen] == TAB) + // Don't need to remove a TAB that takes us to the right + // position. + if (vcol > new_vcol && oldp[col + oldlen] == TAB) { break; - oldlen += (*mb_ptr2len)(oldp + col + oldlen); - /* Deleted a bit too much, insert spaces. */ - if (vcol > new_vcol) - newlen += vcol - new_vcol; + } + oldlen += (size_t)(*mb_ptr2len)(oldp + col + oldlen); + // Deleted a bit too much, insert spaces. + if (vcol > new_vcol) { + newlen += (size_t)(vcol - new_vcol); + } } curwin->w_p_list = old_list; } else if (oldp[col] != NUL) { - /* normal replace */ - oldlen = (*mb_ptr2len)(oldp + col); + // normal replace + oldlen = (size_t)(*mb_ptr2len)(oldp + col); } @@ -1493,38 +1462,39 @@ void ins_char_bytes(char_u *buf, int charlen) * done the other way around, so that the first byte is popped off * first (it tells the byte length of the character). */ replace_push(NUL); - for (i = 0; i < oldlen; ++i) { - if (has_mbyte) - i += replace_push_mb(oldp + col + i) - 1; - else + for (size_t i = 0; i < oldlen; i++) { + if (has_mbyte) { + i += (size_t)replace_push_mb(oldp + col + i) - 1; + } else { replace_push(oldp[col + i]); + } } } - newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen)); + char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen)); - /* Copy bytes before the cursor. */ - if (col > 0) + // Copy bytes before the cursor. + if (col > 0) { memmove(newp, oldp, (size_t)col); + } - /* Copy bytes after the changed character(s). */ - p = newp + col; - memmove(p + newlen, oldp + col + oldlen, - (size_t)(linelen - col - oldlen)); + // Copy bytes after the changed character(s). + char_u *p = newp + col; + memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen)); - /* Insert or overwrite the new character. */ + // Insert or overwrite the new character. memmove(p, buf, charlen); - i = charlen; - /* Fill with spaces when necessary. */ - while (i < newlen) - p[i++] = ' '; + // Fill with spaces when necessary. + for (size_t i = charlen; i < newlen; i++) { + p[i] = ' '; + } /* Replace the line in the buffer. */ ml_replace(lnum, newp, FALSE); - /* mark the buffer as changed and prepare for displaying */ - changed_bytes(lnum, col); + // mark the buffer as changed and prepare for displaying + changed_bytes(lnum, (colnr_T)col); /* * If we're in Insert or Replace mode and 'showmatch' is set, then briefly @@ -1541,8 +1511,8 @@ void ins_char_bytes(char_u *buf, int charlen) } if (!p_ri || (State & REPLACE_FLAG)) { - /* Normal insert: move cursor right */ - curwin->w_cursor.col += charlen; + // Normal insert: move cursor right + curwin->w_cursor.col += (int)charlen; } /* * TODO: should try to update w_row here, to avoid recomputing it later. @@ -1595,7 +1565,7 @@ int del_char(int fixpos) return FAIL; return del_chars(1L, fixpos); } - return del_bytes(1L, fixpos, TRUE); + return del_bytes(1, fixpos, true); } /* @@ -1603,7 +1573,7 @@ int del_char(int fixpos) */ int del_chars(long count, int fixpos) { - long bytes = 0; + int bytes = 0; long i; char_u *p; int l; @@ -1617,30 +1587,22 @@ int del_chars(long count, int fixpos) return del_bytes(bytes, fixpos, TRUE); } -/* - * Delete "count" bytes under the cursor. - * If "fixpos" is TRUE, don't leave the cursor on the NUL after the line. - * Caller must have prepared for undo. - * - * return FAIL for failure, OK otherwise - */ -int -del_bytes ( - long count, - int fixpos_arg, - int use_delcombine /* 'delcombine' option applies */ -) +/// Delete "count" bytes under the cursor. +/// If "fixpos" is true, don't leave the cursor on the NUL after the line. +/// Caller must have prepared for undo. +/// +/// @param count number of bytes to be deleted +/// @param fixpos_arg leave the cursor on the NUL after the line +/// @param use_delcombine 'delcombine' option applies +/// +/// @return FAIL for failure, OK otherwise +int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) { - char_u *oldp, *newp; - colnr_T oldlen; linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; - int was_alloced; - long movelen; - int fixpos = fixpos_arg; - - oldp = ml_get(lnum); - oldlen = (int)STRLEN(oldp); + bool fixpos = fixpos_arg; + char_u *oldp = ml_get(lnum); + colnr_T oldlen = (colnr_T)STRLEN(oldp); /* * Can't do anything when the cursor is on the NUL after the line. @@ -1664,14 +1626,12 @@ del_bytes ( count = utf_ptr2len(oldp + n); n += count; } while (UTF_COMPOSINGLIKE(oldp + col, oldp + n)); - fixpos = 0; + fixpos = false; } } - /* - * When count is too big, reduce it. - */ - movelen = (long)oldlen - (long)col - count + 1; /* includes trailing NUL */ + // When count is too big, reduce it. + int movelen = oldlen - col - count + 1; // includes trailing NUL if (movelen <= 1) { /* * If we just took off the last character of a non-blank line, and @@ -1691,15 +1651,14 @@ del_bytes ( movelen = 1; } - /* - * If the old line has been allocated the deletion can be done in the - * existing line. Otherwise a new line has to be allocated. - */ - was_alloced = ml_line_alloced(); /* check if oldp was allocated */ - if (was_alloced) - newp = oldp; /* use same allocated memory */ - else { /* need to allocate a new line */ - newp = xmalloc(oldlen + 1 - count); + // If the old line has been allocated the deletion can be done in the + // existing line. Otherwise a new line has to be allocated. + bool was_alloced = ml_line_alloced(); // check if oldp was allocated + char_u *newp; + if (was_alloced) { + newp = oldp; // use same allocated memory + } else { // need to allocate a new line + newp = xmalloc((size_t)(oldlen + 1 - count)); memmove(newp, oldp, (size_t)col); } memmove(newp + col, oldp + col + count, (size_t)movelen); @@ -1725,12 +1684,12 @@ truncate_line ( linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; - if (col == 0) + if (col == 0) { newp = vim_strsave((char_u *)""); - else - newp = vim_strnsave(ml_get(lnum), col); - - ml_replace(lnum, newp, FALSE); + } else { + newp = vim_strnsave(ml_get(lnum), (size_t)col); + } + ml_replace(lnum, newp, false); /* mark the buffer as changed and prepare for displaying */ changed_bytes(lnum, curwin->w_cursor.col); @@ -2250,7 +2209,7 @@ change_warning ( msg_col = col; msg_source(hl_attr(HLF_W)); MSG_PUTS_ATTR(_(w_readonly), hl_attr(HLF_W) | MSG_HIST); - set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_readonly), -1); + set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); msg_clr_eos(); (void)msg_end(); if (msg_silent == 0 && !silent_mode) { @@ -2360,13 +2319,13 @@ int get_keystroke(void) * 5 chars plus NUL). And fix_input_buffer() can triple the number of * bytes. */ maxlen = (buflen - 6 - len) / 3; - if (buf == NULL) - buf = xmalloc(buflen); - else if (maxlen < 10) { - /* Need some more space. This might happen when receiving a long - * escape sequence. */ + if (buf == NULL) { + buf = xmalloc((size_t)buflen); + } else if (maxlen < 10) { + // Need some more space. This might happen when receiving a long + // escape sequence. buflen += 100; - buf = xrealloc(buf, buflen); + buf = xrealloc(buf, (size_t)buflen); maxlen = (buflen - 6 - len) / 3; } @@ -2729,38 +2688,33 @@ void fast_breakcheck(void) } } -/* - * Get the stdout of an external command. - * If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not - * NULL store the length there. - * Returns an allocated string, or NULL for error. - */ -char_u * -get_cmd_output ( - char_u *cmd, - char_u *infile, /* optional input file name */ - int flags, // can be kShellOptSilent - size_t *ret_len -) +/// Get the stdout of an external command. +/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not +/// NULL store the length there. +/// +/// @param cmd command to execute +/// @param infile optional input file name +/// @param flags can be kShellOptSilent or 0 +/// @param ret_len length of the stdout +/// +/// @return an allocated string, or NULL for error. +char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, + size_t *ret_len) { - char_u *tempname; - char_u *command; - char_u *buffer = NULL; - int len; - int i = 0; - FILE *fd; + char_u *buffer = NULL; if (check_restricted() || check_secure()) return NULL; - /* get a name for the temp file */ - if ((tempname = vim_tempname()) == NULL) { + // get a name for the temp file + char_u *tempname = vim_tempname(); + if (tempname == NULL) { EMSG(_(e_notmp)); return NULL; } - /* Add the redirection stuff */ - command = make_filter_cmd(cmd, infile, tempname); + // Add the redirection stuff + char_u *command = make_filter_cmd(cmd, infile, tempname); /* * Call the shell to execute the command (errors are ignored). @@ -2772,10 +2726,8 @@ get_cmd_output ( xfree(command); - /* - * read the names from the file into memory - */ - fd = mch_fopen((char *)tempname, READBIN); + // read the names from the file into memory + FILE *fd = mch_fopen((char *)tempname, READBIN); if (fd == NULL) { EMSG2(_(e_notopen), tempname); @@ -2783,11 +2735,11 @@ get_cmd_output ( } fseek(fd, 0L, SEEK_END); - len = ftell(fd); /* get size of temp file */ + size_t len = (size_t)ftell(fd); // get size of temp file fseek(fd, 0L, SEEK_SET); buffer = xmalloc(len + 1); - i = (int)fread((char *)buffer, (size_t)1, (size_t)len, fd); + size_t i = fread((char *)buffer, 1, len, fd); fclose(fd); os_remove((char *)tempname); if (i != len) { diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h index 11891d6f7e..f0f66854d8 100644 --- a/src/nvim/misc1.h +++ b/src/nvim/misc1.h @@ -2,6 +2,7 @@ #define NVIM_MISC1_H #include "nvim/vim.h" +#include "nvim/os/shell.h" /* flags for open_line() */ #define OPENLINE_DELSPACES 1 /* delete spaces after cursor */ diff --git a/src/nvim/misc2.c b/src/nvim/misc2.c index 3c0a1414a6..4b64de1be0 100644 --- a/src/nvim/misc2.c +++ b/src/nvim/misc2.c @@ -327,9 +327,10 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) } } - set_vim_var_nr(VV_SHELL_ERROR, (long)retval); - if (do_profiling == PROF_YES) + set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T) retval); + if (do_profiling == PROF_YES) { prof_child_exit(&wait_time); + } return retval; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 3ae1a6a890..4611b69424 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -14,6 +14,8 @@ #include "nvim/misc1.h" #include "nvim/cursor.h" #include "nvim/buffer_defs.h" +#include "nvim/memline.h" +#include "nvim/charset.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mouse.c.generated.h" @@ -503,3 +505,95 @@ void set_mouse_topline(win_T *wp) orig_topfill = wp->w_topfill; } +/// +/// Return length of line "lnum" for horizontal scrolling. +/// +static colnr_T scroll_line_len(linenr_T lnum) +{ + colnr_T col = 0; + char_u *line = ml_get(lnum); + if (*line != NUL) { + for (;;) { + int numchar = chartabsize(line, col); + mb_ptr_adv(line); + if (*line == NUL) { // don't count the last character + break; + } + col += numchar; + } + } + return col; +} + +/// +/// Find longest visible line number. +/// +static linenr_T find_longest_lnum(void) +{ + linenr_T ret = 0; + + // Calculate maximum for horizontal scrollbar. Check for reasonable + // line numbers, topline and botline can be invalid when displaying is + // postponed. + if (curwin->w_topline <= curwin->w_cursor.lnum && + curwin->w_botline > curwin->w_cursor.lnum && + curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) { + long max = 0; + + // Use maximum of all visible lines. Remember the lnum of the + // longest line, closest to the cursor line. Used when scrolling + // below. + for (linenr_T lnum = curwin->w_topline; lnum < curwin->w_botline; lnum++) { + colnr_T len = scroll_line_len(lnum); + if (len > (colnr_T)max) { + max = len; + ret = lnum; + } else if (len == (colnr_T)max + && abs((int)(lnum - curwin->w_cursor.lnum)) + < abs((int)(ret - curwin->w_cursor.lnum))) { + ret = lnum; + } + } + } else { + // Use cursor line only. + ret = curwin->w_cursor.lnum; + } + + return ret; +} + +/// +/// Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise. +/// +bool mouse_scroll_horiz(int dir) +{ + if (curwin->w_p_wrap) { + return false; + } + + int step = 6; + if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { + step = curwin->w_width; + } + + int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step); + if (leftcol < 0) { + leftcol = 0; + } + + if (curwin->w_leftcol == leftcol) { + return false; + } + + curwin->w_leftcol = (colnr_T)leftcol; + + // When the line of the cursor is too short, move the cursor to the + // longest visible line. + if (!virtual_active() + && (colnr_T)leftcol > scroll_line_len(curwin->w_cursor.lnum)) { + curwin->w_cursor.lnum = find_longest_lnum(); + curwin->w_cursor.col = 0; + } + + return leftcol_changed(); +} diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h index c824bcc8f0..0149f7c7c0 100644 --- a/src/nvim/mouse.h +++ b/src/nvim/mouse.h @@ -34,6 +34,12 @@ #define MOUSE_X1 0x300 // Mouse-button X1 (6th) #define MOUSE_X2 0x400 // Mouse-button X2 +// Direction for nv_mousescroll() and ins_mousescroll() +#define MSCR_DOWN 0 // DOWN must be FALSE +#define MSCR_UP 1 +#define MSCR_LEFT -1 +#define MSCR_RIGHT -2 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mouse.h.generated.h" diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 474e25ffeb..6cc56ba3dd 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -14,7 +14,7 @@ #include "nvim/vim.h" #include "nvim/memory.h" #include "nvim/log.h" -#include "nvim/tempfile.h" +#include "nvim/fileio.h" #include "nvim/path.h" #include "nvim/strings.h" @@ -59,7 +59,7 @@ static void set_vservername(garray_T *srvs) char *default_server = (srvs->ga_len > 0) ? ((SocketWatcher **)srvs->ga_data)[0]->addr : NULL; - set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1); + set_vim_var_string(VV_SEND_SERVER, default_server, -1); } /// Teardown the server module diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2895816b8f..75ee11bc9d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2598,11 +2598,10 @@ do_mouse ( end_visual.col = leftcol; else end_visual.col = rightcol; - if (curwin->w_cursor.lnum < - (start_visual.lnum + end_visual.lnum) / 2) - end_visual.lnum = end_visual.lnum; - else + if (curwin->w_cursor.lnum >= + (start_visual.lnum + end_visual.lnum) / 2) { end_visual.lnum = start_visual.lnum; + } /* move VIsual to the right column */ start_visual = curwin->w_cursor; /* save the cursor pos */ @@ -3244,9 +3243,9 @@ void clear_showcmd(void) top = curwin->w_cursor.lnum; bot = VIsual.lnum; } - /* Include closed folds as a whole. */ - hasFolding(top, &top, NULL); - hasFolding(bot, NULL, &bot); + // Include closed folds as a whole. + (void)hasFolding(top, &top, NULL); + (void)hasFolding(bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -3927,6 +3926,8 @@ static void nv_mousescroll(cmdarg_T *cap) cap->count0 = 3; nv_scroll_line(cap); } + } else { + mouse_scroll_horiz(cap->arg); } curwin->w_redr_status = true; @@ -5152,9 +5153,10 @@ static void nv_gotofile(cmdarg_T *cap) ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { - /* do autowrite if necessary */ - if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) - autowrite(curbuf, false); + // do autowrite if necessary + if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) { + (void)autowrite(curbuf, false); + } setpcmark(); (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST, P_HID(curbuf) ? ECMD_HIDE : 0, curwin); @@ -7057,18 +7059,17 @@ static void nv_operator(cmdarg_T *cap) */ static void set_op_var(int optype) { - char_u opchars[3]; - - if (optype == OP_NOP) + if (optype == OP_NOP) { set_vim_var_string(VV_OP, NULL, 0); - else { + } else { + char opchars[3]; int opchar0 = get_op_char(optype); assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX); - opchars[0] = (char_u)opchar0; + opchars[0] = (char) opchar0; int opchar1 = get_extra_op_char(optype); assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX); - opchars[1] = (char_u)opchar1; + opchars[1] = (char) opchar1; opchars[2] = NUL; set_vim_var_string(VV_OP, opchars, -1); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5f48fdbf3d..601890d133 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3989,7 +3989,7 @@ format_lines ( if (line_count < 0 && u_save_cursor() == FAIL) break; if (next_leader_len > 0) { - (void)del_bytes((long)next_leader_len, FALSE, FALSE); + (void)del_bytes(next_leader_len, false, false); mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L, (long)-next_leader_len); } else if (second_indent > 0) { /* the "leader" for FO_Q_SECOND */ diff --git a/src/nvim/option.c b/src/nvim/option.c index 7b41d256f5..b255d47c18 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1447,7 +1447,7 @@ do_set ( char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; - char_u *saved_origval = NULL; + char *saved_origval = NULL; unsigned newlen; int comma; int bs; @@ -1724,7 +1724,7 @@ do_set ( if (!starting && origval != NULL) { // origval may be freed by // did_set_string_option(), make a copy. - saved_origval = vim_strsave(origval); + saved_origval = xstrdup((char *) origval); } /* Handle side effects, and set the global value for @@ -1739,11 +1739,10 @@ do_set ( } if (saved_origval != NULL) { - char_u buf_type[7]; - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_type[7]; + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, - *(char_u **)varp, -1); + set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1); set_vim_var_string(VV_OPTION_OLD, saved_origval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); apply_autocmds(EVENT_OPTIONSET, @@ -2057,6 +2056,7 @@ static void didset_options(void) (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); + (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)spell_check_msm(); (void)spell_check_sps(); @@ -2144,6 +2144,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_ep); check_string_option(&buf->b_p_path); check_string_option(&buf->b_p_tags); + check_string_option(&buf->b_p_tc); check_string_option(&buf->b_p_dict); check_string_option(&buf->b_p_tsr); check_string_option(&buf->b_p_lw); @@ -2323,7 +2324,7 @@ set_string_option ( char_u *s; char_u **varp; char_u *oldval; - char_u *saved_oldval = NULL; + char *saved_oldval = NULL; char_u *r = NULL; if (options[opt_idx].var == NULL) /* don't set hidden option */ @@ -2339,7 +2340,7 @@ set_string_option ( *varp = s; if (!starting) { - saved_oldval = vim_strsave(oldval); + saved_oldval = xstrdup((char *) oldval); } if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL, @@ -2348,10 +2349,10 @@ set_string_option ( // call autocommand after handling side effects if (saved_oldval != NULL) { - char_u buf_type[7]; - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_type[7]; + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, *varp, -1); + set_vim_var_string(VV_OPTION_NEW, (char *) (*varp), -1); set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); apply_autocmds(EVENT_OPTIONSET, @@ -2984,6 +2985,24 @@ did_set_string_option ( if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) { errmsg = e_invarg; } + } else if (gvarp == &p_tc) { // 'tagcase' + unsigned int *flags; + + if (opt_flags & OPT_LOCAL) { + p = curbuf->b_p_tc; + flags = &curbuf->b_tc_flags; + } else { + p = p_tc; + flags = &tc_flags; + } + + if ((opt_flags & OPT_LOCAL) && *p == NUL) { + // make the local value empty: use the global value + *flags = 0; + } else if (*p == NUL + || opt_strings_flags(p, p_tc_values, flags, false) != OK) { + errmsg = e_invarg; + } } else if (varp == &p_cmp) { // 'casemap' if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) errmsg = e_invarg; @@ -3800,7 +3819,7 @@ set_bool_option ( msg_source(hl_attr(HLF_W)); MSG_ATTR(_(w_arabic), hl_attr(HLF_W)); - set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_arabic), -1); + set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); } /* set 'delcombine' */ @@ -3847,14 +3866,14 @@ set_bool_option ( options[opt_idx].flags |= P_WAS_SET; if (!starting) { - char_u buf_old[2]; - char_u buf_new[2]; - char_u buf_type[7]; - vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%d", + char buf_old[2]; + char buf_new[2]; + char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true: false); - vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%d", + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true: false); - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); @@ -4237,12 +4256,12 @@ set_num_option ( options[opt_idx].flags |= P_WAS_SET; if (!starting && errmsg == NULL) { - char_u buf_old[NUMBUFLEN]; - char_u buf_new[NUMBUFLEN]; - char_u buf_type[7]; - vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); - vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%ld", value); - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_old[NUMBUFLEN]; + char buf_new[NUMBUFLEN]; + char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); @@ -5134,6 +5153,10 @@ void unset_global_local_option(char *name, void *from) case PV_TAGS: clear_string_option(&buf->b_p_tags); break; + case PV_TC: + clear_string_option(&buf->b_p_tc); + buf->b_tc_flags = 0; + break; case PV_DEF: clear_string_option(&buf->b_p_def); break; @@ -5187,6 +5210,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_PATH: return (char_u *)&(curbuf->b_p_path); case PV_AR: return (char_u *)&(curbuf->b_p_ar); case PV_TAGS: return (char_u *)&(curbuf->b_p_tags); + case PV_TC: return (char_u *)&(curbuf->b_p_tc); case PV_DEF: return (char_u *)&(curbuf->b_p_def); case PV_INC: return (char_u *)&(curbuf->b_p_inc); case PV_DICT: return (char_u *)&(curbuf->b_p_dict); @@ -5224,6 +5248,8 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_ar) : p->var; case PV_TAGS: return *curbuf->b_p_tags != NUL ? (char_u *)&(curbuf->b_p_tags) : p->var; + case PV_TC: return *curbuf->b_p_tc != NUL + ? (char_u *)&(curbuf->b_p_tc) : p->var; case PV_BKC: return *curbuf->b_p_bkc != NUL ? (char_u *)&(curbuf->b_p_bkc) : p->var; case PV_DEF: return *curbuf->b_p_def != NUL @@ -5603,6 +5629,8 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_kp = empty_option; buf->b_p_path = empty_option; buf->b_p_tags = empty_option; + buf->b_p_tc = empty_option; + buf->b_tc_flags = 0; buf->b_p_def = empty_option; buf->b_p_inc = empty_option; buf->b_p_inex = vim_strsave(p_inex); diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 8e74e5036e..87a9a7398c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "nvim/types.h" +#include "nvim/macros.h" // For EXTERN // option_defs.h: definition of global variables for settable options @@ -596,6 +597,14 @@ static char *(p_swb_values[]) = #define SWB_NEWTAB 0x008 #define SWB_VSPLIT 0x010 EXTERN int p_tbs; ///< 'tagbsearch' +EXTERN char_u *p_tc; ///< 'tagcase' +EXTERN unsigned tc_flags; ///< flags from 'tagcase' +#ifdef IN_OPTION_C +static char *(p_tc_values[]) = { "followic", "ignore", "match", NULL }; +#endif +#define TC_FOLLOWIC 0x01 +#define TC_IGNORE 0x02 +#define TC_MATCH 0x04 EXTERN long p_tl; ///< 'taglength' EXTERN int p_tr; ///< 'tagrelative' EXTERN char_u *p_tags; ///< 'tags' @@ -736,6 +745,7 @@ enum { , BV_SW , BV_SWF , BV_TAGS + , BV_TC , BV_TS , BV_TW , BV_TX diff --git a/src/nvim/options.lua b/src/nvim/options.lua index df77c374ec..a743e8c605 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2332,6 +2332,13 @@ return { defaults={if_true={vi=true}} }, { + full_name='tagcase', abbreviation='tc', + type='string', scope={'global', 'buffer'}, + vim=true, + varname='p_tc', + defaults={if_true={vi="followic", vim="followic"}} + }, + { full_name='taglength', abbreviation='tl', type='number', scope={'global'}, vi_def=true, diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 34d8fde4f1..bc2d37764d 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -59,6 +59,23 @@ int os_dirname(char_u *buf, size_t len) return OK; } +/// Check if the given path is a directory and not a symlink to a directory. +/// @return `true` if `name` is a directory and NOT a symlink to a directory. +/// `false` if `name` is not a directory or if an error occurred. +bool os_isrealdir(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + uv_fs_t request; + if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + return false; + } + if (S_ISLNK(request.statbuf.st_mode)) { + return false; + } else { + return S_ISDIR(request.statbuf.st_mode); + } +} + /// Check if the given path is a directory or not. /// /// @return `true` if `fname` is a directory. diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index cb9a58cc77..2a859a0f7a 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -34,7 +34,6 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/os/os.h" diff --git a/src/nvim/path.c b/src/nvim/path.c index 22a3f96cfa..29ff62ef77 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -604,7 +604,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, starstar = true; // convert the file pattern to a regexp pattern - int starts_with_dot = (*s == '.'); + int starts_with_dot = *s == '.'; char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); @@ -647,9 +647,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) { // Find all matching entries. char_u *name; - scandir_next_with_dots(NULL /* initialize */); - while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { - if ((name[0] != '.' || starts_with_dot) + scandir_next_with_dots(NULL); // initialize + while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { + if ((name[0] != '.' + || starts_with_dot + || ((flags & EW_DODOT) + && name[1] != NUL && (name[1] != '.' || name[2] != NUL))) && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) && fnamencmp(path + (s - buf), name, e - s) == 0))) { @@ -1220,7 +1223,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = false; - return (ga.ga_data != NULL) ? OK : FAIL; + return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL; } @@ -1495,13 +1498,12 @@ void simplify_filename(char_u *filename) } while (*p != NUL); } -static char_u *eval_includeexpr(char_u *ptr, size_t len) +static char *eval_includeexpr(const char *const ptr, const size_t len) { - assert(len <= INT_MAX); - set_vim_var_string(VV_FNAME, ptr, (int)len); - char_u *res = eval_to_string_safe(curbuf->b_p_inex, NULL, - was_set_insecurely((char_u *)"includeexpr", - OPT_LOCAL)); + set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t) len); + char *res = (char *) eval_to_string_safe( + curbuf->b_p_inex, NULL, was_set_insecurely((char_u *)"includeexpr", + OPT_LOCAL)); set_vim_var_string(VV_FNAME, NULL, 0); return res; } @@ -1523,7 +1525,7 @@ find_file_name_in_path ( char_u *tofree = NULL; if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { - tofree = eval_includeexpr(ptr, len); + tofree = (char_u *) eval_includeexpr((char *) ptr, len); if (tofree != NULL) { ptr = tofree; len = STRLEN(ptr); @@ -1540,7 +1542,7 @@ find_file_name_in_path ( */ if (file_name == NULL && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { - tofree = eval_includeexpr(ptr, len); + tofree = (char_u *) eval_includeexpr((char *) ptr, len); if (tofree != NULL) { ptr = tofree; len = STRLEN(ptr); @@ -1924,7 +1926,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set to /// NULL or points to "". -int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, +int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags) { int retval; @@ -1932,7 +1934,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, char_u *p; int non_suf_match; /* number without matching suffix */ - retval = gen_expand_wildcards(num_pat, pat, num_file, file, flags); + retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags); /* When keeping all matches, return here */ if ((flags & EW_KEEPALL) || retval == FAIL) @@ -1944,18 +1946,20 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, if (*p_wig) { char_u *ffname; - /* check all files in (*file)[] */ - for (i = 0; i < *num_file; ++i) { - ffname = (char_u *)FullName_save((char *)(*file)[i], FALSE); - if (ffname == NULL) /* out of memory */ + // check all filess in (*files)[] + for (i = 0; i < *num_files; i++) { + ffname = (char_u *)FullName_save((char *)(*files)[i], false); + if (ffname == NULL) { // out of memory break; - if (match_file_list(p_wig, (*file)[i], ffname)) { - /* remove this matching file from the list */ - xfree((*file)[i]); - for (j = i; j + 1 < *num_file; ++j) - (*file)[j] = (*file)[j + 1]; - --*num_file; - --i; + } + if (match_file_list(p_wig, (*files)[i], ffname)) { + // remove this matching files from the list + xfree((*files)[i]); + for (j = i; j + 1 < *num_files; j++) { + (*files)[j] = (*files)[j + 1]; + } + (*num_files)--; + i--; } xfree(ffname); } @@ -1964,26 +1968,28 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, /* * Move the names where 'suffixes' match to the end. */ - if (*num_file > 1) { + if (*num_files > 1) { non_suf_match = 0; - for (i = 0; i < *num_file; ++i) { - if (!match_suffix((*file)[i])) { - /* - * Move the name without matching suffix to the front - * of the list. - */ - p = (*file)[i]; - for (j = i; j > non_suf_match; --j) - (*file)[j] = (*file)[j - 1]; - (*file)[non_suf_match++] = p; + for (i = 0; i < *num_files; i++) { + if (!match_suffix((*files)[i])) { + // + // Move the name without matching suffix to the front + // of the list. + // + p = (*files)[i]; + for (j = i; j > non_suf_match; j--) { + (*files)[j] = (*files)[j - 1]; + } + (*files)[non_suf_match++] = p; } } } // Free empty array of matches - if (*num_file == 0) { - xfree(*file); - *file = NULL; + if (*num_files == 0) { + xfree(*files); + *files = NULL; + return FAIL; } return retval; diff --git a/src/nvim/path.h b/src/nvim/path.h index eac367d0ac..88e5935c24 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -21,6 +21,8 @@ /* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND * is used when executing commands and EW_SILENT for interactive expanding. */ #define EW_ALLLINKS 0x1000 // also links not pointing to existing file +#define EW_DODOT 0x4000 // also files starting with a dot +#define EW_EMPTYOK 0x8000 // no matches is not an error /// Return value for the comparison of two files. Also @see path_full_compare. typedef enum file_comparison { diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po index 1a5ef991dc..8a9c86e88d 100644 --- a/src/nvim/po/es.po +++ b/src/nvim/po/es.po @@ -4276,7 +4276,8 @@ msgstr "" "&Abrir para lectura únicamente\n" "&Editar de todas formas\n" "&Recuperar\n" -"&Borrar&Salir\n" +"&Borrar\n" +"&Salir\n" "&Abortar" #. diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index 729697eee3..171e155689 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -11,10 +11,10 @@ # msgid "" msgstr "" -"Project-Id-Version: vim 7.4b\n" +"Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-08-03 17:14+0200\n" +"POT-Creation-Date: 2015-08-11 20:58+0200\n" +"PO-Revision-Date: 2015-08-11 22:02+0200\n" "Last-Translator: Vlad Sandrini <vlad.gently@gmail.com>\n" "Language-Team: Italian Antonio Colombo <azc100@gmail." "com> Vlad Sandrini <vlad.gently@gmail." @@ -104,11 +104,6 @@ msgstr "E84: Nessun buffer risulta modificato" msgid "E85: There is no listed buffer" msgstr "E85: Non c'è alcun buffer elencato" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: Non esiste il buffer %<PRId64>" - #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" msgstr "E87: Non posso oltrepassare l'ultimo buffer" @@ -128,7 +123,7 @@ msgstr "" #. wrap around (may cause duplicates) #: ../buffer.c:1423 msgid "W14: Warning: List of file names overflow" -msgstr "W14: Attenzione: Superato limite della lista dei nomi di file" +msgstr "W14: Avviso: Superato limite della lista dei nomi di file" #: ../buffer.c:1555 ../quickfix.c:3361 #, c-format @@ -148,7 +143,7 @@ msgstr "E94: Nessun buffer corrispondente a %s" #: ../buffer.c:2161 #, c-format msgid "line %<PRId64>" -msgstr "linea %<PRId64>" +msgstr "riga %<PRId64>" #: ../buffer.c:2233 msgid "E95: Buffer with this name already exists" @@ -181,17 +176,17 @@ msgstr "[in sola lettura]" #: ../buffer.c:2524 #, c-format msgid "1 line --%d%%--" -msgstr "1 linea --%d%%--" +msgstr "1 riga --%d%%--" #: ../buffer.c:2526 #, c-format msgid "%<PRId64> lines --%d%%--" -msgstr "%<PRId64> linee --%d%%--" +msgstr "%<PRId64> righe --%d%%--" #: ../buffer.c:2530 #, c-format msgid "line %<PRId64> of %<PRId64> --%d%%-- col " -msgstr "linea %<PRId64> di %<PRId64> --%d%%-- col " +msgstr "riga %<PRId64> di %<PRId64> --%d%%-- col " #: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554 msgid "[No Name]" @@ -250,7 +245,7 @@ msgstr "Segni per %s:" #: ../buffer.c:4543 #, c-format msgid " line=%<PRId64> id=%d name=%s" -msgstr " linea=%<PRId64> id=%d, nome=%s" +msgstr " riga=%<PRId64> id=%d, nome=%s" #: ../cursor_shape.c:68 msgid "E545: Missing colon" @@ -346,11 +341,11 @@ msgstr " modalità ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" #: ../edit.c:85 msgid " Whole line completion (^L^N^P)" -msgstr " Completamento Linea Intera (^L^N^P)" +msgstr " Completamento riga intera (^L^N^P)" #: ../edit.c:86 msgid " File name completion (^F^N^P)" -msgstr " Completamento nomi File (^F^N^P)" +msgstr " Completamento nomi file (^F^N^P)" #: ../edit.c:87 msgid " Tag completion (^]^N^P)" @@ -374,7 +369,7 @@ msgstr " Completamento Thesaurus (^T^N^P)" #: ../edit.c:93 msgid " Command-line completion (^V^N^P)" -msgstr " Completamento linea comandi (^V^N^P)" +msgstr " Completamento riga comandi (^V^N^P)" #: ../edit.c:94 msgid " User defined completion (^U^N^P)" @@ -452,7 +447,7 @@ msgstr "Ritorno all'originale" #: ../edit.c:4621 msgid "Word from other line" -msgstr "Parola da un'altra linea" +msgstr "Parola da un'altra riga" #: ../edit.c:4624 msgid "The only match" @@ -680,6 +675,11 @@ msgstr "E696: Manca virgola nella Lista: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Manca ']' a fine Lista: %s" +#: ../eval.c:5750 +#, c-format +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -754,15 +754,15 @@ msgstr "E785: complete() può essere usata solo in modalità inserimento" msgid "&Ok" msgstr "&OK" -#: ../eval.c:8676 -#, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: Chiave già esistente: %s" - #: ../eval.c:8692 msgid "extend() argument" msgstr "argomento di extend()" +#: ../eval.c:9345 +#, c-format +msgid "E737: Key already exists: %s" +msgstr "E737: Chiave già esistente: %s" + #: ../eval.c:8915 msgid "map() argument" msgstr "argomento di map()" @@ -774,7 +774,7 @@ msgstr "argomento di filter()" #: ../eval.c:9229 #, c-format msgid "+-%s%3ld lines: " -msgstr "+-%s%3ld linee: " +msgstr "+-%s%3ld righe: " #: ../eval.c:9291 #, c-format @@ -1043,21 +1043,21 @@ msgstr "> %d, Esa %08x, Ottale %o" #: ../ex_cmds.c:684 msgid "E134: Move lines into themselves" -msgstr "E134: Movimento di linee verso se stesse" +msgstr "E134: Movimento di righe verso se stesse" #: ../ex_cmds.c:747 msgid "1 line moved" -msgstr "1 linea mossa" +msgstr "1 riga mossa" #: ../ex_cmds.c:749 #, c-format msgid "%<PRId64> lines moved" -msgstr "%<PRId64> linee mosse" +msgstr "%<PRId64> righe mosse" #: ../ex_cmds.c:1175 #, c-format msgid "%<PRId64> lines filtered" -msgstr "%<PRId64> linee filtrate" +msgstr "%<PRId64> righe filtrate" #: ../ex_cmds.c:1194 msgid "E135: *Filter* Autocommands must not change current buffer" @@ -1070,7 +1070,7 @@ msgstr "[Non salvato dopo l'ultima modifica]\n" #: ../ex_cmds.c:1424 #, c-format msgid "%sviminfo: %s in line: " -msgstr "%sviminfo: %s nella linea: " +msgstr "%sviminfo: %s nella riga: " #: ../ex_cmds.c:1431 msgid "E136: viminfo: Too many errors, skipping rest of file" @@ -1239,12 +1239,12 @@ msgstr "%<PRId64> sostituzioni" #: ../ex_cmds.c:4392 msgid " on 1 line" -msgstr " in 1 linea" +msgstr " in 1 riga" #: ../ex_cmds.c:4395 #, c-format msgid " on %<PRId64> lines" -msgstr " in %<PRId64> linee" +msgstr " in %<PRId64> righe" #: ../ex_cmds.c:4438 msgid "E147: Cannot do :global recursive" @@ -1257,7 +1257,7 @@ msgstr "E148: Manca espressione regolare nel comando 'global'" #: ../ex_cmds.c:4508 #, c-format msgid "Pattern found in every line: %s" -msgstr "Espressione trovata su ogni linea: %s" +msgstr "Espressione trovata su ogni riga: %s" #: ../ex_cmds.c:4510 #, c-format @@ -1355,6 +1355,11 @@ msgstr "E158: Nome buffer non valido: %s" msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: ID 'sign' non valido: %<PRId64>" +#: ../ex_cmds.c:5517 +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: Impossibile cambiare segno %s" + #: ../ex_cmds.c:6066 msgid " (not supported)" msgstr " (non supportata)" @@ -1370,7 +1375,7 @@ msgstr "Entro modalità Debug. Batti \"cont\" per continuare." #: ../ex_cmds2.c:143 ../ex_docmd.c:759 #, c-format msgid "line %<PRId64>: %s" -msgstr "linea %<PRId64>: %s" +msgstr "riga %<PRId64>: %s" #: ../ex_cmds2.c:145 #, c-format @@ -1380,7 +1385,7 @@ msgstr "com: %s" #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" -msgstr "Pausa in \"%s%s\" linea %<PRId64>" +msgstr "Pausa in \"%s%s\" riga %<PRId64>" #: ../ex_cmds2.c:581 #, c-format @@ -1394,7 +1399,7 @@ msgstr "Nessun 'breakpoint' definito" #: ../ex_cmds2.c:617 #, c-format msgid "%3d %s %s line %<PRId64>" -msgstr "%3d %s %s linea %<PRId64>" +msgstr "%3d %s %s riga %<PRId64>" #: ../ex_cmds2.c:942 msgid "E750: First use \":profile start {fname}\"" @@ -1417,7 +1422,7 @@ msgstr "E162: Buffer \"%s\" non salvato dopo modifica" #: ../ex_cmds2.c:1480 msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "" -"Attenzione: Entrato in altro buffer inaspettatamente (controllare " +"Avviso: Entrato in altro buffer inaspettatamente (controllare " "autocomandi)" #: ../ex_cmds2.c:1826 @@ -1465,7 +1470,7 @@ msgstr "non riesco ad eseguire \"%s\"" #: ../ex_cmds2.c:2520 #, c-format msgid "line %<PRId64>: could not source \"%s\"" -msgstr "linea %<PRId64>: non riesco ad eseguire \"%s\"" +msgstr "riga %<PRId64>: non riesco ad eseguire \"%s\"" #: ../ex_cmds2.c:2535 #, c-format @@ -1475,7 +1480,7 @@ msgstr "eseguo \"%s\"" #: ../ex_cmds2.c:2537 #, c-format msgid "line %<PRId64>: sourcing \"%s\"" -msgstr "linea %<PRId64>: eseguo \"%s\"" +msgstr "riga %<PRId64>: eseguo \"%s\"" #: ../ex_cmds2.c:2693 #, c-format @@ -1504,7 +1509,7 @@ msgstr "gestore di errore" #: ../ex_cmds2.c:3020 msgid "W15: Warning: Wrong line separator, ^M may be missing" -msgstr "W15: Attenzione: Separatore di linea errato, forse manca ^M" +msgstr "W15: Avviso: Separatore di riga errato, forse manca ^M" #: ../ex_cmds2.c:3139 msgid "E167: :scriptencoding used outside of a sourced file" @@ -1606,10 +1611,10 @@ msgstr "E174: Il comando esiste già: aggiungi ! per sostituirlo" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nome Arg. Inter Completo Definizione" +" Nome Arg. Indir. Completo Definizione" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1635,6 +1640,10 @@ msgstr "E178: Valore predefinito del contatore non valido" msgid "E179: argument required for -complete" msgstr "E179: argomento necessario per -complete" +#: ../ex_docmd.c:4923 +msgid "E179: argument required for -addr" +msgstr "E179: argomento necessario per -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1658,6 +1667,11 @@ msgstr "E841: Nome riservato, non usabile in un comando definito dall'utente" msgid "E184: No such user-defined command: %s" msgstr "E184: Comando definito dall'utente %s inesistente" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Tipo di indirizzo non valido: %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -1666,7 +1680,7 @@ msgstr "E180: Valore %s non valido per 'complete'" #: ../ex_docmd.c:5225 msgid "E468: Completion argument only allowed for custom completion" msgstr "" -"E468: Argomento di completamento permesso solo per completamento " +"E468: Argomento di completamento consentito solo per completamento " "personalizzato" #: ../ex_docmd.c:5231 @@ -1815,7 +1829,7 @@ msgstr "Eccezione scartata: %s" #: ../ex_eval.c:588 ../ex_eval.c:634 #, c-format msgid "%s, line %<PRId64>" -msgstr "%s, linea %<PRId64>" +msgstr "%s, riga %<PRId64>" #. always scroll up, don't overwrite #: ../ex_eval.c:608 @@ -1961,7 +1975,7 @@ msgstr "" #: ../ex_getln.c:5047 msgid "Command Line" -msgstr "Linea di Comando" +msgstr "Riga di Comando" #: ../ex_getln.c:5048 msgid "Search String" @@ -1973,7 +1987,7 @@ msgstr "Espressione" #: ../ex_getln.c:5050 msgid "Input Line" -msgstr "Linea di Input" +msgstr "Riga di Input" #: ../ex_getln.c:5117 msgid "E198: cmd_pchar beyond the command length" @@ -2091,7 +2105,7 @@ msgstr "[manca CR]" #: ../fileio.c:1819 msgid "[long lines split]" -msgstr "[linee lunghe divise]" +msgstr "[righe lunghe divise]" #: ../fileio.c:1823 ../fileio.c:3512 msgid "[NOT converted]" @@ -2104,12 +2118,12 @@ msgstr "[convertito]" #: ../fileio.c:1831 #, c-format msgid "[CONVERSION ERROR in line %<PRId64>]" -msgstr "[ERRORE DI CONVERSIONE alla linea %<PRId64>]" +msgstr "[ERRORE DI CONVERSIONE alla riga %<PRId64>]" #: ../fileio.c:1835 #, c-format msgid "[ILLEGAL BYTE in line %<PRId64>]" -msgstr "[BYTE NON VALIDO alla linea %<PRId64>]" +msgstr "[BYTE NON VALIDO alla riga %<PRId64>]" #: ../fileio.c:1838 msgid "[READ ERRORS]" @@ -2137,7 +2151,7 @@ msgstr "E203: Buffer in scrittura cancellato o scaricato dagli autocomandi" #: ../fileio.c:2486 msgid "E204: Autocommand changed number of lines in unexpected way" -msgstr "E204: L'autocomando ha modificato numero linee in maniera imprevista" +msgstr "E204: L'autocomando ha modificato numero righe in maniera imprevista" #: ../fileio.c:2548 ../fileio.c:2565 msgid "is not a file or writable device" @@ -2213,7 +2227,7 @@ msgid "" "E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to " "override)" msgstr "" -"E513: errore in scrittura, conversione fallita alla linea %<PRId64> (rendere " +"E513: errore in scrittura, conversione fallita alla riga %<PRId64> (rendere " "'fenc' nullo per eseguire comunque)" #: ../fileio.c:3448 @@ -2227,7 +2241,7 @@ msgstr " ERRORE DI CONVERSIONE" #: ../fileio.c:3509 #, c-format msgid " in line %<PRId64>;" -msgstr " alla linea %<PRId64>;" +msgstr " alla riga %<PRId64>;" #: ../fileio.c:3519 msgid "[Device]" @@ -2271,7 +2285,7 @@ msgid "" "WARNING: Original file may be lost or damaged\n" msgstr "" "\n" -"ATTENZIONE: Il file originale può essere perso o danneggiato\n" +"AVVISO: Il file originale può essere perso o danneggiato\n" #: ../fileio.c:3675 msgid "don't quit the editor until the file is successfully written!" @@ -2303,12 +2317,12 @@ msgstr "[in formato UNIX]" #: ../fileio.c:3831 msgid "1 line, " -msgstr "1 linea, " +msgstr "1 riga, " #: ../fileio.c:3833 #, c-format msgid "%<PRId64> lines, " -msgstr "%<PRId64> linee," +msgstr "%<PRId64> righe," #: ../fileio.c:3836 msgid "1 character" @@ -2325,14 +2339,14 @@ msgstr "[noeol]" #: ../fileio.c:3849 msgid "[Incomplete last line]" -msgstr "[Manca carattere di fine linea]" +msgstr "[Manca carattere di fine riga]" #. don't overwrite messages here #. must give this prompt #. don't use emsg() here, don't want to flush the buffers #: ../fileio.c:3865 msgid "WARNING: The file has been changed since reading it!!!" -msgstr "ATTENZIONE: File modificato dopo essere stato letto dall'Editor!!!" +msgstr "AVVISO: File modificato dopo essere stato letto dall'Editor!!!" #: ../fileio.c:3867 msgid "Do you really want to write to it" @@ -2368,7 +2382,7 @@ msgid "" "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " "well" msgstr "" -"W12: Attenzione: File \"%s\" modificato su disco ed anche nel buffer di Vim" +"W12: Avviso: File \"%s\" modificato su disco ed anche nel buffer di Vim" #: ../fileio.c:4907 msgid "See \":help W12\" for more info." @@ -2377,7 +2391,7 @@ msgstr "Vedere \":help W12\" per ulteriori informazioni." #: ../fileio.c:4910 #, c-format msgid "W11: Warning: File \"%s\" has changed since editing started" -msgstr "W11: Attenzione: File \"%s\" modificato dopo l'apertura" +msgstr "W11: Avviso: File \"%s\" modificato dopo l'apertura" #: ../fileio.c:4911 msgid "See \":help W11\" for more info." @@ -2386,7 +2400,7 @@ msgstr "Vedere \":help W11\" per ulteriori informazioni." #: ../fileio.c:4914 #, c-format msgid "W16: Warning: Mode of file \"%s\" has changed since editing started" -msgstr "W16: Attenzione: Modo File \"%s\" modificato dopo l'apertura" +msgstr "W16: Avviso: Modo File \"%s\" modificato dopo l'apertura" #: ../fileio.c:4915 msgid "See \":help W16\" for more info." @@ -2395,11 +2409,11 @@ msgstr "Vedere \":help W16\" per ulteriori informazioni." #: ../fileio.c:4927 #, c-format msgid "W13: Warning: File \"%s\" has been created after editing started" -msgstr "W13: Attenzione: Il file \"%s\" risulta creato dopo l'apertura" +msgstr "W13: Avviso: Il file \"%s\" risulta creato dopo l'apertura" #: ../fileio.c:4947 msgid "Warning" -msgstr "Attenzione" +msgstr "Avviso" #: ../fileio.c:4948 msgid "" @@ -2513,7 +2527,7 @@ msgstr "E351: Non posso cancellare piegatura con il 'foldmethod' in uso" #: ../fold.c:1784 #, c-format msgid "+--%3ld lines folded " -msgstr "+--%3ld linee piegate" +msgstr "+--%3ld righe piegate" #. buffer has already been read #: ../getchar.c:273 @@ -2679,7 +2693,7 @@ msgstr "E364: Chiamata a libreria fallita per \"%s()\"" #: ../globals.h:1026 msgid "E19: Mark has invalid line number" -msgstr "E19: 'Mark' con numero linea non valido" +msgstr "E19: 'Mark' con numero riga non valido" #: ../globals.h:1027 msgid "E20: Mark not set" @@ -2720,7 +2734,7 @@ msgstr "E29: Ancora nessun testo inserito" #: ../globals.h:1038 msgid "E30: No previous command line" -msgstr "E30: Nessuna linea comandi precedente" +msgstr "E30: Nessuna riga comandi precedente" #: ../globals.h:1039 msgid "E31: No such mapping" @@ -2947,6 +2961,11 @@ msgstr "E363: l'espressione usa troppa memoria rispetto a 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: buffer vuoto" +#: ../globals.h:1226 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Non esiste il buffer %<PRId64>" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Espressione o delimitatore di ricerca non validi" @@ -3260,11 +3279,11 @@ msgid "" " # line" msgstr "" "\n" -" # linea" +" # riga" #: ../if_cscope.c:1713 msgid "filename / context / line\n" -msgstr "nomefile / contest / linea\n" +msgstr "nomefile / contest / riga\n" #: ../if_cscope.c:1809 #, c-format @@ -3326,16 +3345,16 @@ msgstr "Non posso aprire come script output: \"" #: ../main.c:1622 msgid "Vim: Warning: Output is not to a terminal\n" -msgstr "Vim: Attenzione: Output non diretto a un terminale\n" +msgstr "Vim: Avviso: Output non diretto a un terminale\n" #: ../main.c:1624 msgid "Vim: Warning: Input is not from a terminal\n" -msgstr "Vim: Attenzione: Input non proveniente da un terminale\n" +msgstr "Vim: Avviso: Input non proveniente da un terminale\n" #. just in case.. #: ../main.c:1891 msgid "pre-vimrc command line" -msgstr "linea comandi prima di vimrc" +msgstr "riga comandi prima di vimrc" #: ../main.c:1964 #, c-format @@ -3528,7 +3547,7 @@ msgstr "+\t\t\tPosizionati alla fine del file" #: ../main.c:2231 msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<lnum>\t\tPosizionati alla linea <lnum>" +msgstr "+<lnum>\t\tPosizionati alla riga <lnum>" #: ../main.c:2232 msgid "--cmd <command>\tExecute <command> before loading any vimrc file" @@ -3589,7 +3608,7 @@ msgid "" "mark line col file/text" msgstr "" "\n" -"mark linea col.file/testo" +"mark riga col.file/testo" #. Highlight title #: ../mark.c:789 @@ -3598,7 +3617,7 @@ msgid "" " jump line col file/text" msgstr "" "\n" -" salt.linea col.file/testo" +" salt.riga col.file/testo" #. Highlight title #: ../mark.c:831 @@ -3607,7 +3626,7 @@ msgid "" "change line col text" msgstr "" "\n" -"modif linea col testo" +"modif riga col testo" #: ../mark.c:1238 msgid "" @@ -3767,7 +3786,7 @@ msgstr "File originale \"%s\"" #: ../memline.c:995 msgid "E308: Warning: Original file may have been changed" msgstr "" -"E308: Attenzione: il file originale può essere stato modificato nel frattempo" +"E308: Avviso: il file originale può essere stato modificato nel frattempo" #: ../memline.c:1061 #, c-format @@ -3776,7 +3795,7 @@ msgstr "E309: Impossibile leggere blocco 1 da %s" #: ../memline.c:1065 msgid "???MANY LINES MISSING" -msgstr "???MOLTE LINEE MANCANTI" +msgstr "???MOLTE RIGHE MANCANTI" #: ../memline.c:1076 msgid "???LINE COUNT WRONG" @@ -3788,7 +3807,7 @@ msgstr "???BLOCCO VUOTO" #: ../memline.c:1103 msgid "???LINES MISSING" -msgstr "???LINEE MANCANTI" +msgstr "???RIGHE MANCANTI" #: ../memline.c:1128 #, c-format @@ -3801,12 +3820,12 @@ msgstr "???BLOCCO MANCANTE" #: ../memline.c:1147 msgid "??? from here until ???END lines may be messed up" -msgstr "??? da qui fino a ???END le linee possono essere fuori ordine" +msgstr "??? da qui fino a ???END le righe possono essere fuori ordine" #: ../memline.c:1164 msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "" -"??? da qui fino a ???END linee possono essere state inserite/cancellate" +"??? da qui fino a ???END righe possono essere state inserite/cancellate" #: ../memline.c:1181 msgid "???END" @@ -3819,7 +3838,7 @@ msgstr "E311: Recupero Interrotto" #: ../memline.c:1243 msgid "" "E312: Errors detected while recovering; look for lines starting with ???" -msgstr "E312: Errori durante recupero; controlla linee che iniziano con ???" +msgstr "E312: Errori durante recupero; controlla righe che iniziano con ???" #: ../memline.c:1245 msgid "See \":help E312\" for more information." @@ -3980,12 +3999,12 @@ msgstr "E314: Preservazione fallita" #: ../memline.c:1819 #, c-format msgid "E315: ml_get: invalid lnum: %<PRId64>" -msgstr "E315: ml_get: numero linea non valido: %<PRId64>" +msgstr "E315: ml_get: numero riga non valido: %<PRId64>" #: ../memline.c:1851 #, c-format msgid "E316: ml_get: cannot find line %<PRId64>" -msgstr "E316: ml_get: non riesco a trovare la linea %<PRId64>" +msgstr "E316: ml_get: non riesco a trovare la riga %<PRId64>" #: ../memline.c:2236 msgid "E317: pointer block id wrong 3" @@ -4010,7 +4029,7 @@ msgstr "cancellato blocco 1?" #: ../memline.c:2707 #, c-format msgid "E320: Cannot find line %<PRId64>" -msgstr "E320: Non riesco a trovare la linea %<PRId64>" +msgstr "E320: Non riesco a trovare la riga %<PRId64>" #: ../memline.c:2916 msgid "E317: pointer block id wrong" @@ -4023,12 +4042,12 @@ msgstr "pe_line_count a zero" #: ../memline.c:2955 #, c-format msgid "E322: line number out of range: %<PRId64> past the end" -msgstr "E322: numero linea non ammissibile: %<PRId64> dopo la fine" +msgstr "E322: numero riga non ammissibile: %<PRId64> dopo la fine" #: ../memline.c:2959 #, c-format msgid "E323: line count wrong in block %<PRId64>" -msgstr "E323: contatore linee errato nel blocco %<PRId64>" +msgstr "E323: contatore righe errato nel blocco %<PRId64>" #: ../memline.c:2999 msgid "Stack size increases" @@ -4242,7 +4261,7 @@ msgstr "Errore/i eseguendo %s:" #: ../message.c:445 #, c-format msgid "line %4ld:" -msgstr "linea %4ld:" +msgstr "riga %4ld:" #: ../message.c:617 #, c-format @@ -4264,7 +4283,7 @@ msgstr "Premi INVIO o un comando per proseguire" #: ../message.c:1843 #, c-format msgid "%s line %<PRId64>" -msgstr "%s linea %<PRId64>" +msgstr "%s riga %<PRId64>" #: ../message.c:2392 msgid "-- More --" @@ -4272,7 +4291,7 @@ msgstr "-- Ancora --" #: ../message.c:2398 msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit " -msgstr " SPAZIO/d/j: schermo/pagina/linea giù, b/u/k: su, q: abbandona " +msgstr " SPAZIO/d/j: schermo/pagina/riga giù, b/u/k: su, q: abbandona " #: ../message.c:3021 ../message.c:3031 msgid "Question" @@ -4324,7 +4343,7 @@ msgstr "E767: Troppi argomenti per printf()" #: ../misc1.c:2256 msgid "W10: Warning: Changing a readonly file" -msgstr "W10: Attenzione: Modifica a un file in sola-lettura" +msgstr "W10: Avviso: Modifica a un file in sola-lettura" #: ../misc1.c:2537 msgid "Type number and <Enter> or click with mouse (empty cancels): " @@ -4337,21 +4356,21 @@ msgstr "Inserire numero e <Invio> (vuoto per annullare): " #: ../misc1.c:2585 msgid "1 more line" -msgstr "1 linea in più" +msgstr "1 riga in più" #: ../misc1.c:2588 msgid "1 line less" -msgstr "1 linea in meno" +msgstr "1 riga in meno" #: ../misc1.c:2593 #, c-format msgid "%<PRId64> more lines" -msgstr "%<PRId64> linee in più" +msgstr "%<PRId64> righe in più" #: ../misc1.c:2596 #, c-format msgid "%<PRId64> fewer lines" -msgstr "%<PRId64> linee in meno" +msgstr "%<PRId64> righe in meno" #: ../misc1.c:2599 msgid " (Interrupted)" @@ -4376,7 +4395,7 @@ msgstr "E774: opzione 'operatorfunc' non impostata" #: ../normal.c:2637 msgid "Warning: terminal cannot highlight" -msgstr "Attenzione: il terminale non è in grado di evidenziare" +msgstr "Avviso: il terminale non è in grado di evidenziare" #: ../normal.c:2807 msgid "E348: No string under cursor" @@ -4405,36 +4424,36 @@ msgstr "Batti :quit<Invio> per uscire da Vim" #: ../ops.c:248 #, c-format msgid "1 line %sed 1 time" -msgstr "1 linea %sa 1 volta" +msgstr "1 riga %sa 1 volta" #: ../ops.c:250 #, c-format msgid "1 line %sed %d times" -msgstr "1 linea %sa %d volte" +msgstr "1 riga %sa %d volte" #: ../ops.c:253 #, c-format msgid "%<PRId64> lines %sed 1 time" -msgstr "%<PRId64> linee %se 1 volta" +msgstr "%<PRId64> righe %se 1 volta" #: ../ops.c:256 #, c-format msgid "%<PRId64> lines %sed %d times" -msgstr "%<PRId64> linee %se %d volte" +msgstr "%<PRId64> righe %se %d volte" #: ../ops.c:592 #, c-format msgid "%<PRId64> lines to indent... " -msgstr "%<PRId64> linee da rientrare... " +msgstr "%<PRId64> righe da rientrare... " #: ../ops.c:634 msgid "1 line indented " -msgstr "1 linea rientrata " +msgstr "1 riga rientrata " #: ../ops.c:636 #, c-format msgid "%<PRId64> lines indented " -msgstr "%<PRId64> linee rientrate " +msgstr "%<PRId64> righe rientrate " #: ../ops.c:938 msgid "E748: No previously used register" @@ -4447,30 +4466,30 @@ msgstr "non riesco a salvare in un registro; cancello comunque" #: ../ops.c:1929 msgid "1 line changed" -msgstr "1 linea cambiata" +msgstr "1 riga cambiata" #: ../ops.c:1931 #, c-format msgid "%<PRId64> lines changed" -msgstr "%<PRId64> linee cambiate" +msgstr "%<PRId64> righe cambiate" #: ../ops.c:2521 msgid "block of 1 line yanked" -msgstr "blocco di 1 linea messo in registro" +msgstr "blocco di 1 riga messo in registro" #: ../ops.c:2523 msgid "1 line yanked" -msgstr "1 linea messa in registro" +msgstr "1 riga messa in registro" #: ../ops.c:2525 #, c-format msgid "block of %<PRId64> lines yanked" -msgstr "blocco di %<PRId64> linee messo in registro" +msgstr "blocco di %<PRId64> righe messo in registro" #: ../ops.c:2528 #, c-format msgid "%<PRId64> lines yanked" -msgstr "%<PRId64> linee messe in registro" +msgstr "%<PRId64> righe messe in registro" #: ../ops.c:2710 #, c-format @@ -4503,6 +4522,13 @@ msgstr "" msgid "E574: Unknown register type %d" msgstr "E574: Tipo di registro sconosciuto: %d" +#: ../ops.c:4897 +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: espressione di ricerca e registro dell'espressione non possono " +"contenere due o più righe" + #: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " @@ -4514,7 +4540,7 @@ msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " "%<PRId64> of %<PRId64> Bytes" msgstr "" -"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; " +"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; " "%<PRId64> di %<PRId64> Caratt." #: ../ops.c:5105 @@ -4523,7 +4549,7 @@ msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " "%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes" msgstr "" -"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; " +"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; " "%<PRId64> di %<PRId64> Caratt.; %<PRId64> di %<PRId64> Byte" #: ../ops.c:5123 @@ -4532,7 +4558,7 @@ msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " "%<PRId64> of %<PRId64>" msgstr "" -"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " +"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " "Caratt. %<PRId64> di %<PRId64>" #: ../ops.c:5133 @@ -4541,7 +4567,7 @@ msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " "%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>" msgstr "" -"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " +"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " "Caratt. %<PRId64> di %<PRId64>; Byte %<PRId64> di %<PRId64>" #: ../ops.c:5146 @@ -4587,6 +4613,11 @@ msgstr "E522: Non trovato in 'termcap'" msgid "E539: Illegal character <%s>" msgstr "E539: Carattere non ammesso <%s>" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Per opzione %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: Non posso assegnare a 'term' il valore 'stringa nulla'" @@ -4665,7 +4696,7 @@ msgstr "W17: Arabo richiede UTF-8, esegui ':set encoding=utf-8'" #: ../option.c:5623 #, c-format msgid "E593: Need at least %d lines" -msgstr "E593: Servono almeno %d linee" +msgstr "E593: Servono almeno %d righe" #: ../option.c:5631 #, c-format @@ -4822,7 +4853,7 @@ msgstr "(%d di %d)%s%s: " #: ../quickfix.c:1676 msgid " (line deleted)" -msgstr " (linea cancellata)" +msgstr " (riga cancellata)" #: ../quickfix.c:1863 msgid "E380: At bottom of quickfix stack" @@ -4973,6 +5004,10 @@ msgstr "E554: Errore sintattico in %s{...}" msgid "External submatches:\n" msgstr "Sotto-corrispondenze esterne:\n" +#: ../regexp.c:2470 +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA regexp) non riesco a ripetere %s" + #: ../regexp.c:7022 msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -4981,6 +5016,10 @@ msgstr "" "E864: \\%#= può essere seguito solo da 0, 1 o 2. Sarà usato il motore " "automatico " +#: ../regexp.c:7039 +msgid "Switching to backtracking RE engine for pattern: " +msgstr "Passo alla ricerca di RE col vecchio metodo: " + #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" msgstr "E865: (NFA) Fine prematura dell'espressione regolare" @@ -5113,7 +5152,7 @@ msgstr " VISUALE" #: ../screen.c:7470 msgid " VISUAL LINE" -msgstr " VISUALE LINEA" +msgstr " VISUALE RIGA" #: ../screen.c:7471 msgid " VISUAL BLOCK" @@ -5125,7 +5164,7 @@ msgstr " SELEZIONA" #: ../screen.c:7473 msgid " SELECT LINE" -msgstr " SELEZIONA LINEA" +msgstr " SELEZIONA RIGA" #: ../screen.c:7474 msgid " SELECT BLOCK" @@ -5191,7 +5230,7 @@ msgstr "Cerco nel file incluso: %s" #: ../search.c:4405 msgid "E387: Match is on current line" -msgstr "E387: Corrispondenza nella linea corrente" +msgstr "E387: Corrispondenza nella riga corrente" #: ../search.c:4517 msgid "All included files were found" @@ -5235,12 +5274,12 @@ msgstr "E758: File ortografico troncato" #: ../spell.c:953 #, c-format msgid "Trailing text in %s line %d: %s" -msgstr "Testo in eccesso in %s linea %d: %s" +msgstr "Testo in eccesso in %s riga %d: %s" #: ../spell.c:954 #, c-format msgid "Affix name too long in %s line %d: %s" -msgstr "Nome affisso troppo lungo in %s linea %d: %s" +msgstr "Nome affisso troppo lungo in %s riga %d: %s" #: ../spell.c:955 msgid "E761: Format error in affix file FOL, LOW or UPP" @@ -5261,7 +5300,7 @@ msgstr "E756: Controllo ortografico non abilitato" #: ../spell.c:2249 #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" -msgstr "Attenzione: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\"" +msgstr "Avviso: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\"" #: ../spell.c:2473 #, c-format @@ -5287,7 +5326,7 @@ msgstr "E770: Sezione non supportata nel file ortografico" #: ../spell.c:3762 #, c-format msgid "Warning: region %s not supported" -msgstr "Attenzione: regione %s non supportata" +msgstr "Avviso: regione %s non supportata" #: ../spell.c:4550 #, c-format @@ -5297,7 +5336,7 @@ msgstr "Lettura file affissi %s ..." #: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140 #, c-format msgid "Conversion failure for word in %s line %d: %s" -msgstr "Conversione fallita per una parola in %s linea %d: %s" +msgstr "Conversione fallita per una parola in %s riga %d: %s" #: ../spell.c:4630 ../spell.c:6170 #, c-format @@ -5307,12 +5346,12 @@ msgstr "Conversione in %s non supportata: da %s a %s" #: ../spell.c:4642 #, c-format msgid "Invalid value for FLAG in %s line %d: %s" -msgstr "Valore di FLAG non valido in %s linea %d: %s" +msgstr "Valore di FLAG non valido in %s riga %d: %s" #: ../spell.c:4655 #, c-format msgid "FLAG after using flags in %s line %d: %s" -msgstr "FLAG dopo l'uso di flags in %s linea %d: %s" +msgstr "FLAG dopo l'uso di flags in %s riga %d: %s" #: ../spell.c:4723 #, c-format @@ -5321,7 +5360,7 @@ msgid "" "%d" msgstr "" "Definire COMPOUNDFORBIDFLAG dopo l'elemento PFX potrebbe dare risultati " -"errati in %s linea %d" +"errati in %s riga %d" #: ../spell.c:4731 #, c-format @@ -5330,43 +5369,43 @@ msgid "" "%d" msgstr "" "Definire COMPOUNDPERMITFLAG dopo l'elemento PFX potrebbe dare risultati " -"errati in %s linea %d" +"errati in %s riga %d" #: ../spell.c:4747 #, c-format msgid "Wrong COMPOUNDRULES value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDRULES in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDRULES in %s riga %d: %s" #: ../spell.c:4771 #, c-format msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDWORDMAX in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDWORDMAX in %s riga %d: %s" #: ../spell.c:4777 #, c-format msgid "Wrong COMPOUNDMIN value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDMIN in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDMIN in %s riga %d: %s" #: ../spell.c:4783 #, c-format msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDSYLMAX in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDSYLMAX in %s riga %d: %s" #: ../spell.c:4795 #, c-format msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s" -msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s linea %d: %s" +msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s riga %d: %s" #: ../spell.c:4847 #, c-format msgid "Different combining flag in continued affix block in %s line %d: %s" msgstr "" -"Flag combinazione diverso in blocco affissi continuo in %s linea %d: %s" +"Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s" #: ../spell.c:4850 #, c-format msgid "Duplicate affix in %s line %d: %s" -msgstr "Affisso duplicato in %s linea %d: %s" +msgstr "Affisso duplicato in %s riga %d: %s" #: ../spell.c:4871 #, c-format @@ -5375,42 +5414,42 @@ msgid "" "line %d: %s" msgstr "" "Affisso usato anche per BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST " -"in %s linea %d: %s" +"in %s riga %d: %s" #: ../spell.c:4893 #, c-format msgid "Expected Y or N in %s line %d: %s" -msgstr "Y o N deve essere presente in %s linea %d: %s" +msgstr "Y o N deve essere presente in %s riga %d: %s" #: ../spell.c:4968 #, c-format msgid "Broken condition in %s line %d: %s" -msgstr "Condizione non rispettata in %s linea %d: %s" +msgstr "Condizione non rispettata in %s riga %d: %s" #: ../spell.c:5091 #, c-format msgid "Expected REP(SAL) count in %s line %d" -msgstr "Contatore REP(SAL) necessario in %s linea %d" +msgstr "Contatore REP(SAL) necessario in %s riga %d" #: ../spell.c:5120 #, c-format msgid "Expected MAP count in %s line %d" -msgstr "Contatore MAP necessario in %s linea %d" +msgstr "Contatore MAP necessario in %s riga %d" #: ../spell.c:5132 #, c-format msgid "Duplicate character in MAP in %s line %d" -msgstr "Carattere duplicato in MAP in %s linea %d" +msgstr "Carattere duplicato in MAP in %s riga %d" #: ../spell.c:5176 #, c-format msgid "Unrecognized or duplicate item in %s line %d: %s" -msgstr "Elemento non riconosciuto o duplicato in %s linea %d: %s" +msgstr "Elemento non riconosciuto o duplicato in %s riga %d: %s" #: ../spell.c:5197 #, c-format msgid "Missing FOL/LOW/UPP line in %s" -msgstr "Linea FOL/LOW/UPP mancante in %s" +msgstr "Riga FOL/LOW/UPP mancante in %s" #: ../spell.c:5220 msgid "COMPOUNDSYLMAX used without SYLLABLE" @@ -5431,22 +5470,22 @@ msgstr "Troppi suffissi e/o flag composti" #: ../spell.c:5250 #, c-format msgid "Missing SOFO%s line in %s" -msgstr "Linea SOFO%s mancante in %s" +msgstr "Riga SOFO%s mancante in %s" #: ../spell.c:5253 #, c-format msgid "Both SAL and SOFO lines in %s" -msgstr "Linee sia SAL che SOFO in %s" +msgstr "Riga sia SAL che SOFO in %s" #: ../spell.c:5331 #, c-format msgid "Flag is not a number in %s line %d: %s" -msgstr "Il flag non è un numero in %s linea %d: %s" +msgstr "Il flag non è un numero in %s riga %d: %s" #: ../spell.c:5334 #, c-format msgid "Illegal flag in %s line %d: %s" -msgstr "Flag non ammesso in %s linea %d: %s" +msgstr "Flag non ammesso in %s riga %d: %s" #: ../spell.c:5493 ../spell.c:5501 #, c-format @@ -5466,17 +5505,17 @@ msgstr "E760: Nessun contatore parole in %s" #: ../spell.c:5669 #, c-format msgid "line %6d, word %6d - %s" -msgstr "linea %6d, parola %6d - %s" +msgstr "riga %6d, parola %6d - %s" #: ../spell.c:5691 #, c-format msgid "Duplicate word in %s line %d: %s" -msgstr "Parola duplicata in %s linea %d: %s" +msgstr "Parola duplicata in %s riga %d: %s" #: ../spell.c:5694 #, c-format msgid "First duplicate word in %s line %d: %s" -msgstr "Prima parola duplicata in %s linea %d: %s" +msgstr "Prima parola duplicata in %s riga %d: %s" #: ../spell.c:5746 #, c-format @@ -5496,37 +5535,37 @@ msgstr "Lettura file parole %s ..." #: ../spell.c:6155 #, c-format msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "Linea /encoding= duplicata ignorata in %s linea %d: %s" +msgstr "Riga /encoding= duplicata ignorata in %s riga %d: %s" #: ../spell.c:6159 #, c-format msgid "/encoding= line after word ignored in %s line %d: %s" -msgstr "Linea /encoding= dopo parola ignorata in %s linea %d: %s" +msgstr "Riga /encoding= dopo parola ignorata in %s riga %d: %s" #: ../spell.c:6180 #, c-format msgid "Duplicate /regions= line ignored in %s line %d: %s" -msgstr "Linea /regions= duplicata ignorata in %s linea %d: %s" +msgstr "Riga /regions= duplicata ignorata in %s riga %d: %s" #: ../spell.c:6185 #, c-format msgid "Too many regions in %s line %d: %s" -msgstr "Troppe regioni in %s linea %d: %s" +msgstr "Troppe regioni in %s riga %d: %s" #: ../spell.c:6198 #, c-format msgid "/ line ignored in %s line %d: %s" -msgstr "Linea / ignorata in %s linea %d: %s" +msgstr "Riga / ignorata in %s riga %d: %s" #: ../spell.c:6224 #, c-format msgid "Invalid region nr in %s line %d: %s" -msgstr "N. regione non valido in %s linea %d: %s" +msgstr "N. regione non valido in %s riga %d: %s" #: ../spell.c:6230 #, c-format msgid "Unrecognized flags in %s line %d: %s" -msgstr "Flag non riconosciuti in %s linea %d: %s" +msgstr "Flag non riconosciuti in %s riga %d: %s" #: ../spell.c:6257 #, c-format @@ -5583,7 +5622,7 @@ msgstr "E755: Regione non valida in %s" #: ../spell.c:7907 msgid "Warning: both compounding and NOBREAK specified" -msgstr "Attenzione: specificati sia composizione sia NOBREAK" +msgstr "Avviso: specificati sia composizione sia NOBREAK" #: ../spell.c:7920 #, c-format @@ -5700,7 +5739,7 @@ msgstr "la sincronizzazione inizia " #: ../syntax.c:3443 ../syntax.c:3506 msgid " lines before top line" -msgstr " linee prima della linea iniziale" +msgstr " righe prima della riga iniziale" #: ../syntax.c:3448 msgid "" @@ -5745,7 +5784,7 @@ msgstr "; corrisp. " #: ../syntax.c:3515 msgid " line breaks" -msgstr " interruzioni di linea" +msgstr " interruzioni di riga" #: ../syntax.c:4076 msgid "E395: contains argument not accepted here" @@ -5809,7 +5848,7 @@ msgstr "E402: Spazzatura dopo espressione: %s" #: ../syntax.c:5120 msgid "E403: syntax sync: line continuations pattern specified twice" msgstr "" -"E403: syntax sync: espressione di continuazione linea specificata due volte" +"E403: syntax sync: espressione di continuazione riga specificata due volte" #: ../syntax.c:5169 #, c-format @@ -5998,7 +6037,7 @@ msgid "" " # TO tag FROM line in file/text" msgstr "" "\n" -" # A tag DA__ linea in file/testo" +" # A tag DA__ riga in file/testo" #: ../tag.c:1303 #, c-format @@ -6007,7 +6046,7 @@ msgstr "Ricerca nel tag file %s" #: ../tag.c:1545 msgid "Ignoring long line in tags file" -msgstr "Linea lunga ignorata nel tag file" +msgstr "Riga lunga ignorata nel tag file" #: ../tag.c:1915 #, c-format @@ -6177,23 +6216,23 @@ msgstr "E830: Undo numero %<PRId64> non trovato" #: ../undo.c:1979 msgid "E438: u_undo: line numbers wrong" -msgstr "E438: u_undo: numeri linee errati" +msgstr "E438: u_undo: numeri righe errati" #: ../undo.c:2183 msgid "more line" -msgstr "linea in più" +msgstr "riga in più" #: ../undo.c:2185 msgid "more lines" -msgstr "linee in più" +msgstr "righe in più" #: ../undo.c:2187 msgid "line less" -msgstr "linea in meno" +msgstr "riga in meno" #: ../undo.c:2189 msgid "fewer lines" -msgstr "linee in meno" +msgstr "righe in meno" #: ../undo.c:2193 msgid "change" @@ -6239,7 +6278,7 @@ msgstr "E439: lista 'undo' non valida" #: ../undo.c:2495 msgid "E440: undo line missing" -msgstr "E440: linea di 'undo' mancante" +msgstr "E440: riga di 'undo' mancante" #: ../version.c:600 msgid "" @@ -6439,6 +6478,10 @@ msgstr "E445: Altre finestre contengono modifiche" msgid "E446: No file name under cursor" msgstr "E446: Nessun nome file sotto il cursore" +#: ../window.c:5484 +msgid "List or number required" +msgstr "È necessaria una lista o un numero" + #~ msgid "E831: bf_key_init() called with empty password" #~ msgstr "E831: chiamata a bf_key_init() con password nulla" @@ -6524,15 +6567,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Reading from stdin..." #~ msgstr "Leggo da 'stdin'..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - -#~ msgid "[crypted]" -#~ msgstr "[cifrato]" - -#~ msgid "E821: File is encrypted with unknown method" -#~ msgstr "E821: File cifrato con metodo sconosciuto" - #~ msgid "NetBeans disallows writes of unmodified buffers" #~ msgstr "NetBeans non permette la scrittura di un buffer non modificato" @@ -6648,8 +6682,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Vim: Received \"die\" request from session manager\n" #~ msgstr "Vim: Ricevuta richiesta \"die\" dal session manager\n" -#~ msgid "Close" -#~ msgstr "Chiusura" +#~ msgid "Close tab" +#~ msgstr "Chiudi linguetta" #~ msgid "New tab" #~ msgstr "Nuova linguetta" @@ -6705,9 +6739,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Non posso aprire la finestra in un'applicazione MDI" -#~ msgid "Close tab" -#~ msgstr "Chiudi linguetta" - #~ msgid "Open tab..." #~ msgstr "Apri linguetta..." @@ -6830,13 +6861,13 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "non sono riuscito ad aprire il buffer" #~ msgid "cannot delete line" -#~ msgstr "non posso cancellare la linea" +#~ msgstr "non posso cancellare la riga" #~ msgid "cannot replace line" -#~ msgstr "non posso sostituire la linea" +#~ msgstr "non posso sostituire la riga" #~ msgid "cannot insert line" -#~ msgstr "non posso inserire la linea" +#~ msgstr "non posso inserire la riga" #~ msgid "string cannot contain newlines" #~ msgstr "la stringa non può contenere caratteri 'A CAPO'" @@ -6857,7 +6888,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "finestra non valida" #~ msgid "linenr out of range" -#~ msgstr "numero linea non nell'intervallo" +#~ msgstr "numero riga non nell'intervallo" #~ msgid "not allowed in the Vim sandbox" #~ msgstr "non ammesso in ambiente protetto" @@ -6865,9 +6896,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E836: This Vim cannot execute :python after using :py3" #~ msgstr "E836: Python: Impossibile usare :py e :py3 nella stessa sessione" -#~ msgid "E837: This Vim cannot execute :py3 after using :python" -#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python" - #~ msgid "" #~ "E263: Sorry, this command is disabled, the Python library could not be " #~ "loaded." @@ -6875,14 +6903,18 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ "E263: Spiacente, comando non disponibile, non riesco a caricare libreria " #~ "programmi Python." +#~ msgid "" +#~ "E887: Sorry, this command is disabled, the Python's site module could not be " +#~ "loaded." +#~ msgstr "" +#~ "E887: Spiacente, comando non disponibile, non riesco a caricare il modulo " +#~ "Python locale." + #~ msgid "E659: Cannot invoke Python recursively" #~ msgstr "E659: Python non può essere chiamato ricorsivamente" -#~ msgid "line number out of range" -#~ msgstr "numero linea non nell'intervallo" - -#~ msgid "invalid mark name" -#~ msgstr "nome di mark non valido" +#~ msgid "E837: This Vim cannot execute :py3 after using :python" +#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python" #~ msgid "E265: $_ must be an instance of String" #~ msgstr "E265: $_ deve essere un'istanza di String" @@ -7010,7 +7042,10 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "non ancora implementato" #~ msgid "cannot set line(s)" -#~ msgstr "non posso impostare linea(e)" +#~ msgstr "non posso impostare riga(he)" + +#~ msgid "invalid mark name" +#~ msgstr "nome di mark non valido" #~ msgid "mark not set" #~ msgstr "mark non impostato" @@ -7019,7 +7054,10 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "riga %d colonna %d" #~ msgid "cannot insert/append line" -#~ msgstr "non riesco a inserire/aggiungere linea" +#~ msgstr "non riesco a inserire/aggiungere riga" + +#~ msgid "line number out of range" +#~ msgstr "numero linea non nell'intervallo" #~ msgid "unknown flag: " #~ msgstr "opzione inesistente: " @@ -7067,7 +7105,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E572: codice di uscita %d" #~ msgid "cannot get line" -#~ msgstr "non riesco a ottenere la linea" +#~ msgstr "non riesco a ottenere la riga" #~ msgid "Unable to register a command server name" #~ msgstr "Non riesco a registrare un nome di server comando" @@ -7293,7 +7331,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E286: Apertura 'input method' fallita" #~ msgid "E287: Warning: Could not set destroy callback to IM" -#~ msgstr "E287: Attenzione: Non posso assegnare IM a 'destroy callback'" +#~ msgstr "E287: Avviso: Non posso assegnare IM a 'destroy callback'" #~ msgid "E288: input method doesn't support any style" #~ msgstr "E288: 'input method' non sopporta alcuno stile" @@ -7365,12 +7403,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E338: Sorry, no file browser in console mode" #~ msgstr "E338: Spiacente, niente esplorazione file in modalità console" -#~ msgid "Vim: preserving files...\n" -#~ msgstr "Vim: preservo file...\n" - -#~ msgid "Vim: Finished.\n" -#~ msgstr "Vim: Finito.\n" - #~ msgid "ERROR: " #~ msgstr "ERRORE: " @@ -7391,7 +7423,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ "\n" #~ msgid "E340: Line is becoming too long" -#~ msgstr "E340: La linea sta diventando troppo lunga" +#~ msgstr "E340: La riga sta diventando troppo lunga" #~ msgid "E341: Internal error: lalloc(%<PRId64>, )" #~ msgstr "E341: Errore interno: lalloc(%<PRId64>, )" @@ -7399,15 +7431,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E547: Illegal mouseshape" #~ msgstr "E547: Forma del mouse non valida" -#~ msgid "Enter encryption key: " -#~ msgstr "Immetti chiave di cifratura: " - -#~ msgid "Enter same key again: " -#~ msgstr "Ribatti per conferma la stessa chiave: " - -#~ msgid "Keys don't match!" -#~ msgstr "Le chiavi non corrispondono!" - #~ msgid "Cannot connect to Netbeans #2" #~ msgstr "Non posso connettermi a Netbeans #2" @@ -7437,7 +7460,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E775: Funzionalità [eval] non disponibile" #~ msgid "freeing %<PRId64> lines" -#~ msgstr "libero %<PRId64> linee" +#~ msgstr "libero %<PRId64> righe" #~ msgid "E530: Cannot change term in GUI" #~ msgstr "E530: Non posso modificare 'term' mentre sono nella GUI" @@ -7538,15 +7561,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E245: Illegal char '%c' in font name \"%s\"" #~ msgstr "E245: Carattere non ammesso '%c' nel font di nome \"%s\"" -#~ msgid "Vim: Double signal, exiting\n" -#~ msgstr "Vim: Segnale doppio, esco\n" - -#~ msgid "Vim: Caught deadly signal %s\n" -#~ msgstr "Vim: Intercettato segnale fatale %s\n" - -#~ msgid "Vim: Caught deadly signal\n" -#~ msgstr "Vim: Intercettato segnale fatale\n" - #~ msgid "Opening the X display took %<PRId64> msec" #~ msgstr "Attivazione visualizzazione X ha richiesto %<PRId64> msec" @@ -7610,7 +7624,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "XSMP SmcOpenConnection fallita: %s" #~ msgid "At line" -#~ msgstr "Alla linea" +#~ msgstr "Alla riga" #~ msgid "Could not load vim32.dll!" #~ msgstr "Non riesco a caricare vim32.dll!" @@ -7875,7 +7889,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr " modo Vim predefinito " #~ msgid "WARNING: Windows 95/98/ME detected" -#~ msgstr "ATTENZIONE: Trovato Windows 95/98/ME" +#~ msgstr "AVVISO: Trovato Windows 95/98/ME" #~ msgid "type :help windows95<Enter> for info on this" #~ msgstr "batti :help windows95<Enter> per info al riguardo" @@ -7962,6 +7976,22 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Need encryption key for \"%s\"" #~ msgstr "Serve una chiave di cifratura per \"%s\"" +#~ msgid "empty keys are not allowed" +#~ msgstr "chiavi nulle non consentite" + +#~ msgid "dictionary is locked" +#~ msgstr "il dizionario è bloccato" + +#~ msgid "list is locked" +#~ msgstr "la lista è bloccata" + +#~ msgid "failed to add key '%s' to dictionary" +#~ msgstr "non non riusciato ad aggiungere la chiave '%s' al dizionario" + +#~ #, c-format +#~ msgid "index must be int or slice, not %s" +#~ msgstr "l'indice deve'essere un intero o un intervallo, non %s" + #~ msgid "expected str() or unicode() instance, but got %s" #~ msgstr "attesa istanza di str() o unicode(), trovato invece %s" @@ -7985,8 +8015,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "value is too small to fit into C int type" #~ msgstr "valore troppo piccolo per il tipo int del C" -#~ msgid "number must be greater then zero" -#~ msgstr "il numero dev'essere maggiore di zero" +#~ msgid "number must be greater than zero" +#~ msgstr "il numero deve essere maggiore di zero" #~ msgid "number must be greater or equal to zero" #~ msgstr "il numero dev'essere maggiore o uguale a zero" @@ -8005,7 +8035,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "expected 3-tuple as imp.find_module() result, but got %s" #~ msgstr "" -#~ "atteso terzetto come risultato di imp.find_module(), trovato invece %s" +#~ "atteso terzetto come risultato di imp.find_module(), trovato invece tuple di " +#~ "dimens. %d" #~ msgid "" #~ "expected 3-tuple as imp.find_module() result, but got tuple of size %d" @@ -8042,15 +8073,30 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "internal error: failed to get vim list item %d" #~ msgstr "errore interno: non ho potuto ottenere l'elemento di vim list %d" -#~ msgid "failed to add item to list" -#~ msgstr "non ho potuto aggiungere un elemento alla lista" +#~ msgid "slice step cannot be zero" +#~ msgstr "il passo scorrendo un intervallo non può essere zero" + +#~ #, c-format +#~ msgid "attempt to assign sequence of size greater than %d to extended slice" +#~ msgstr "tentativo di assegnare una sequenza maggiore di %d a un intervallo " +#~ "esteso" #~ msgid "internal error: no vim list item %d" #~ msgstr "errore interno: non c'è un elemento di vim list %d" +#~ msgid "internal error: not enough list items" +#~ msgstr "errore interno: non ci sono abbastanza elementi per la lista" + #~ msgid "internal error: failed to add item to list" #~ msgstr "errore interno: non ho potuto aggiungere un elemento alla lista" +#~ msgid "attempt to assign sequence of size %d to extended slice of size %d" +#~ msgstr "tentativo di assegnare sequenza di dimensione %d a un intervallo " +#~ " esteso di dimensione %d" + +#~ msgid "failed to add item to list" +#~ msgstr "non ho potuto aggiungere un elemento alla lista" + #~ msgid "cannot delete vim.List attributes" #~ msgstr "non riesco a cancellare gli attributi vim.List" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3bc6d46dd9..97db69d3f3 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -39,7 +39,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -2440,8 +2439,6 @@ int grep_internal(cmdidx_T cmdidx) void ex_make(exarg_T *eap) { char_u *fname; - char_u *cmd; - size_t len; win_T *wp = NULL; qf_info_T *qi = &ql_info; int res; @@ -2479,30 +2476,28 @@ void ex_make(exarg_T *eap) return; os_remove((char *)fname); // in case it's not unique - /* - * If 'shellpipe' empty: don't redirect to 'errorfile'. - */ - len = STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1; + // If 'shellpipe' empty: don't redirect to 'errorfile'. + const size_t len = (STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1 + + (*p_sp == NUL + ? 0 + : STRLEN(p_sp) + STRLEN(fname) + 3)); + char *const cmd = xmalloc(len); + snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)eap->arg, + (char *)p_shq); if (*p_sp != NUL) { - len += STRLEN(p_sp) + STRLEN(fname) + 3; + append_redir(cmd, len, (char *) p_sp, (char *) fname); + } + // Output a newline if there's something else than the :make command that + // was typed (in which case the cursor is in column 0). + if (msg_col == 0) { + msg_didout = false; } - cmd = xmalloc(len); - sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg, - (char *)p_shq); - if (*p_sp != NUL) - append_redir(cmd, len, p_sp, fname); - /* - * Output a newline if there's something else than the :make command that - * was typed (in which case the cursor is in column 0). - */ - if (msg_col == 0) - msg_didout = FALSE; msg_start(); MSG_PUTS(":!"); - msg_outtrans(cmd); /* show what we are doing */ + msg_outtrans((char_u *) cmd); // show what we are doing - /* let the shell know if we are redirecting output or not */ - do_shell(cmd, *p_sp != NUL ? kShellOptDoOut : 0); + // let the shell know if we are redirecting output or not + do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0); res = qf_init(wp, fname, (eap->cmdidx != CMD_make diff --git a/src/nvim/search.c b/src/nvim/search.c index fffae1ecb2..faf70472bf 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -453,25 +453,24 @@ void last_pat_prog(regmmatch_T *regmatch) --emsg_off; } -/* - * lowest level search function. - * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. - * Start at position 'pos' and return the found position in 'pos'. - * - * if (options & SEARCH_MSG) == 0 don't give any messages - * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages - * if (options & SEARCH_MSG) == SEARCH_MSG give all messages - * if (options & SEARCH_HIS) put search pattern in history - * if (options & SEARCH_END) return position at end of match - * if (options & SEARCH_START) accept match at pos itself - * if (options & SEARCH_KEEP) keep previous search pattern - * if (options & SEARCH_FOLD) match only once in a closed fold - * if (options & SEARCH_PEEK) check for typed char, cancel search - * - * Return FAIL (zero) for failure, non-zero for success. - * Returns the index of the first matching - * subpattern plus one; one if there was none. - */ +/// lowest level search function. +/// Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. +/// Start at position 'pos' and return the found position in 'pos'. +/// +/// if (options & SEARCH_MSG) == 0 don't give any messages +/// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages +/// if (options & SEARCH_MSG) == SEARCH_MSG give all messages +/// if (options & SEARCH_HIS) put search pattern in history +/// if (options & SEARCH_END) return position at end of match +/// if (options & SEARCH_START) accept match at pos itself +/// if (options & SEARCH_KEEP) keep previous search pattern +/// if (options & SEARCH_FOLD) match only once in a closed fold +/// if (options & SEARCH_PEEK) check for typed char, cancel search +/// if (options & SEARCH_COL) start at pos->col instead of zero +/// +/// @returns FAIL (zero) for failure, non-zero for success. +/// the index of the first matching +/// subpattern plus one; one if there was none. int searchit( win_T *win, /* window to search in, can be NULL for a buffer without a window! */ @@ -571,16 +570,14 @@ int searchit( if (tm != NULL && profile_passed_limit(*tm)) break; - /* - * Look for a match somewhere in line "lnum". - */ + // Look for a match somewhere in line "lnum". + colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, (colnr_T)0, - tm - ); - /* Abort searching on an error (e.g., out of stack). */ - if (called_emsg) + lnum, col, tm); + // Abort searching on an error (e.g., out of stack). + if (called_emsg) { break; + } if (nmatched > 0) { /* match may actually be in another line when using \zs */ matchpos = regmatch.startpos[0]; @@ -881,9 +878,8 @@ static void set_vv_searchforward(void) set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/')); } -/* - * Return the number of the first subpat that matched. - */ +// Return the number of the first subpat that matched. +// Return zero if none of them matched. static int first_submatch(regmmatch_T *rp) { int submatch; diff --git a/src/nvim/search.h b/src/nvim/search.h index 6947f79d49..d4e40cb287 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -15,19 +15,20 @@ #define ACTION_SHOW_ALL 4 #define ACTION_EXPAND 5 -/* Values for 'options' argument in do_search() and searchit() */ -#define SEARCH_REV 0x01 /* go in reverse of previous dir. */ -#define SEARCH_ECHO 0x02 /* echo the search command and handle options */ -#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */ -#define SEARCH_NFMSG 0x08 /* give all messages except not found */ -#define SEARCH_OPT 0x10 /* interpret optional flags */ -#define SEARCH_HIS 0x20 /* put search pattern in history */ -#define SEARCH_END 0x40 /* put cursor at end of match */ -#define SEARCH_NOOF 0x80 /* don't add offset to position */ -#define SEARCH_START 0x100 /* start search without col offset */ -#define SEARCH_MARK 0x200 /* set previous context mark */ -#define SEARCH_KEEP 0x400 /* keep previous search pattern */ -#define SEARCH_PEEK 0x800 /* peek for typed char, cancel search */ +// Values for 'options' argument in do_search() and searchit() +#define SEARCH_REV 0x01 ///< go in reverse of previous dir. +#define SEARCH_ECHO 0x02 ///< echo the search command and handle options +#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04) +#define SEARCH_NFMSG 0x08 ///< give all messages except not found +#define SEARCH_OPT 0x10 ///< interpret optional flags +#define SEARCH_HIS 0x20 ///< put search pattern in history +#define SEARCH_END 0x40 ///< put cursor at end of match +#define SEARCH_NOOF 0x80 ///< don't add offset to position +#define SEARCH_START 0x100 ///< start search without col offset +#define SEARCH_MARK 0x200 ///< set previous context mark +#define SEARCH_KEEP 0x400 ///< keep previous search pattern +#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search +#define SEARCH_COL 0x1000 ///< start at specified column instead of zero /* Values for flags argument for findmatchlimit() */ #define FM_BACKWARD 0x01 /* search backwards */ diff --git a/src/nvim/shada.c b/src/nvim/shada.c index def2de9b1a..32a02b0fb7 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -31,7 +31,6 @@ #include "nvim/misc2.h" #include "nvim/ex_getln.h" #include "nvim/search.h" -#include "nvim/eval.h" #include "nvim/regexp.h" #include "nvim/eval_defs.h" #include "nvim/version.h" @@ -39,6 +38,8 @@ #include "nvim/fileio.h" #include "nvim/strings.h" #include "nvim/quickfix.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/decode.h" #include "nvim/lib/khash.h" #include "nvim/lib/kvec.h" @@ -65,9 +66,6 @@ KHASH_SET_INIT_STR(strset) ((char *) copy_option_part((char_u **) src, (char_u *) dest, __VA_ARGS__)) #define find_shada_parameter(...) \ ((const char *) find_shada_parameter(__VA_ARGS__)) -#define emsg2(a, b) emsg2((char_u *) a, (char_u *) b) -#define emsg3(a, b, c) emsg3((char_u *) a, (char_u *) b, (char_u *) c) -#define emsgu(a, ...) emsgu((char_u *) a, __VA_ARGS__) #define home_replace_save(a, b) \ ((char *)home_replace_save(a, (char_u *)b)) #define home_replace(a, b, c, d, e) \ @@ -761,7 +759,7 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer) { const int fd = (int)(intptr_t) sd_writer->cookie; if (os_fsync(fd) < 0) { - emsg2(_(SERR "System error while synchronizing ShaDa file: %s"), + emsgf(_(SERR "System error while synchronizing ShaDa file: %s"), os_strerror(errno)); errno = 0; } @@ -811,11 +809,11 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, { if (sd_reader->skip(sd_reader, offset) != OK) { if (sd_reader->error != NULL) { - emsg2(_(SERR "System error while skipping in ShaDa file: %s"), + emsgf(_(SERR "System error while skipping in ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), (uint64_t) offset); @@ -849,7 +847,7 @@ open_file_start: goto open_file_start; } if (fd != UV_EEXIST) { - emsg3(_(SERR "System error while opening ShaDa file %s: %s"), + emsgf(_(SERR "System error while opening ShaDa file %s: %s"), fname, os_strerror(fd)); } return fd; @@ -897,7 +895,7 @@ close_file_start: errno = 0; goto close_file_start; } else { - emsg2(_(SERR "System error while closing ShaDa file: %s"), + emsgf(_(SERR "System error while closing ShaDa file: %s"), strerror(errno)); errno = 0; } @@ -934,7 +932,7 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *) data; ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len); if (written_bytes == -1) { - emsg2(_(SERR "System error while writing ShaDa file: %s"), + emsgf(_(SERR "System error while writing ShaDa file: %s"), sd_writer->error); return -1; } @@ -981,7 +979,7 @@ static int shada_read_file(const char *const file, const int flags) if (of_ret != 0) { if (of_ret == UV_ENOENT && (flags & kShaDaMissingError)) { - emsg3(_(SERR "System error while opening ShaDa file %s for reading: %s"), + emsgf(_(SERR "System error while opening ShaDa file %s for reading: %s"), fname, os_strerror(of_ret)); } xfree(fname); @@ -1687,8 +1685,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, do { \ if ((src) != NULL) { \ for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ - if (vim_to_msgpack(spacker, &li->li_tv, \ - _("additional elements of ShaDa " what)) == FAIL) { \ + if (encode_vim_to_msgpack(spacker, &li->li_tv, \ + _("additional elements of ShaDa " what)) \ + == FAIL) { \ goto shada_pack_entry_error; \ } \ } \ @@ -1706,8 +1705,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, const size_t key_len = strlen((const char *) hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ - if (vim_to_msgpack(spacker, &di->di_tv, \ - _("additional data of ShaDa " what)) == FAIL) { \ + if (encode_vim_to_msgpack(spacker, &di->di_tv, \ + _("additional data of ShaDa " what)) \ + == FAIL) { \ goto shada_pack_entry_error; \ } \ } \ @@ -1757,7 +1757,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, char vardesc[256] = "variable g:"; memcpy(&vardesc[sizeof("variable g:") - 1], varname.data, varname.size + 1); - if (vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc) + if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc) == FAIL) { ret = kSDWriteIgnError; EMSG2(_(WERR "Failed to write variable %s"), @@ -2159,7 +2159,7 @@ shada_parse_msgpack_read_next: {} break; } case MSGPACK_UNPACK_PARSE_ERROR: { - emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " + emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -2176,7 +2176,7 @@ shada_parse_msgpack_read_next: {} break; } case MSGPACK_UNPACK_CONTINUE: { - emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " + emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -2184,7 +2184,7 @@ shada_parse_msgpack_read_next: {} } case MSGPACK_UNPACK_EXTRA_BYTES: { shada_parse_msgpack_extra_bytes: - emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " + emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -3265,11 +3265,11 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, (void) read_bytes; if (sd_reader->error != NULL) { - emsg2(_(SERR "System error while reading ShaDa file: %s"), + emsgf(_(SERR "System error while reading ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), (uint64_t) length); @@ -3304,11 +3304,11 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, if (first_char == EOF) { if (sd_reader->error) { - emsg2(_(SERR "System error while reading integer from ShaDa file: %s"), + emsgf(_(SERR "System error while reading integer from ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "expected positive integer at position %" PRIu64 ", but got nothing"), (uint64_t) fpos); @@ -3339,7 +3339,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, break; } default: { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "expected positive integer at position %" PRIu64), (uint64_t) fpos); return kSDReadStatusNotShaDa; @@ -3403,18 +3403,18 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, proc) \ do { \ if (!(condition)) { \ - emsgu(_(READERR(entry_name, error_desc)), initial_fpos); \ + emsgf(_(READERR(entry_name, error_desc)), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } \ tgt = proc(obj.via.attr); \ } while (0) #define CHECK_KEY_IS_STR(entry_name) \ if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ - emsgu(_(READERR(entry_name, "has key which is not a string")), \ + emsgf(_(READERR(entry_name, "has key which is not a string")), \ initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \ - emsgu(_(READERR(entry_name, "has empty key")), initial_fpos); \ + emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } #define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \ @@ -3477,7 +3477,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, typval_T adtv; \ if (msgpack_to_vim(obj, &adtv) == FAIL \ || adtv.v_type != VAR_DICT) { \ - emsgu(_(READERR(name, \ + emsgf(_(READERR(name, \ "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ @@ -3502,7 +3502,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, }; \ typval_T aetv; \ if (msgpack_to_vim(obj, &aetv) == FAIL) { \ - emsgu(_(READERR(name, "cannot be converted to a VimL list")), \ + emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ clear_tv(&aetv); \ goto shada_read_next_item_error; \ @@ -3570,7 +3570,7 @@ shada_read_next_item_start: // kSDItemUnknown cannot possibly pass that far because it is -1 and that // will fail in msgpack_read_uint64. But kSDItemMissing may and it will // otherwise be skipped because (1 << 0) will never appear in flags. - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "there is an item at position %" PRIu64 " " "that must not be there: Missing items are " "for internal uses only"), @@ -3640,14 +3640,14 @@ shada_read_next_item_start: switch ((ShadaEntryType) type_u64) { case kSDItemHeader: { if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) { - emsgu(_(READERR("header", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("header", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } break; } case kSDItemSearchPattern: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("search pattern", "is not a dictionary")), + emsgf(_(READERR("search pattern", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } @@ -3678,7 +3678,7 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.search_pattern.pat == NULL) { - emsgu(_(READERR("search pattern", "has no pattern")), initial_fpos); + emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data, @@ -3690,7 +3690,7 @@ shada_read_next_item_start: case kSDItemGlobalMark: case kSDItemLocalMark: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("mark", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } garray_T ad_ga; @@ -3699,7 +3699,7 @@ shada_read_next_item_start: CHECK_KEY_IS_STR("mark") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { - emsgu(_(READERR("mark", "has n key which is only valid for " + emsgf(_(READERR("mark", "has n key which is only valid for " "local and global mark entries")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3716,15 +3716,15 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.filemark.fname == NULL) { - emsgu(_(READERR("mark", "is missing file name")), initial_fpos); + emsgf(_(READERR("mark", "is missing file name")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.filemark.mark.lnum <= 0) { - emsgu(_(READERR("mark", "has invalid line number")), initial_fpos); + emsgf(_(READERR("mark", "has invalid line number")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.filemark.mark.col < 0) { - emsgu(_(READERR("mark", "has invalid column number")), initial_fpos); + emsgf(_(READERR("mark", "has invalid column number")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark"); @@ -3732,7 +3732,7 @@ shada_read_next_item_start: } case kSDItemRegister: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("register", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("register", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } garray_T ad_ga; @@ -3742,14 +3742,14 @@ shada_read_next_item_start: if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, REG_KEY_CONTENTS)) { if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("register", + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " key with non-array value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) { - emsgu(_(READERR("register", + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " key with empty array")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); @@ -3758,7 +3758,7 @@ shada_read_next_item_start: unpacked.data.via.map.ptr[i].val.via.array; for (size_t i = 0; i < arr.size; i++) { if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("register", "has " REG_KEY_CONTENTS " array " + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " "with non-binary value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3778,7 +3778,7 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.reg.contents == NULL) { - emsgu(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), + emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3787,29 +3787,29 @@ shada_read_next_item_start: } case kSDItemHistoryEntry: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("history", "is not an array")), initial_fpos); + emsgf(_(READERR("history", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 2) { - emsgu(_(READERR("history", "does not have enough elements")), + emsgf(_(READERR("history", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgu(_(READERR("history", "has wrong history type type")), + emsgf(_(READERR("history", "has wrong history type type")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[1].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("history", "has wrong history string type")), + emsgf(_(READERR("history", "has wrong history string type")), initial_fpos); goto shada_read_next_item_error; } if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0, unpacked.data.via.array.ptr[1].via.bin.size) != NULL) { - emsgu(_(READERR("history", "contains string with zero byte inside")), + emsgf(_(READERR("history", "contains string with zero byte inside")), initial_fpos); goto shada_read_next_item_error; } @@ -3819,13 +3819,13 @@ shada_read_next_item_start: entry->data.history_item.histtype == HIST_SEARCH; if (is_hist_search) { if (unpacked.data.via.array.size < 3) { - emsgu(_(READERR("search history", + emsgf(_(READERR("search history", "does not have separator character")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgu(_(READERR("search history", + emsgf(_(READERR("search history", "has wrong history separator type")), initial_fpos); goto shada_read_next_item_error; } @@ -3867,22 +3867,16 @@ shada_read_next_item_hist_no_conv: } case kSDItemVariable: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("variable", "is not an array")), initial_fpos); + emsgf(_(READERR("variable", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 2) { - emsgu(_(READERR("variable", "does not have enough elements")), + emsgf(_(READERR("variable", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("variable", "has wrong variable name type")), - initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_NIL - || unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_EXT) { - emsgu(_(READERR("variable", "has wrong variable value type")), + emsgf(_(READERR("variable", "has wrong variable name type")), initial_fpos); goto shada_read_next_item_error; } @@ -3891,7 +3885,7 @@ shada_read_next_item_hist_no_conv: unpacked.data.via.array.ptr[0].via.bin.size); if (msgpack_to_vim(unpacked.data.via.array.ptr[1], &(entry->data.global_var.value)) == FAIL) { - emsgu(_(READERR("variable", "has value that cannot " + emsgf(_(READERR("variable", "has value that cannot " "be converted to the VimL value")), initial_fpos); goto shada_read_next_item_error; } @@ -3912,16 +3906,16 @@ shada_read_next_item_hist_no_conv: } case kSDItemSubString: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("sub string", "is not an array")), initial_fpos); + emsgf(_(READERR("sub string", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 1) { - emsgu(_(READERR("sub string", "does not have enough elements")), + emsgf(_(READERR("sub string", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("sub string", "has wrong sub string type")), + emsgf(_(READERR("sub string", "has wrong sub string type")), initial_fpos); goto shada_read_next_item_error; } @@ -3934,7 +3928,7 @@ shada_read_next_item_hist_no_conv: } case kSDItemBufferList: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("buffer list", "is not an array")), initial_fpos); + emsgf(_(READERR("buffer list", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size == 0) { @@ -3951,7 +3945,7 @@ shada_read_next_item_hist_no_conv: { msgpack_unpacked unpacked = unpacked_2; if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that is not a dictionary"), initial_fpos); @@ -3976,21 +3970,21 @@ shada_read_next_item_hist_no_conv: } } if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry with invalid line number"), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.buffer_list.buffers[i].pos.col < 0) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry with invalid column number"), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.buffer_list.buffers[i].fname == NULL) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that does not have a file name"), initial_fpos); diff --git a/src/nvim/spell.c b/src/nvim/spell.c index fdae89b84c..0acaa9ae2b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -319,7 +319,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/os/os.h" #include "nvim/os/input.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 8fcb02c3b6..da84106454 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -872,7 +872,7 @@ do_tag ( /* Let the SwapExists event know what tag we are jumping to. */ vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name); - set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1); + set_vim_var_string(VV_SWAPCOMMAND, (char *) IObuff, -1); /* * Jump to the desired match. @@ -1147,6 +1147,22 @@ find_tags ( int get_it_again = FALSE; int use_cscope = (flags & TAG_CSCOPE); int verbose = (flags & TAG_VERBOSE); + int save_p_ic = p_ic; + + // Change the value of 'ignorecase' according to 'tagcase' for the + // duration of this function. + switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) { + case TC_FOLLOWIC: + break; + case TC_IGNORE: + p_ic = true; + break; + case TC_MATCH: + p_ic = false; + break; + default: + assert(false); + } help_save = curbuf->b_help; orgpat.pat = pat; @@ -1955,6 +1971,8 @@ findtag_end: curbuf->b_help = help_save; xfree(saved_pat); + p_ic = save_p_ic; + return retval; } diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c deleted file mode 100644 index afe926b2ef..0000000000 --- a/src/nvim/tempfile.c +++ /dev/null @@ -1,139 +0,0 @@ -#include <inttypes.h> -#include <stdbool.h> -#include <stdlib.h> -#include <stdio.h> - -#include "nvim/ascii.h" -#include "nvim/memory.h" -#include "nvim/misc1.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" -#include "nvim/path.h" -#include "nvim/strings.h" -#include "nvim/tempfile.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.c.generated.h" -#endif - -/// Name of Vim's own temp dir. Ends in a slash. -static char_u *vim_tempdir = NULL; - -/// Create a directory for private use by this instance of Neovim. -/// This is done once, and the same directory is used for all temp files. -/// This method avoids security problems because of symlink attacks et al. -/// It's also a bit faster, because we only need to check for an existing -/// file when creating the directory and not for each temp file. -static void vim_maketempdir(void) -{ - static const char *temp_dirs[] = TEMP_DIR_NAMES; - // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. - char_u template[TEMP_FILE_PATH_MAXLEN]; - char_u path[TEMP_FILE_PATH_MAXLEN]; - for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { - // Expand environment variables, leave room for "/nvimXXXXXX/999999999" - // Skip the directory check if the expansion fails. - expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); - if (template[0] == '$' || !os_isdir(template)) { - continue; - } - - add_pathsep((char *)template); - // Concatenate with temporary directory name pattern - STRCAT(template, "nvimXXXXXX"); - - if (os_mkdtemp((const char *)template, (char *)path) != 0) { - continue; - } - - if (vim_settempdir((char *)path)) { - // Successfully created and set temporary directory so stop trying. - break; - } else { - // Couldn't set `vim_tempdir` to `path` so remove created directory. - os_rmdir((char *)path); - } - } -} - -/// Delete the temp directory and all files it contains. -void vim_deltempdir(void) -{ - if (vim_tempdir != NULL) { - snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir); - - char_u **files; - int file_count; - - // Note: We cannot just do `&NameBuff` because it is a statically - // sized array so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; - if (gen_expand_wildcards(1, buff_list, &file_count, &files, - EW_DIR|EW_FILE|EW_SILENT) == OK) { - for (int i = 0; i < file_count; ++i) { - os_remove((char *)files[i]); - } - FreeWild(file_count, files); - } - path_tail(NameBuff)[-1] = NUL; - os_rmdir((char *)NameBuff); - - xfree(vim_tempdir); - vim_tempdir = NULL; - } -} - -/// Get the name of temp directory. This directory would be created on the first -/// call to this function. -char_u *vim_gettempdir(void) -{ - if (vim_tempdir == NULL) { - vim_maketempdir(); - } - - return vim_tempdir; -} - -/// Set Neovim own temporary directory name to `tempdir`. This directory should -/// be already created. Expand this name to a full path and put it in -/// `vim_tempdir`. This avoids that using `:cd` would confuse us. -/// -/// @param tempdir must be no longer than MAXPATHL. -/// -/// @return false if we run out of memory. -static bool vim_settempdir(char *tempdir) -{ - char *buf = verbose_try_malloc(MAXPATHL + 2); - if (!buf) { - return false; - } - vim_FullName(tempdir, buf, MAXPATHL, false); - add_pathsep(buf); - vim_tempdir = (char_u *)xstrdup(buf); - xfree(buf); - return true; -} - -/// Return a unique name that can be used for a temp file. -/// -/// @note The temp file is NOT created. -/// -/// @return pointer to the temp file name or NULL if Neovim can't create -/// temporary directory for its own temporary files. -char_u *vim_tempname(void) -{ - // Temp filename counter. - static uint32_t temp_count; - - char_u *tempdir = vim_gettempdir(); - if (!tempdir) { - return NULL; - } - - // There is no need to check if the file exists, because we own the directory - // and nobody else creates a file in it. - char_u template[TEMP_FILE_PATH_MAXLEN]; - snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, - "%s%" PRIu32, tempdir, temp_count++); - return vim_strsave(template); -} diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h deleted file mode 100644 index c030a70eeb..0000000000 --- a/src/nvim/tempfile.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NVIM_TEMPFILE_H -#define NVIM_TEMPFILE_H - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.h.generated.h" -#endif - -#endif // NVIM_TEMPFILE_H diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 63a7e20880..0440272eb9 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -627,6 +627,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) api_free_object(dict_set_value(buf->b_vars, cstr_as_string("term_title"), STRING_OBJ(cstr_as_string(val->string)), + false, &err)); break; } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 55a49122d5..4debdd9acc 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -36,9 +36,12 @@ SCRIPTS := \ test_breakindent.out \ test_close_count.out \ test_marks.out \ - test_match_conceal.out \ -NEW_TESTS = test_viml.res +# Tests using runtest.vim.vim. +# Keep test_alot*.res as the last one, sort the others. +NEW_TESTS = \ + test_viml.res \ + test_alot.res SCRIPTS_GUI := test16.out diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in index 7fef87d383..a8c81b832d 100644 --- a/src/nvim/testdir/test17.in +++ b/src/nvim/testdir/test17.in @@ -41,17 +41,17 @@ STARTTEST :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :e! Xdir1/dir2/foo.a -i#include "bar.a" +i#include "bar.a": :w :e Xdir1/dir2/bar.a -i#include "baz.a" +i#include "baz.a": :w :e Xdir1/dir2/baz.a -i#include "foo.a" +i#include "foo.a": :w :e Xbase.a :set path=Xdir1/dir2 -i#include <foo.a> +i#include <foo.a>: :w :redir! >>test.out :checkpath! @@ -71,17 +71,17 @@ STARTTEST :endfunction :let &includeexpr='DotsToSlashes()' :e! Xdir1/dir2/foo.b -i%inc /bar/ +i%inc /bar/: :w :e Xdir1/dir2/bar.b -i%inc /baz/ +i%inc /baz/: :w :e Xdir1/dir2/baz.b -i%inc /foo/ +i%inc /foo/: :w :e Xbase.b :set path=Xdir1/dir2 -i%inc /foo/ +i%inc /foo/: :w :redir! >>test.out :checkpath! @@ -104,20 +104,20 @@ STARTTEST :endfunction :let &includeexpr='StripNewlineChar()' :e! Xdir1/dir2/foo.c -i%inc bar.c +i%inc bar.c: :w :e Xdir1/dir2/bar.c -i%inc baz.c +i%inc baz.c: :w :e Xdir1/dir2/baz.c -i%inc foo.c +i%inc foo.c: :w :e Xdir1/dir2/FALSE.c -i%inc foo.c +i%inc foo.c: :w :e Xbase.c :set path=Xdir1/dir2 -i%inc FALSE.c foo.c +i%inc FALSE.c foo.c: :w :redir! >>test.out :checkpath! diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in index 1ce57246ee..d95052e14c 100644 --- a/src/nvim/testdir/test49.in +++ b/src/nvim/testdir/test49.in @@ -8,7 +8,9 @@ STARTTEST :se nomore :lang mess C :so test49.vim -GGGGGGGGGGGGGG"rp:.-,$w! test.out +:" Go back to this file and append the results from register r. +:buf test49.in +G"rp:/^Results/,$w! test.out :" :" make valgrind happy :redir => funclist diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim new file mode 100644 index 0000000000..1d1da94bac --- /dev/null +++ b/src/nvim/testdir/test_alot.vim @@ -0,0 +1,3 @@ +" A series of tests that can run in one Vim invocation. +" This makes testing go faster, since Vim doesn't need to restart. + diff --git a/src/nvim/testdir/test_marks.in b/src/nvim/testdir/test_marks.in deleted file mode 100644 index 23c2fb65fe..0000000000 --- a/src/nvim/testdir/test_marks.in +++ /dev/null @@ -1,34 +0,0 @@ -Tests for marks. - -STARTTEST -:so small.vim -:" test that a deleted mark is restored after delete-undo-redo-undo -:/^\t/+1 -:set nocp viminfo+=nviminfo -madduu -:let a = string(getpos("'a")) -:$put ='Mark after delete-undo-redo-undo: '.a -:'' -ENDTEST - - textline A - textline B - textline C - -STARTTEST -:" test that CTRL-A and CTRL-X updates last changed mark '[, ']. -:/^123/ -:execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX" -ENDTEST - -CTRL-A CTRL-X: -123 123 123 -123 123 123 -123 123 123 - -STARTTEST -:g/^STARTTEST/.,/^ENDTEST/d -:wq! test.out -ENDTEST - -Results: diff --git a/src/nvim/testdir/test_marks.ok b/src/nvim/testdir/test_marks.ok deleted file mode 100644 index e6c02ee7b0..0000000000 --- a/src/nvim/testdir/test_marks.ok +++ /dev/null @@ -1,16 +0,0 @@ -Tests for marks. - - - textline A - textline B - textline C - - -CTRL-A CTRL-X: -AAA 123 123 -123 XXXXXXX -XXX 123 123 - - -Results: -Mark after delete-undo-redo-undo: [0, 15, 2, 0] diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim index 5d65953a9e..9f0618bd45 100644 --- a/src/nvim/testdir/test_viml.vim +++ b/src/nvim/testdir/test_viml.vim @@ -1,5 +1,5 @@ " Test various aspects of the Vim language. -" This was formerly in test49. +" Most of this was formerly in test49. "------------------------------------------------------------------------------- " Test environment {{{1 @@ -908,6 +908,22 @@ endfunc "------------------------------------------------------------------------------- +" Test 16: Recognizing {} in variable name. {{{1 +"------------------------------------------------------------------------------- + +func Test_curlies() + let s:var = 66 + let ns = 's' + call assert_equal(66, {ns}:var) + + let g:a = {} + let g:b = 't' + let g:a[g:b] = 77 + call assert_equal(77, g:a['t']) +endfunc + + +"------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker " vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "") diff --git a/src/nvim/version.c b/src/nvim/version.c index 3b160d0d99..a492c8d7ec 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,80 +69,395 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 1757, + 1755, 1654, 1652, 1643, 1641, + + // 1600 NA + // 1599 NA + // 1598 NA + // 1597 NA + // 1596, + // 1595 NA + // 1594 NA + // 1593 NA + // 1592, + // 1591, + // 1590, + // 1589, + // 1588, + // 1587 NA + // 1586, + // 1585, + // 1584 NA + // 1583 NA + // 1582, + + // 1581, + // 1580, + // 1579, + // 1578, + // 1577, + 1576, + // 1575 NA 1574, + // 1573, + // 1572 NA + // 1571, 1570, + 1569, + // 1568, + // 1567, + // 1566 NA + // 1565, + // 1564, + // 1563, + // 1562, + // 1561 NA + // 1560, + // 1559, + // 1558, + // 1557, + // 1556, + // 1555, + // 1554, + // 1553, + // 1552, + // 1551, + // 1550, + // 1549, + // 1548, + // 1547, + // 1546, + // 1545 NA + // 1544 NA + // 1543 NA + // 1542 NA + // 1541 NA + // 1540 NA + // 1539 NA + // 1538 NA + // 1537 NA + // 1536 NA + // 1535, + // 1534 NA + // 1533, + // 1532 NA + // 1531 NA + // 1530 NA + // 1529 NA + // 1528, + // 1527 NA + // 1526 NA + // 1525 NA + // 1524 NA + // 1523 NA + // 1522 NA + // 1521, + // 1520 NA + // 1519 NA + // 1518 NA + // 1517 NA + // 1516, + // 1515 NA + // 1514 NA + // 1513, + // 1512 NA 1511, + // 1510 NA + // 1509 NA + // 1508 NA + // 1507 NA + // 1506 NA + // 1505 NA + // 1504 NA + // 1503 NA + // 1502 NA + // 1501 NA + 1500, + // 1499, + // 1498 NA + // 1497 NA + // 1496 NA + // 1495 NA + // 1494, + // 1493 NA + // 1492, + // 1491, + // 1490 NA + // 1489 NA + // 1488 NA + // 1487 NA + // 1486, + // 1485 NA + // 1484 NA + // 1483 NA + // 1482 NA + // 1481 NA + // 1480, + // 1479, + // 1478, + // 1477, + // 1476 NA + // 1475 NA + // 1474 NA + // 1473 NA + // 1472 NA + // 1471 NA + // 1470 NA + // 1469 NA + // 1468, + // 1467 NA + // 1466 NA + // 1465 NA + // 1464, + // 1463 NA + // 1462 NA + // 1461 NA + // 1460 NA + // 1459 NA + // 1458 NA + // 1457 NA + // 1456, + // 1455 NA + // 1454 NA + // 1453 NA + // 1452 NA + // 1451 NA + // 1450 NA + // 1449 NA + // 1448 NA + // 1447 NA + // 1446 NA + // 1445 NA + // 1444 NA + // 1443 NA + // 1442 NA + // 1441 NA + // 1440 NA + // 1439 NA + // 1438 NA + // 1437 NA + // 1436 NA + // 1435 NA + // 1434 NA + // 1433 NA + // 1432 NA + // 1431 NA + // 1430 NA + // 1429 NA + // 1428 NA + // 1427 NA + // 1426 NA 1425, + // 1424 NA + // 1423 NA + // 1422 NA + // 1421 NA + // 1420 NA + // 1419 NA + // 1418 NA + // 1417 NA + // 1416 NA + // 1415 NA + // 1414 NA + // 1413 NA + // 1412 NA + // 1411 NA + 1410, + // 1409 NA + // 1408 NA + // 1407 NA + 1406, + 1405, + // 1404 NA + // 1403 NA + // 1402 NA + // 1401, + // 1400 NA + // 1399 NA + // 1398 NA + // 1397, + // 1396, + // 1395 NA + // 1394, + // 1393 NA + // 1392 NA + // 1391 NA + // 1390 NA + // 1389 NA + // 1388, + // 1387 NA + // 1386 NA + // 1385 NA + // 1384, + // 1383 NA + // 1382 NA + // 1381 NA + // 1380 NA + // 1379 NA + // 1378 NA + // 1377 NA + // 1376 NA + // 1375 NA + // 1374 NA + // 1373 NA + // 1372 NA + // 1371 NA + // 1370 NA + // 1369 NA + // 1368 NA + // 1367 NA 1366, + // 1365, + // 1364 NA + // 1363 NA + // 1362 NA + // 1361 NA + // 1360 NA + // 1359 NA + // 1358 NA + // 1357 NA + // 1356 NA + // 1355 NA + // 1354 NA + // 1353 NA + // 1352, + // 1351 NA + // 1350 NA + // 1349 NA + // 1348 NA + // 1347, + 1346, + // 1345 NA + // 1344 NA + // 1343 NA + // 1342 NA + // 1341 NA + // 1340 NA + // 1339 NA + // 1338 NA + // 1337 NA + // 1336 NA + // 1335 NA + // 1334 NA + // 1333 NA + // 1332 NA + // 1331 NA + // 1330 NA + // 1329 NA + // 1328 NA + // 1327 NA + // 1326 NA + // 1325 NA + // 1324 NA + // 1323 NA + // 1322 NA + // 1321 NA + // 1320 NA + // 1319 NA + // 1318 NA + // 1317 NA + // 1316 NA + // 1315 NA + // 1314 NA + // 1313 NA + // 1312 NA + // 1311 NA + // 1310 NA + 1309, + // 1308 NA + // 1307 NA + // 1306 NA + // 1305, + 1304, + // 1303 NA + // 1302 NA + // 1301 NA + // 1300, + // 1299 NA + // 1298 NA + // 1297 NA + // 1296, + // 1295 NA + // 1294 NA + // 1293 NA + 1292, + // 1291 NA + // 1290 NA + // 1289 NA + // 1288 NA + // 1287 NA + // 1286 NA + // 1285, 1284, - // 1283 + // 1283 NA 1282, - // 1281 - // 1280 - // 1279 - // 1278 - // 1277 - // 1276 - // 1275 - // 1274 - // 1273 - // 1272 - // 1271 - // 1270 - // 1269 - // 1268 - // 1267 + // 1281, + // 1280 NA + // 1279 NA + // 1278 NA + // 1277 NA + // 1276, + // 1275 NA + // 1274 NA + // 1273, + // 1272 NA + 1271, + // 1270 NA + 1269, + // 1268 NA + 1267, // 1266 - // 1265 - // 1264 - // 1263 - // 1262 - // 1261 - // 1260 - // 1259 - // 1258 - // 1257 - // 1256 - // 1255 - // 1254 - // 1253 - // 1252 - // 1251 - // 1250 - // 1249 - // 1248 - // 1247 - // 1246 - // 1245 - // 1244 - // 1243 - // 1242 - // 1241 - // 1240 - // 1239 - // 1238 - // 1237 - // 1236 - // 1235 - // 1234 - // 1233 - // 1232 - // 1231 - // 1230 - // 1229 - // 1228 - // 1227 - // 1226 - // 1225 - // 1224 - // 1223 - // 1223 - // 1221 - // 1220 + // 1265 NA + // 1264 NA + // 1263 NA + // 1262 NA + // 1261 NA + // 1260 NA + // 1259, + // 1258 NA + // 1257 NA + // 1256 NA + // 1255 NA + // 1254 NA + // 1253 NA + // 1252 NA + // 1251 NA + // 1250 NA + // 1249 NA + // 1248 NA + // 1247 NA + // 1246 NA + // 1245 NA + // 1244 NA + // 1243 NA + // 1242 NA + // 1241 NA + // 1240 NA + // 1239 NA + // 1238 NA + // 1237, + // 1236, + // 1235 NA + // 1234 NA + // 1233 NA + // 1232 NA + // 1231 NA + // 1230 NA + // 1229 NA + 1228, + // 1227 NA + // 1226 NA + // 1225 NA + // 1224 NA + // 1223, + // 1222 NA + // 1221 NA + // 1220 NA // 1219 NA // 1218 NA // 1217 NA @@ -174,41 +489,41 @@ static int included_patches[] = { // 1191 NA // 1190 NA // 1189 NA - // 1188, + // 1188 NA // 1187 NA // 1186, // 1185 NA // 1184 NA // 1183 NA // 1182 NA - // 1181, + 1181, 1180, // 1179, - // 1178, + 1178, // 1177 NA // 1176 NA // 1175 NA // 1174 NA - // 1173, + 1173, // 1172 NA // 1171 NA // 1170 NA // 1169 NA - // 1168, - // 1167, - // 1166, + 1168, + 1167, + 1166, // 1165 NA - // 1164, - // 1163, + 1164, + 1163, // 1162 NA // 1161, - // 1160, + 1160, // 1159 NA // 1158 NA - // 1157, - // 1156, + 1157, + // 1156 NA // 1155 NA - // 1154, + // 1154 NA // 1153, // 1152 NA // 1151, @@ -218,8 +533,8 @@ static int included_patches[] = { // 1147, // 1146 NA // 1145 NA - // 1144 NA - // 1143, + 1144, + 1143, // 1142, 1141, // 1140, @@ -227,8 +542,8 @@ static int included_patches[] = { // 1138 NA 1137, // 1136, - // 1135 NA, - // 1134 NA, + // 1135 NA + // 1134 NA // 1133 NA // 1132, // 1131 NA @@ -242,20 +557,20 @@ static int included_patches[] = { // 1123, // 1122 NA // 1121, - // 1120, + 1120, // 1119, // 1118, - // 1117, - // 1116, + 1117, + 1116, // 1115 NA - // 1114, - // 1113, + 1114, + 1113, 1112, // 1111, // 1110, // 1109 NA // 1108, - // 1107, + 1107, // 1106 NA 1105, // 1104 NA @@ -269,7 +584,7 @@ static int included_patches[] = { // 1096, // 1095 NA // 1094, - // 1093, + 1093, // 1092, // 1091, // 1090, @@ -279,30 +594,30 @@ static int included_patches[] = { // 1086, 1085, 1084, - // 1083 NA, - // 1082 NA, + // 1083 NA + // 1082 NA 1081, - // 1080 NA, + // 1080 NA // 1079, - // 1078 NA, - // 1077 NA, + // 1078 NA + // 1077 NA 1076, // 1075, - // 1074 NA, + // 1074 NA // 1073, 1072, // 1071, - // 1070 NA, - // 1069 NA, + // 1070 NA + // 1069 NA // 1068, - // 1067 NA, - // 1066 NA, + // 1067 NA + // 1066 NA 1065, // 1064, - // 1063 NA, - // 1062 NA, + // 1063 NA + // 1062 NA // 1061, - // 1060 NA, + // 1060 NA // 1059, // 1058, // 1057, @@ -317,44 +632,44 @@ static int included_patches[] = { // 1048, // 1047, // 1046, - // 1045 NA, - // 1044 NA, - // 1043 NA, + // 1045 NA + // 1044 NA + // 1043 NA // 1042, 1041, - // 1040 NA, + // 1040 NA // 1039, - // 1038 NA, + // 1038 NA // 1037, // 1036, // 1035, // 1034, - // 1033 NA, + // 1033 NA 1032, // 1031 NA, 1030, 1029, - // 1028 NA, + // 1028 NA 1027, - // 1026 NA, - // 1025 NA, - // 1024 NA, - // 1023 NA, - // 1022 NA, - // 1021 NA, - // 1020 NA, - // 1019 NA, + // 1026 NA + // 1025 NA + // 1024 NA + // 1023 NA + // 1022 NA + // 1021 NA + // 1020 NA + // 1019 NA // 1018, // 1017, - // 1016 NA, + // 1016 NA // 1015, - // 1014 NA, + // 1014 NA 1013, - // 1012 NA, - // 1011 NA, + // 1012 NA + // 1011 NA // 1010, - // 1009 NA, - // 1008 NA, + // 1009 NA + // 1008 NA // 1007, // 1006, // 1005, @@ -364,7 +679,7 @@ static int included_patches[] = { 1001, 1000, // 999 NA - // 998, + 998, // 997 NA // 996 NA // 995 NA @@ -378,8 +693,8 @@ static int included_patches[] = { // 987 NA // 986 NA // 985 NA - // 984, - // 983, + 984, + // 983 NA // 982 NA 981, 980, @@ -405,13 +720,13 @@ static int included_patches[] = { // 960 NA // 959 NA 958, - // 957, + 957, // 956 NA 955, // 954 NA 953, 952, - // 951, + 951, 950, 949, // 948 NA @@ -420,8 +735,8 @@ static int included_patches[] = { 945, 944, // 943 NA - // 942, - // 941, + 942, + 941, // 940 NA 939, // 938 NA @@ -540,10 +855,10 @@ static int included_patches[] = { 825, // 824 NA 823, - // 822, + 822, // 821 NA 820, - // 819, + 819, 818, 817, 816, @@ -565,7 +880,7 @@ static int included_patches[] = { 800, 799, 798, - // 797, + // 797 NA // 796 NA 795, // 794 NA @@ -629,7 +944,7 @@ static int included_patches[] = { 736, // 735 NA 734, - // 733, + // 733 NA 732, // 731 NA // 730 NA @@ -1118,13 +1433,13 @@ static int included_patches[] = { 247, // 246 NA 245, - // 244, + // 244 NA 243, 242, 241, 240, 239, - // 238, + // 238 NA 237, 236, 235, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 545b903d2f..623ea19e36 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -271,26 +271,11 @@ enum { # define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs)) -#define MSG(s) msg((char_u *)(s)) -#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr)) -#define EMSG(s) emsg((char_u *)(s)) -#define EMSG2(s, p) emsg2((char_u *)(s), (char_u *)(p)) -#define EMSG3(s, p, q) emsg3((char_u *)(s), (char_u *)(p), \ - (char_u *)(q)) -#define EMSGN(s, n) emsgn((char_u *)(s), (int64_t)(n)) -#define EMSGU(s, n) emsgu((char_u *)(s), (uint64_t)(n)) -#define OUT_STR(s) out_str((char_u *)(s)) -#define OUT_STR_NF(s) out_str_nf((char_u *)(s)) -#define MSG_PUTS(s) msg_puts((char_u *)(s)) -#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a)) -#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s)) -#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0) -#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a)) - -/* Prefer using emsg3(), because perror() may send the output to the wrong - * destination and mess up the screen. */ -#define PERROR(msg) \ - (void) emsg3((char_u *) "%s: %s", (char_u *)msg, (char_u *)strerror(errno)) +#include "nvim/message.h" + +// Prefer using emsgf(), because perror() may send the output to the wrong +// destination and mess up the screen. +#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno)) #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */ diff --git a/src/nvim/window.c b/src/nvim/window.c index 1b8c953596..bea55c465f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -97,7 +97,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, 0); + (void)win_split((int)Prenum, 0); break; /* split current window in two parts, vertically */ @@ -108,7 +108,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, WSP_VERT); + (void)win_split((int)Prenum, WSP_VERT); break; /* split current window and edit alternate file */ @@ -2948,7 +2948,7 @@ void free_tabpage(tabpage_T *tp) unref_var_dict(tp->tp_vars); - + xfree(tp->localdir); // Free tab-local working directory xfree(tp); } @@ -3560,18 +3560,24 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ - if (curwin->w_localdir != NULL) { - /* Window has a local directory: Save current directory as global - * directory (unless that was done already) and change to the local - * directory. */ + // The new directory is either the local directory of the window, of the tab + // or NULL. + char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir; + + if (new_dir) { + // Window/tab has a local directory: Save current directory as global + // directory (unless that was done already) and change to the local + // directory. if (globaldir == NULL) { char_u cwd[MAXPATHL]; - if (os_dirname(cwd, MAXPATHL) == OK) + if (os_dirname(cwd, MAXPATHL) == OK) { globaldir = vim_strsave(cwd); + } + } + if (os_chdir((char *)new_dir) == 0) { + shorten_fnames(true); } - if (os_chdir((char *)curwin->w_localdir) == 0) - shorten_fnames(TRUE); } else if (globaldir != NULL) { /* Window doesn't have a local directory and we are not in the global * directory: Change to the global directory. */ |