diff options
Diffstat (limited to 'src')
86 files changed, 7758 insertions, 5145 deletions
diff --git a/src/.clang-format b/src/.clang-format index 35e545ac4b..5a910ff34b 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -1,4 +1,4 @@ -BasedOnStyle: llvm +BasedOnStyle: Google Language: Cpp ColumnLimit: 80 IndentWidth: 2 @@ -10,3 +10,9 @@ AlignEscapedNewlinesLeft: false AllowShortFunctionsOnASingleLine: false SpacesBeforeTrailingComments: 2 PenaltyReturnTypeOnItsOwnLine: 200 +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackParameters: false +BreakBeforeBinaryOperators: true +ContinuationIndentWidth: 4 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index ef9e11fbef..a473f99278 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -41,25 +41,33 @@ 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) +# Sort file lists to ensure generated files are created in the same order from +# build to build. +list(SORT NEOVIM_SOURCES) +list(SORT NEOVIM_HEADERS) + foreach(sfile ${NEOVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") @@ -72,7 +80,6 @@ list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) # Handle legacy files that don't yet pass -Wconversion. set(CONV_SOURCES buffer.c - charset.c diff.c edit.c eval.c @@ -84,10 +91,7 @@ set(CONV_SOURCES mbyte.c memline.c message.c - misc1.c ops.c - path.c - quickfix.c regexp.c screen.c search.c diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index fa4b8e5f7d..55b535c78c 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -45,14 +45,22 @@ Integer buffer_line_count(Buffer buffer, Error *err) /// Gets a buffer line /// +/// @deprecated use buffer_get_lines instead. +/// for positive indices (including 0) use +/// "buffer_get_lines(buffer, index, index+1, true)" +/// for negative indices use +/// "buffer_get_lines(buffer, index-1, index, true)" +/// /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred /// @return The line string String buffer_get_line(Buffer buffer, Integer index, Error *err) { - String rv = {.size = 0}; - Array slice = buffer_get_line_slice(buffer, index, index, true, true, err); + String rv = { .size = 0 }; + + index = convert_index(index); + Array slice = buffer_get_lines(buffer, index, index+1, true, err); if (!err->set && slice.size) { rv = slice.items[0].data.string; @@ -65,6 +73,12 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// Sets a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [line])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [line])" +/// /// @param buffer The buffer handle /// @param index The line index /// @param line The new line. @@ -72,23 +86,34 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { Object l = STRING_OBJ(line); - Array array = {.items = &l, .size = 1}; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + Array array = { .items = &l, .size = 1 }; + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Deletes a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [])" /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) { Array array = ARRAY_DICT_INIT; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Retrieves a line range from the buffer /// +/// @deprecated use buffer_get_lines(buffer, newstart, newend, false) +/// where newstart = start + int(not include_start) - int(start < 0) +/// newend = end + int(include_end) - int(end < 0) +/// int(bool) = 1 if bool is true else 0 /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -103,16 +128,48 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Boolean include_end, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + return buffer_get_lines(buffer, start , end, false, err); +} + + +/// Retrieves a line range from the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to get the +/// last element set start=-2 and end=-1. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param[out] err Details of an error that may have occurred +/// @return An array of lines +ArrayOf(String) buffer_get_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + Error *err) +{ Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf || !inbounds(buf, start)) { + if (!buf) { return rv; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { + api_set_error(err, Validation, _("Index out of bounds")); + return rv; + } if (start >= end) { // Return 0-length array @@ -152,8 +209,14 @@ end: return rv; } + /// Replaces a line range on the buffer /// +/// @deprecated use buffer_set_lines(buffer, newstart, newend, false, lines) +/// where newstart = start + int(not include_start) + int(start < 0) +/// newend = end + int(include_end) + int(end < 0) +/// int(bool) = 1 if bool is true else 0 +/// /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -170,20 +233,52 @@ void buffer_set_line_slice(Buffer buffer, ArrayOf(String) replacement, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + buffer_set_lines(buffer, start, end, false, replacement, err); +} + + +/// Replaces line range on the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to change +/// or delete the last element set start=-2 and end=-1. +/// +/// To insert lines at a given index, set both start and end to the same index. +/// To delete a range of lines, set replacement to an empty array. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param replacement An array of lines to use as replacement +/// @param[out] err Details of an error that may have occurred +void buffer_set_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + ArrayOf(String) replacement, + Error *err) +{ buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return; } - if (!inbounds(buf, start)) { + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { api_set_error(err, Validation, _("Index out of bounds")); return; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); if (start > end) { api_set_error(err, @@ -328,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); @@ -343,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 @@ -457,6 +575,8 @@ Boolean buffer_is_valid(Buffer buffer) /// Inserts a sequence of lines to a buffer at a certain index /// +/// @deprecated use buffer_set_lines(buffer, lnum, lnum, true, lines) +/// /// @param buffer The buffer handle /// @param lnum Insert the lines after `lnum`. If negative, it will append /// to the end of the buffer. @@ -467,8 +587,9 @@ void buffer_insert(Buffer buffer, ArrayOf(String) lines, Error *err) { - bool end_start = lnum < 0; - buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err); + // "lnum" will be the index of the line after inserting, + // no matter if it is negative or not + buffer_set_lines(buffer, lnum, lnum, true, lines, err); } /// Return a tuple (row,col) representing the position of the named mark @@ -632,20 +753,26 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) } // Normalizes 0-based indexes to buffer line numbers -static int64_t normalize_index(buf_T *buf, int64_t index) +static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) { + int64_t line_count = buf->b_ml.ml_line_count; // Fix if < 0 - index = index < 0 ? buf->b_ml.ml_line_count + index : index; + index = index < 0 ? line_count + index +1 : index; + + // Check for oob + if (index > line_count) { + *oob = true; + index = line_count; + } else if (index < 0) { + *oob = true; + index = 0; + } // Convert the index to a vim line number index++; - // Fix if > line_count - index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index; return index; } -// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds. -static bool inbounds(buf_T *buf, int64_t index) +static int64_t convert_index(int64_t index) { - linenr_T nlines = buf->b_ml.ml_line_count; - return index >= -nlines && index < nlines; + return index < 0 ? index - 1 : index; } 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 9279f6b469..243806b3f1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -291,7 +291,7 @@ void vim_change_directory(String dir, Error *err) return; } - post_chdir(false); + post_chdir(kCdScopeGlobal); try_end(err); } @@ -331,15 +331,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 62ab7495da..529d0889bd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -141,14 +141,21 @@ open_buffer ( /* mark cursor position as being invalid */ curwin->w_valid = 0; - if (curbuf->b_ffname != NULL - ) { + if (curbuf->b_ffname != NULL) { + int old_msg_silent = msg_silent; + if (shortmess(SHM_FILEINFO)) { + msg_silent = 1; + } + retval = readfile(curbuf->b_ffname, curbuf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_NEW); - /* Help buffer is filtered. */ - if (curbuf->b_help) + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, + flags | READ_NEW); + msg_silent = old_msg_silent; + + // Help buffer is filtered. + if (curbuf->b_help) { fix_help_buffer(); + } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; linenr_T line_count; @@ -257,17 +264,16 @@ open_buffer ( return retval; } -/* - * Return TRUE if "buf" points to a valid buffer (in the buffer list). - */ -int buf_valid(buf_T *buf) +/// Check that "buf" points to a valid buffer (in the buffer list). +bool buf_valid(buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_BUFFERS(bp) { if (bp == buf) { - return TRUE; + return true; } } - return FALSE; + return false; } /* @@ -1547,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); @@ -2055,16 +2062,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, } -/* - * Return true when "wip" has 'diff' set and the diff is only for another tab - * page. That's because a diff is local to a tab page. - */ +/// Check that "wip" has 'diff' set and the diff is only for another tab page. +/// That's because a diff is local to a tab page. static bool wininfo_other_tab_diff(wininfo_T *wip) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (wip->wi_opt.wo_diff) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - /* return false when it's a window in the current tab page, thus - * the buffer was in diff mode here */ + // return false when it's a window in the current tab page, thus + // the buffer was in diff mode here if (wip->wi_win == wp) { return false; } @@ -2421,52 +2427,62 @@ void buflist_altfpos(win_T *win) buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE); } -/* - * Return TRUE if 'ffname' is not the same file as current file. - * Fname must have a full path (expanded by path_get_absolute_path()). - */ -int otherfile(char_u *ffname) +/// Check that "ffname" is not the same file as current file. +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param ffname full path name to check +bool otherfile(char_u *ffname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return otherfile_buf(curbuf, ffname, NULL, false); } -static int otherfile_buf(buf_T *buf, char_u *ffname, - FileID *file_id_p, bool file_id_valid) +/// Check that "ffname" is not the same file as the file loaded in "buf". +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param buf buffer to check +/// @param ffname full path name to check +/// @param file_id_p information about the file at "ffname". +/// @param file_id_valid whether a valid "file_id_p" was passed in. +static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, + bool file_id_valid) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* no name is different */ + // no name is different if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) { - return TRUE; + return true; } if (fnamecmp(ffname, buf->b_ffname) == 0) { - return FALSE; + return false; } { FileID file_id; - /* If no struct stat given, get it now */ + // If no struct stat given, get it now if (file_id_p == NULL) { file_id_p = &file_id; file_id_valid = os_fileid((char *)ffname, file_id_p); } if (!file_id_valid) { // file_id not valid, assume files are different. - return TRUE; + return true; } - /* Use dev/ino to check if the files are the same, even when the names - * are different (possible with links). Still need to compare the - * name above, for when the file doesn't exist yet. - * Problem: The dev/ino changes when a file is deleted (and created - * again) and remains the same when renamed/moved. We don't want to - * stat() each buffer each time, that would be too slow. Get the - * dev/ino again when they appear to match, but not when they appear - * to be different: Could skip a buffer when it's actually the same - * file. */ + // Use dev/ino to check if the files are the same, even when the names + // are different (possible with links). Still need to compare the + // name above, for when the file doesn't exist yet. + // Problem: The dev/ino changes when a file is deleted (and created + // again) and remains the same when renamed/moved. We don't want to + // stat() each buffer each time, that would be too slow. Get the + // dev/ino again when they appear to match, but not when they appear + // to be different: Could skip a buffer when it's actually the same + // file. if (buf_same_file_id(buf, file_id_p)) { buf_set_file_id(buf); - if (buf_same_file_id(buf, file_id_p)) - return FALSE; + if (buf_same_file_id(buf, file_id_p)) { + return false; + } } } - return TRUE; + return true; } // Set file_id for a buffer. @@ -2483,11 +2499,14 @@ void buf_set_file_id(buf_T *buf) } } -// return TRUE if file_id in buffer "buf" matches with "file_id". +/// Check that file_id in buffer "buf" matches with "file_id". +/// +/// @param buf buffer +/// @param file_id file id static bool buf_same_file_id(buf_T *buf, FileID *file_id) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return buf->file_id_valid - && os_fileid_equal(&(buf->file_id), file_id); + return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id); } /* @@ -2770,23 +2789,28 @@ void maketitle(void) resettitle(); } -/* - * Used for title and icon: Check if "str" differs from "*last". Set "*last" - * from "str" if it does. - * Return TRUE when "*last" changed. - */ -static int ti_change(char_u *str, char_u **last) +/// Used for title and icon: Check if "str" differs from "*last". Set "*last" +/// from "str" if it does by freeing the old value of "*last" and duplicating +/// "str". +/// +/// @param str desired title string +/// @param[in,out] last current title string +// +/// @return true when "*last" changed. +static bool ti_change(char_u *str, char_u **last) + FUNC_ATTR_WARN_UNUSED_RESULT { if ((str == NULL) != (*last == NULL) || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) { xfree(*last); - if (str == NULL) + if (str == NULL) { *last = NULL; - else + } else { *last = vim_strsave(str); - return TRUE; + } + return true; } - return FALSE; + return false; } /* @@ -3014,7 +3038,7 @@ int build_stl_str_hl( && item[groupitem[groupdepth]].minwid == 0) { bool has_normal_items = false; for (long n = groupitem[groupdepth] + 1; n < curitem; n++) { - if (item[n].type == Normal) { + if (item[n].type == Normal || item[n].type == Highlight) { has_normal_items = true; break; } @@ -3922,26 +3946,29 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) : (int)(above * 100L / (above + below))); } -/* - * Append (file 2 of 8) to "buf[buflen]", if editing more than one file. - * Return TRUE if it was appended. - */ -static int -append_arg_number ( - win_T *wp, - char_u *buf, - int buflen, - int add_file /* Add "file" before the arg number */ -) +/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file. +/// +/// @param wp window whose buffers to check +/// @param[in,out] buf string buffer to add the text to +/// @param buflen length of the string buffer +/// @param add_file if true, add "file" before the arg number +/// +/// @return true if it was appended. +static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) + FUNC_ATTR_NONNULL_ALL { - char_u *p; + // Nothing to do + if (ARGCOUNT <= 1) { + return false; + } + + char_u *p = buf + STRLEN(buf); // go to the end of the buffer - if (ARGCOUNT <= 1) /* nothing to do */ - return FALSE; + // Early out if the string is getting too long + if (p - buf + 35 >= buflen) { + return false; + } - p = buf + STRLEN(buf); /* go to the end of the buffer */ - if (p - buf + 35 >= buflen) /* getting too long */ - return FALSE; *p++ = ' '; *p++ = '('; if (add_file) { @@ -3949,9 +3976,10 @@ append_arg_number ( p += 5; } vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), - wp->w_arg_idx_invalid ? "(%d) of %d)" - : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); - return TRUE; + wp->w_arg_idx_invalid + ? "(%d) of %d)" + : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); + return true; } /* @@ -4585,11 +4613,16 @@ char_u *buf_spname(buf_T *buf) return NULL; } -/* - * Find a window for buffer "buf". - * If found true is returned and "wp" and "tp" are set to the window and tabpage. - * If not found false is returned. - */ +/// Find a window for buffer "buf". +/// If found true is returned and "wp" and "tp" are set to +/// the window and tabpage. +/// If not found, false is returned. +/// +/// @param buf buffer to find a window for +/// @param[out] wp stores the found window +/// @param[out] tp stores the found tabpage +/// +/// @return true if a window was found for the buffer. bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) { *wp = NULL; @@ -5103,50 +5136,54 @@ void set_buflisted(int on) } } -/* - * Read the file for "buf" again and check if the contents changed. - * Return TRUE if it changed or this could not be checked. - */ -int buf_contents_changed(buf_T *buf) +/// Read the file for "buf" again and check if the contents changed. +/// Return true if it changed or this could not be checked. +/// +/// @param buf buffer to check +/// +/// @return true if the buffer's contents have changed +bool buf_contents_changed(buf_T *buf) + FUNC_ATTR_NONNULL_ALL { - buf_T *newbuf; - int differ = TRUE; - linenr_T lnum; - aco_save_T aco; - exarg_T ea; + bool differ = true; - /* Allocate a buffer without putting it in the buffer list. */ - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); - if (newbuf == NULL) - return TRUE; + // Allocate a buffer without putting it in the buffer list. + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + if (newbuf == NULL) { + return true; + } - /* Force the 'fileencoding' and 'fileformat' to be equal. */ + // Force the 'fileencoding' and 'fileformat' to be equal. + exarg_T ea; prep_exarg(&ea, buf); - /* set curwin/curbuf to buf and save a few things */ + // set curwin/curbuf to buf and save a few things + aco_save_T aco; aucmd_prepbuf(&aco, newbuf); if (ml_open(curbuf) == OK && readfile(buf->b_ffname, buf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - &ea, READ_NEW | READ_DUMMY) == OK) { - /* compare the two files line by line */ + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, + &ea, READ_NEW | READ_DUMMY) == OK) { + // compare the two files line by line if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) { - differ = FALSE; - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) - if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) { - differ = TRUE; + differ = false; + for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { + if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) { + differ = true; break; } + } } } xfree(ea.cmd); - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); - if (curbuf != newbuf) /* safety check */ - wipe_buffer(newbuf, FALSE); + if (curbuf != newbuf) { // safety check + wipe_buffer(newbuf, false); + } return differ; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 936a14b903..c0cd801cd4 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -533,9 +533,9 @@ struct file_buffer { /* * Character table, only used in charset.c for 'iskeyword' - * 32 bytes of 8 bits: 1 bit per character 0-255. + * bitset with 4*64=256 bits: 1 bit per character 0-255. */ - char_u b_chartab[32]; + uint64_t b_chartab[4]; /* Table used for mappings local to a buffer. */ mapblock_T *(b_maphash[256]); @@ -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/charset.c b/src/nvim/charset.c index 4e329b5cd8..83e2aaa6e6 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -32,16 +32,16 @@ #endif -static int chartab_initialized = FALSE; +static bool chartab_initialized = false; -// b_chartab[] is an array of 32 bytes, each bit representing one of the +// b_chartab[] is an array with 256 bits, each bit representing one of the // characters 0-255. #define SET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] |= (1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] &= ~(1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 3] & (1 << ((c) & 0x7))) + ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) /// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword /// characters for current buffer. @@ -69,12 +69,12 @@ static int chartab_initialized = FALSE; /// an error, OK otherwise. int init_chartab(void) { - return buf_init_chartab(curbuf, TRUE); + return buf_init_chartab(curbuf, true); } /// Helper for init_chartab /// -/// @param global FALSE: only set buf->b_chartab[] +/// @param global false: only set buf->b_chartab[] /// /// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has /// an error, OK otherwise. @@ -84,13 +84,13 @@ int buf_init_chartab(buf_T *buf, int global) int c2; char_u *p; int i; - int tilde; - int do_isalpha; + bool tilde; + bool do_isalpha; if (global) { // Set the default size for printable characters: // From <Space> to '~' is 1 (printable), others are 2 (not printable). - // This also inits all 'isident' and 'isfname' flags to FALSE. + // This also inits all 'isident' and 'isfname' flags to false. c = 0; while (c < ' ') { @@ -133,7 +133,7 @@ int buf_init_chartab(buf_T *buf, int global) } } - // Init word char flags all to FALSE + // Init word char flags all to false memset(buf->b_chartab, 0, (size_t)32); if (enc_dbcs != 0) { @@ -169,11 +169,11 @@ int buf_init_chartab(buf_T *buf, int global) } while (*p) { - tilde = FALSE; - do_isalpha = FALSE; + tilde = false; + do_isalpha = false; if ((*p == '^') && (p[1] != NUL)) { - tilde = TRUE; + tilde = true; ++p; } @@ -212,7 +212,7 @@ int buf_init_chartab(buf_T *buf, int global) // standard function isalpha(). This takes care of locale for // single-byte characters). if (c == '@') { - do_isalpha = TRUE; + do_isalpha = true; c = 1; c2 = 255; } else { @@ -231,7 +231,7 @@ int buf_init_chartab(buf_T *buf, int global) if (i == 0) { // (re)set ID flag if (tilde) { - chartab[c] &= ~CT_ID_CHAR; + chartab[c] &= (uint8_t)~CT_ID_CHAR; } else { chartab[c] |= CT_ID_CHAR; } @@ -244,18 +244,18 @@ int buf_init_chartab(buf_T *buf, int global) || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) && !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) { if (tilde) { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) - + ((dy_flags & DY_UHEX) ? 4 : 2); - chartab[c] &= ~CT_PRINT_CHAR; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + + ((dy_flags & DY_UHEX) ? 4 : 2)); + chartab[c] &= (uint8_t)~CT_PRINT_CHAR; } else { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1); chartab[c] |= CT_PRINT_CHAR; } } } else if (i == 2) { // (re)set fname flag if (tilde) { - chartab[c] &= ~CT_FNAME_CHAR; + chartab[c] &= (uint8_t)~CT_FNAME_CHAR; } else { chartab[c] |= CT_FNAME_CHAR; } @@ -280,7 +280,7 @@ int buf_init_chartab(buf_T *buf, int global) } } } - chartab_initialized = TRUE; + chartab_initialized = true; return OK; } @@ -333,7 +333,8 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET { char_u *res; char_u *p; - int l, c; + int c; + size_t l; char_u hexbuf[11]; if (has_mbyte) { @@ -343,7 +344,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET p = s; while (*p != NUL) { - if ((l = (*mb_ptr2len)(p)) > 1) { + if ((l = (size_t)(*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); p += l; @@ -354,7 +355,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET len += STRLEN(hexbuf); } } else { - l = byte2cells(*p++); + l = (size_t)byte2cells(*p++); if (l > 0) { len += l; @@ -366,14 +367,14 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET } res = xmallocz(len); } else { - res = xmallocz(vim_strsize(s)); + res = xmallocz((size_t)vim_strsize(s)); } *res = NUL; p = s; while (*p != NUL) { - if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) { + if (has_mbyte && ((l = (size_t)(*mb_ptr2len)(p)) > 1)) { c = (*mb_ptr2char)(p); if (vim_isprintc(c)) { @@ -477,9 +478,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) i += (*mb_ptr2len)(STR_PTR(i)); } else { if (buf == NULL) { - GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i)); + GA_CHAR(i) = (char_u)TOLOWER_LOC(GA_CHAR(i)); } else { - buf[i] = TOLOWER_LOC(buf[i]); + buf[i] = (char_u)TOLOWER_LOC(buf[i]); } ++i; } @@ -493,7 +494,7 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) // Catch 22: chartab[] can't be initialized before the options are // initialized, and initializing options may cause transchar() to be called! -// When chartab_initialized == FALSE don't use chartab[]. +// When chartab_initialized == false don't use chartab[]. // Does NOT work for multi-byte characters, c must be <= 255. // Also doesn't work for the first byte of a multi-byte, "c" must be a // character! @@ -518,7 +519,7 @@ char_u* transchar(int c) if ((!chartab_initialized && (((c >= ' ') && (c <= '~')) || F_ischar(c))) || ((c < 256) && vim_isprintc_strict(c))) { // printable character - transchar_buf[i] = c; + transchar_buf[i] = (char_u)c; transchar_buf[i + 1] = NUL; } else { transchar_nonprint(transchar_buf + i, c); @@ -564,7 +565,7 @@ void transchar_nonprint(char_u *buf, int c) // 0x00 - 0x1f and 0x7f buf[0] = '^'; // DEL displayed as ^? - buf[1] = c ^ 0x40; + buf[1] = (char_u)(c ^ 0x40); buf[2] = NUL; } else if (enc_utf8 && (c >= 0x80)) { @@ -572,12 +573,12 @@ void transchar_nonprint(char_u *buf, int c) } else if ((c >= ' ' + 0x80) && (c <= '~' + 0x80)) { // 0xa0 - 0xfe buf[0] = '|'; - buf[1] = c - 0x80; + buf[1] = (char_u)(c - 0x80); buf[2] = NUL; } else { // 0x80 - 0x9f and 0xff buf[0] = '~'; - buf[1] = (c - 0x80) ^ 0x40; + buf[1] = (char_u)((c - 0x80) ^ 0x40); buf[2] = NUL; } } @@ -592,11 +593,11 @@ void transchar_hex(char_u *buf, int c) buf[0] = '<'; if (c > 255) { - buf[++i] = nr2hex((unsigned)c >> 12); - buf[++i] = nr2hex((unsigned)c >> 8); + buf[++i] = (char_u)nr2hex((unsigned)c >> 12); + buf[++i] = (char_u)nr2hex((unsigned)c >> 8); } - buf[++i] = nr2hex((unsigned)c >> 4); - buf[++i] = nr2hex((unsigned)c); + buf[++i] = (char_u)(nr2hex((unsigned)c >> 4)); + buf[++i] = (char_u)(nr2hex((unsigned)c)); buf[++i] = '>'; buf[++i] = NUL; } @@ -734,9 +735,8 @@ int vim_strnsize(char_u *s, int len) /// @return Number of characters. #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \ - int ts; \ - ts = (buf)->b_p_ts; \ - return (int)(ts - (col % ts)); \ + const int ts = (int) (buf)->b_p_ts; \ + return (ts - (int)(col % ts)); \ } else { \ return ptr2cells(p); \ } @@ -1137,7 +1137,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) int n; if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) { - n = wp->w_buffer->b_p_ts; + n = (int)wp->w_buffer->b_p_ts; return n - (col % n); } n = ptr2cells(s); @@ -1205,11 +1205,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, char_u *line; // start of the line int incr; int head; - int ts = wp->w_buffer->b_p_ts; + int ts = (int)wp->w_buffer->b_p_ts; int c; vcol = 0; - line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col == MAXCOL) { // continue until the NUL @@ -1329,7 +1329,7 @@ colnr_T getvcol_nolist(pos_T *posp) int list_save = curwin->w_p_list; colnr_T vcol; - curwin->w_p_list = FALSE; + curwin->w_p_list = false; getvcol(curwin, posp, NULL, &vcol, NULL); curwin->w_p_list = list_save; return vcol; @@ -1358,7 +1358,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, endadd = 0; // Cannot put the cursor on part of a wide character. - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col < (colnr_T)STRLEN(ptr)) { int c = (*mb_ptr2char)(ptr + pos->col); @@ -1595,7 +1595,7 @@ bool vim_islower(int c) if (c >= 0x100) { if (has_mbyte) { - return iswlower(c); + return iswlower((wint_t)c); } // islower() can't handle these chars and may crash @@ -1626,7 +1626,7 @@ bool vim_isupper(int c) if (c >= 0x100) { if (has_mbyte) { - return iswupper(c); + return iswupper((wint_t)c); } // isupper() can't handle these chars and may crash @@ -1653,7 +1653,7 @@ int vim_toupper(int c) if (c >= 0x100) { if (has_mbyte) { - return towupper(c); + return (int)towupper((wint_t)c); } // toupper() can't handle these chars and may crash @@ -1680,7 +1680,7 @@ int vim_tolower(int c) if (c >= 0x100) { if (has_mbyte) { - return towlower(c); + return (int)towlower((wint_t)c); } // tolower() can't handle these chars and may crash diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0be8b3c514..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, (int)len, p_srr, tmp_diff); - block_autocmds(); /* Avoid ShellCmdPost stuff */ - (void)call_shell( - cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL - ); + append_redir(cmd, len, (char *) p_srr, tmp_diff); + block_autocmds(); // Avoid ShellCmdPost stuff + (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 6313ea2e81..667ce1e779 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -165,10 +165,6 @@ static int compl_restarting = FALSE; /* don't insert match */ * FALSE the word to be completed must be located. */ static int compl_started = FALSE; -/* Set when doing something for completion that may call edit() recursively, - * which is not allowed. */ -static int compl_busy = FALSE; - static int compl_matches = 0; static char_u *compl_pattern = NULL; static int compl_direction = FORWARD; @@ -203,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; @@ -274,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 @@ -887,7 +883,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_T: // Make indent one shiftwidth greater. if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { - if (has_compl_option(false)) { + if (check_compl_option(false)) { insert_do_complete(s); } break; @@ -1102,7 +1098,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_K: // digraph or keyword completion if (ctrl_x_mode == CTRL_X_DICTIONARY) { - if (has_compl_option(true)) { + if (check_compl_option(true)) { insert_do_complete(s); } break; @@ -1264,30 +1260,28 @@ static void insert_do_cindent(InsertState *s) } } -/* - * edit(): Start inserting text. - * - * "cmdchar" can be: - * 'i' normal insert command - * 'a' normal append command - * 'R' replace command - * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo, - * but still only one <CR> is inserted. The <Esc> is not used for redo. - * 'g' "gI" command. - * 'V' "gR" command for Virtual Replace mode. - * 'v' "gr" command for single character Virtual Replace mode. - * - * This function is not called recursively. For CTRL-O commands, it returns - * and lets the caller handle the Normal-mode command. - * - * Return TRUE if a CTRL-O command caused the return (insert mode pending). - */ -int -edit ( - int cmdchar, - int startln, /* if set, insert at start of line */ - long count -) +/// edit(): Start inserting text. +/// +/// "cmdchar" can be: +/// 'i' normal insert command +/// 'a' normal append command +/// 'R' replace command +/// 'r' "r<CR>" command: insert one <CR>. +/// Note: count can be > 1, for redo, but still only one <CR> is inserted. +/// <Esc> is not used for redo. +/// 'g' "gI" command. +/// 'V' "gR" command for Virtual Replace mode. +/// 'v' "gr" command for single character Virtual Replace mode. +/// +/// This function is not called recursively. For CTRL-O commands, it returns +/// and lets the caller handle the Normal-mode command. +/// +/// @param cmdchar command that started the insert +/// @param startln if true, insert at start of line +/// @param count repeat count for the command +/// +/// @return true if a CTRL-O command caused the return (insert mode pending). +bool edit(int cmdchar, bool startln, long count) { if (curbuf->terminal) { if (ex_normal_busy) { @@ -1795,33 +1789,37 @@ void backspace_until_column(int col) } } -/* - * Like del_char(), but make sure not to go before column "limit_col". - * Only matters when there are composing characters. - * Return TRUE when something was deleted. - */ -static int del_char_after_col(int limit_col) +/// Like del_char(), but make sure not to go before column "limit_col". +/// Only matters when there are composing characters. +/// +/// @param limit_col only delete the character if it is after this column +// +/// @return true when something was deleted. +static bool del_char_after_col(int limit_col) { if (enc_utf8 && limit_col >= 0) { colnr_T ecol = curwin->w_cursor.col + 1; - /* Make sure the cursor is at the start of a character, but - * skip forward again when going too far back because of a - * composing character. */ + // Make sure the cursor is at the start of a character, but + // skip forward again when going too far back because of a + // composing character. mb_adjust_cursor(); while (curwin->w_cursor.col < (colnr_T)limit_col) { int l = utf_ptr2len(get_cursor_pos_ptr()); - if (l == 0) /* end of line */ + if (l == 0) { // end of line break; + } curwin->w_cursor.col += l; } - if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) - return FALSE; - del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE); - } else - (void)del_char(FALSE); - return TRUE; + if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) { + return false; + } + del_bytes(ecol - curwin->w_cursor.col, false, true); + } else { + del_char(false); + } + return true; } /* @@ -1846,47 +1844,48 @@ static void ins_ctrl_x(void) } } -/* - * Return TRUE if the 'dict' or 'tsr' option can be used. - */ -static int has_compl_option(int dict_opt) +/// Check that the "dict" or "tsr" option can be used. +/// +/// @param dict_opt check "dict" when true, "tsr" when false. +static bool check_compl_option(bool dict_opt) { - if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL - && !curwin->w_p_spell - ) + if (dict_opt + ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) { ctrl_x_mode = 0; edit_submode = NULL; msg_attr(dict_opt ? (char_u *)_("'dictionary' option is empty") - : (char_u *)_("'thesaurus' option is empty"), - hl_attr(HLF_E)); + : (char_u *)_("'thesaurus' option is empty"), hl_attr(HLF_E)); if (emsg_silent == 0) { vim_beep(BO_COMPL); setcursor(); ui_flush(); os_delay(2000L, false); } - return FALSE; + return false; } - return TRUE; + return true; } -/* - * Is the character 'c' a valid key to go to or keep us in CTRL-X mode? - * This depends on the current mode. - */ -int vim_is_ctrl_x_key(int c) +/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode? +/// This depends on the current mode. +/// +/// @param c character to check +bool vim_is_ctrl_x_key(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { - /* Always allow ^R - let it's results then be checked */ - if (c == Ctrl_R) - return TRUE; + // Always allow ^R - let its results then be checked + if (c == Ctrl_R) { + return true; + } - /* Accept <PageUp> and <PageDown> if the popup menu is visible. */ - if (ins_compl_pum_key(c)) - return TRUE; + // Accept <PageUp> and <PageDown> if the popup menu is visible. + if (ins_compl_pum_key(c)) { + return true; + } switch (ctrl_x_mode) { - case 0: /* Not in any CTRL-X mode */ + case 0: // Not in any CTRL-X mode return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; case CTRL_X_NOT_DEFINED_YET: return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E @@ -1924,35 +1923,37 @@ int vim_is_ctrl_x_key(int c) return (c == Ctrl_P || c == Ctrl_N); } EMSG(_(e_internal)); - return FALSE; + return false; } -/* - * Return TRUE when character "c" is part of the item currently being - * completed. Used to decide whether to abandon complete mode when the menu - * is visible. - */ -static int ins_compl_accept_char(int c) +/// Check that character "c" is part of the item currently being +/// completed. Used to decide whether to abandon complete mode when the menu +/// is visible. +/// +/// @param c character to check +static bool ins_compl_accept_char(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - if (ctrl_x_mode & CTRL_X_WANT_IDENT) - /* When expanding an identifier only accept identifier chars. */ + if (ctrl_x_mode & CTRL_X_WANT_IDENT) { + // When expanding an identifier only accept identifier chars. return vim_isIDc(c); + } switch (ctrl_x_mode) { case CTRL_X_FILES: - /* When expanding file name only accept file name chars. But not - * path separators, so that "proto/<Tab>" expands files in - * "proto", not "proto/" as a whole */ + // When expanding file name only accept file name chars. But not + // path separators, so that "proto/<Tab>" expands files in + // "proto", not "proto/" as a whole return vim_isfilec(c) && !vim_ispathsep(c); case CTRL_X_CMDLINE: case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ + // Command line and Omni completion can work with just about any + // printable character, but do stop at white space. return vim_isprintc(c) && !ascii_iswhite(c); case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ + // For while line completion a space can be part of the line. return vim_isprintc(c); } return vim_iswordc(c); @@ -2197,15 +2198,19 @@ ins_compl_add ( return OK; } -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ -static int ins_compl_equal(compl_T *match, char_u *str, int len) +/// Check that "str[len]" matches with "match->cp_str", considering +/// "match->cp_icase". +/// +/// @param match completion match +/// @param str character string to check +/// @param len lenth of "str" +static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; + if (match->cp_icase) { + return STRNICMP(match->cp_str, str, len) == 0; + } + return STRNCMP(match->cp_str, str, len) == 0; } /* @@ -2353,13 +2358,13 @@ void set_completion(colnr_T startcol, list_T *list) int save_w_wrow = curwin->w_wrow; compl_curr_match = compl_first_match; - if (compl_no_insert) { + if (compl_no_insert || compl_no_select) { ins_complete(K_DOWN, false); - } else { - ins_complete(Ctrl_N, false); if (compl_no_select) { - ins_complete(Ctrl_P, false); + ins_complete(K_UP, false); } + } else { + ins_complete(Ctrl_N, false); } // Lazily show the popup menu, unless we got interrupted. @@ -2403,44 +2408,33 @@ static void ins_compl_del_pum(void) } } -/* - * Return TRUE if the popup menu should be displayed. - */ -static int pum_wanted(void) +/// Check if the popup menu should be displayed. +static bool pum_wanted(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 - ) - return FALSE; - return TRUE; + // "completeopt" must contain "menu" or "menuone" + return vim_strchr(p_cot, 'm') != NULL; } -/* - * Return TRUE if there are two or more matches to be shown in the popup menu. - * One if 'completopt' contains "menuone". - */ -static int pum_enough_matches(void) +/// Check that there are two or more matches to be shown in the popup menu. +/// One if "completopt" contains "menuone". +static bool pum_enough_matches(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - compl_T *compl; - int i; - - /* Don't display the popup menu if there are no matches or there is only - * one (ignoring the original text). */ - compl = compl_first_match; - i = 0; + // Don't display the popup menu if there are no matches or there is only + // one (ignoring the original text). + compl_T *comp = compl_first_match; + int i = 0; do { - if (compl == NULL - || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) + if (comp == NULL || ((comp->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) { break; - compl = compl->cp_next; - } while (compl != compl_first_match); + } + comp = comp->cp_next; + } while (comp != compl_first_match); - if (strstr((char *)p_cot, "menuone") != NULL) + if (strstr((char *)p_cot, "menuone") != NULL) { return i >= 1; + } return i >= 2; } @@ -2871,10 +2865,9 @@ static void ins_compl_clear(void) set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); } -/* - * Return TRUE when Insert completion is active. - */ -int ins_compl_active(void) +/// Check that Insert completion is active. +bool ins_compl_active(void) + FUNC_ATTR_PURE { return compl_started; } @@ -2918,14 +2911,13 @@ static int ins_compl_bs(void) return NUL; } -/* - * Return TRUE when we need to find matches again, ins_compl_restart() is to - * be called. - */ -static int ins_compl_need_restart(void) +/// Check that we need to find matches again, ins_compl_restart() is to +/// be called. +static bool ins_compl_need_restart(void) + FUNC_ATTR_PURE { - /* Return TRUE if we didn't complete finding matches or when the - * 'completefunc' returned "always" in the "refresh" dictionary item. */ + // Return true if we didn't complete finding matches or when the + // "completefunc" returned "always" in the "refresh" dictionary item. return compl_was_interrupted || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) && compl_opt_refresh_always); @@ -3073,16 +3065,16 @@ static void ins_compl_addfrommatch(void) ins_compl_addleader(c); } -/* - * Prepare for Insert mode completion, or stop it. - * Called just after typing a character in Insert mode. - * Returns TRUE when the character is not to be inserted; - */ -static int ins_compl_prep(int c) +/// Prepare for Insert mode completion, or stop it. +/// Called just after typing a character in Insert mode. +/// +/// @param c character that was typed +/// +/// @return true when the character is not to be inserted; +static bool ins_compl_prep(int c) { - char_u *ptr; - int want_cindent; - int retval = FALSE; + char_u *ptr; + bool retval = false; /* Forget any previous 'special' messages if this is actually * a ^X mode key - bar ^R, in which case we wait to see what it gives us. @@ -3092,8 +3084,10 @@ static int ins_compl_prep(int c) /* Ignore end of Select mode mapping and mouse scroll buttons. */ if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT) + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT + || c == K_FOCUSGAINED || c == K_FOCUSLOST) { return retval; + } /* Set "compl_get_longest" when finding the first matches. */ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET @@ -3247,11 +3241,9 @@ static int ins_compl_prep(int c) ins_compl_fixRedoBufForLeader(ptr); } - want_cindent = (can_cindent && cindent_on()); - /* - * When completing whole lines: fix indent for 'cindent'. - * Otherwise, break line if it's too long. - */ + bool want_cindent = (can_cindent && cindent_on()); + // When completing whole lines: fix indent for 'cindent'. + // Otherwise, break line if it's too long. if (compl_cont_mode == CTRL_X_WHOLE_LINE) { /* re-indent the current line */ if (want_cindent) { @@ -3271,22 +3263,24 @@ static int ins_compl_prep(int c) inc_cursor(); } - /* If the popup menu is displayed pressing CTRL-Y means accepting - * the selection without inserting anything. When - * compl_enter_selects is set the Enter key does the same. */ + // If the popup menu is displayed pressing CTRL-Y means accepting + // the selection without inserting anything. When + // compl_enter_selects is set the Enter key does the same. if ((c == Ctrl_Y || (compl_enter_selects && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) - retval = TRUE; + && pum_visible()) { + retval = true; + } /* CTRL-E means completion is Ended, go back to the typed text. */ if (c == Ctrl_E) { ins_compl_delete(); - if (compl_leader != NULL) + if (compl_leader != NULL) { ins_bytes(compl_leader + ins_compl_len()); - else if (compl_first_match != NULL) + } else if (compl_first_match != NULL) { ins_bytes(compl_orig_text + ins_compl_len()); - retval = TRUE; + } + retval = true; } auto_format(FALSE, TRUE); @@ -4245,22 +4239,22 @@ void ins_compl_check_keys(int frequency) static int ins_compl_key2dir(int c) { if (c == Ctrl_P || c == Ctrl_L - || (pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP))) + || c == K_PAGEUP || c == K_KPAGEUP + || c == K_S_UP || c == K_UP) { return BACKWARD; + } return FORWARD; } -/* - * Return TRUE for keys that are used for completion only when the popup menu - * is visible. - */ -static int ins_compl_pum_key(int c) +/// Check that "c" is a valid completion key only while the popup menu is shown +/// +/// @param c character to check +static bool ins_compl_pum_key(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP - || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == - K_S_DOWN - || c == K_UP || c == K_DOWN); + || c == K_PAGEDOWN || c == K_KPAGEDOWN + || c == K_S_DOWN || c == K_UP || c == K_DOWN); } /* @@ -4280,11 +4274,12 @@ static int ins_compl_key2count(int c) return 1; } -/* - * Return TRUE if completion with "c" should insert the match, FALSE if only - * to change the currently selected completion. - */ -static int ins_compl_use_match(int c) +/// Check that completion with "c" should insert the match, false if only +/// to change the currently selected completion. +/// +/// @param c character to check +static bool ins_compl_use_match(int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { switch (c) { case K_UP: @@ -4295,9 +4290,9 @@ static int ins_compl_use_match(int c) case K_PAGEUP: case K_KPAGEUP: case K_S_UP: - return FALSE; + return false; } - return TRUE; + return true; } /* @@ -6313,18 +6308,22 @@ char_u *get_last_insert_save(void) return s; } -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text and - * the replacement string is inserted in typebuf.tb_buf[], followed by "c". - */ -static int echeck_abbr(int c) +/// Check the word in front of the cursor for an abbreviation. +/// Called when the non-id character "c" has been entered. +/// When an abbreviation is recognized it is removed from the text and +/// the replacement string is inserted in typebuf.tb_buf[], followed by "c". +/// +/// @param c character +/// +/// @return true if the word is a known abbreviation. +static bool echeck_abbr(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* Don't check for abbreviation in paste mode, when disabled and just - * after moving around with cursor keys. */ - if (p_paste || no_abbr || arrow_used) - return FALSE; + // Don't check for abbreviation in paste mode, when disabled and just + // after moving around with cursor keys. + if (p_paste || no_abbr || arrow_used) { + return false; + } return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col, curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); @@ -6560,13 +6559,11 @@ static void replace_do_bs(int limit_col) (void)del_char_after_col(limit_col); } -/* - * Return TRUE if C-indenting is on. - */ -static int cindent_on(void) { - return !p_paste && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ); +/// Check that C-indenting is on. +static bool cindent_on(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL); } /* @@ -6596,32 +6593,33 @@ void fix_indent(void) { do_c_expr_indent(); } -/* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ -int in_cinkeys(int keytyped, int when, int line_is_empty) +/// Check that "cinkeys" contains the key "keytyped", +/// when == '*': Only if key is preceded with '*' (indent before insert) +/// when == '!': Only if key is preceded with '!' (don't insert) +/// when == ' ': Only if key is not preceded with '*' (indent afterwards) +/// +/// "keytyped" can have a few special values: +/// KEY_OPEN_FORW : +/// KEY_OPEN_BACK : +/// KEY_COMPLETE : Just finished completion. +/// +/// @param keytyped key that was typed +/// @param when condition on when to perform the check +/// @param line_is_empty when true, accept keys with '0' before them. +bool in_cinkeys(int keytyped, int when, bool line_is_empty) { - char_u *look; + char_u *look; int try_match; int try_match_word; - char_u *p; - char_u *line; + char_u *p; + char_u *line; int icase; int i; - if (keytyped == NUL) - /* Can happen with CTRL-Y and CTRL-E on a short line. */ - return FALSE; + if (keytyped == NUL) { + // Can happen with CTRL-Y and CTRL-E on a short line. + return false; + } if (*curbuf->b_p_inde != NUL) look = curbuf->b_p_indk; /* 'indentexpr' set: use 'indentkeys' */ @@ -6637,68 +6635,64 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) case '!': try_match = (*look == '!'); break; default: try_match = (*look != '*'); break; } - if (*look == '*' || *look == '!') - ++look; + if (*look == '*' || *look == '!') { + look++; + } - /* - * If there is a '0', only accept a match if the line is empty. - * But may still match when typing last char of a word. - */ + // If there is a '0', only accept a match if the line is empty. + // But may still match when typing last char of a word. if (*look == '0') { try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } else - try_match_word = FALSE; + if (!line_is_empty) { + try_match = false; + } + look++; + } else { + try_match_word = false; + } - /* - * does it look like a control character? - */ - if (*look == '^' - && look[1] >= '?' && look[1] <= '_' - ) { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; + // Does it look like a control character? + if (*look == '^' && look[1] >= '?' && look[1] <= '_') { + if (try_match && keytyped == Ctrl_chr(look[1])) { + return true; + } look += 2; - } - /* - * 'o' means "o" command, open forward. - * 'O' means "O" command, open backward. - */ - else if (*look == 'o') { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; + + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. + } else if (*look == 'o') { + if (try_match && keytyped == KEY_OPEN_FORW) { + return true; + } + look++; } else if (*look == 'O') { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - /* - * 'e' means to check for "else" at start of line and just before the - * cursor. - */ - else if (*look == 'e') { + if (try_match && keytyped == KEY_OPEN_BACK) { + return true; + } + look++; + + // 'e' means to check for "else" at start of line and just before the + // cursor. + } else if (*look == 'e') { if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { p = get_cursor_line_ptr(); if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; + STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) { + return true; + } } - ++look; - } - /* - * ':' only causes an indent if it is at the end of a label or case - * statement, or when it was before typing the ':' (to fix - * class::method for C++). - */ - else if (*look == ':') { + look++; + + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). + } else if (*look == ':') { if (try_match && keytyped == ':') { p = get_cursor_line_ptr(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - /* Need to get the line again after cin_islabel(). */ + if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) { + return true; + } + // Need to get the line again after cin_islabel(). p = get_cursor_line_ptr(); if (curwin->w_cursor.col > 2 && p[curwin->w_cursor.col - 1] == ':' @@ -6708,28 +6702,27 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) || cin_islabel()); p = get_cursor_line_ptr(); p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; + if (i) { + return true; + } } } - ++look; - } - /* - * Is it a key in <>, maybe? - */ - else if (*look == '<') { + look++; + + // Is it a key in <>, maybe? + } else if (*look == '<') { if (try_match) { - /* - * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, - * <:> and <!> so that people can re-indent on o, O, e, 0, <, - * >, *, : and ! keys if they really really want to. - */ + // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, + // <:> and <!> so that people can re-indent on o, O, e, 0, <, + // >, *, : and ! keys if they really really want to. if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; + && keytyped == look[1]) { + return true; + } - if (keytyped == get_special_key_code(look + 1)) - return TRUE; + if (keytyped == get_special_key_code(look + 1)) { + return true; + } } while (*look && *look != '>') look++; @@ -6800,18 +6793,18 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) (int)(curwin->w_cursor.col - (p - look))) match = FALSE; } - if (match) - return TRUE; + if (match) { + return true; + } } look = p; - } - /* - * ok, it's a boring generic character. - */ - else { - if (try_match && *look == keytyped) - return TRUE; - ++look; + + // Ok, it's a boring generic character. + } else { + if (try_match && *look == keytyped) { + return true; + } + look++; } /* @@ -6819,7 +6812,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) */ look = skip_to_option_part(look); } - return FALSE; + return false; } /* @@ -7046,27 +7039,24 @@ static void ins_ctrl_hat(void) status_redraw_curbuf(); } -/* - * Handle ESC in insert mode. - * Returns TRUE when leaving insert mode, FALSE when going to repeat the - * insert. - */ -static int -ins_esc ( - long *count, - int cmdchar, - int nomove /* don't move cursor */ -) +/// Handle ESC in insert mode. +/// +/// @param[in,out] count repeat count of the insert command +/// @param cmdchar command that started the insert +/// @param nomove when true, don't move the cursor +/// +/// @return true when leaving insert mode, false when repeating the insert. +static bool ins_esc(long *count, int cmdchar, bool nomove) + FUNC_ATTR_NONNULL_ARG(1) { - int temp; - static int disabled_redraw = FALSE; + static bool disabled_redraw = false; check_spell_redraw(); - temp = curwin->w_cursor.col; + int temp = curwin->w_cursor.col; if (disabled_redraw) { - --RedrawingDisabled; - disabled_redraw = FALSE; + RedrawingDisabled--; + disabled_redraw = false; } if (!arrow_used) { /* @@ -7096,9 +7086,10 @@ ins_esc ( if (cmdchar == 'r' || cmdchar == 'v') { stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer } - ++RedrawingDisabled; - disabled_redraw = TRUE; - return FALSE; /* repeat the insert */ + RedrawingDisabled++; + disabled_redraw = true; + // Repeat the insert + return false; } stop_insert(&curwin->w_cursor, TRUE, nomove); undisplay_dollar(); @@ -7148,16 +7139,15 @@ ins_esc ( setmouse(); ui_cursor_shape(); /* may show different cursor shape */ - /* - * When recording or for CTRL-O, need to display the new mode. - * Otherwise remove the mode message. - */ - if (Recording || restart_edit != NUL) + // When recording or for CTRL-O, need to display the new mode. + // Otherwise remove the mode message. + if (Recording || restart_edit != NUL) { showmode(); - else if (p_smd) + } else if (p_smd) { MSG(""); - - return TRUE; /* exit Insert mode */ + } + // Exit Insert mode + return true; } /* @@ -7195,14 +7185,16 @@ static void ins_ctrl_(void) showmode(); } -/* - * If 'keymodel' contains "startsel", may start selection. - * Returns TRUE when a CTRL-O and other keys stuffed. - */ -static int ins_start_select(int c) +/// If 'keymodel' contains "startsel", may start selection. +/// +/// @param c character to check +// +/// @return true when a CTRL-O and other keys stuffed. +static bool ins_start_select(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { if (!km_startsel) { - return FALSE; + return false; } switch (c) { case K_KHOME: @@ -7213,32 +7205,27 @@ static int ins_start_select(int c) case K_KPAGEDOWN: if (!(mod_mask & MOD_MASK_SHIFT)) break; - /* FALLTHROUGH */ + // FALLTHROUGH case K_S_LEFT: case K_S_RIGHT: case K_S_UP: case K_S_DOWN: case K_S_END: case K_S_HOME: - /* Start selection right away, the cursor can move with - * CTRL-O when beyond the end of the line. */ + // Start selection right away, the cursor can move with + // CTRL-O when beyond the end of the line. start_selection(); - /* Execute the key in (insert) Select mode. */ + // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4]; - - buf[0] = K_SPECIAL; - buf[1] = KS_MODIFIER; - buf[2] = mod_mask; - buf[3] = NUL; + char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); - return TRUE; + return true; } - return FALSE; + return false; } /* @@ -7252,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 */ @@ -7362,18 +7349,23 @@ static void ins_bs_one(colnr_T *vcolp) (void)del_char(FALSE); } -/* - * Handle Backspace, delete-word and delete-line in Insert mode. - * Return TRUE when backspace was actually used. - */ -static int ins_bs(int c, int mode, int *inserted_space_p) +/// Handle Backspace, delete-word and delete-line in Insert mode. +/// +/// @param c charcter that was typed +/// @param mode backspace mode to use +/// @param[in,out] inserted_space_p whether a space was the last +// character inserted +/// +/// @return true when backspace was actually used. +static bool ins_bs(int c, int mode, int *inserted_space_p) + FUNC_ATTR_NONNULL_ARG(3) { linenr_T lnum; int cc; int temp = 0; /* init for GCC */ colnr_T save_col; colnr_T mincol; - int did_backspace = FALSE; + bool did_backspace = false; int in_indent; int oldState; int cpc[MAX_MCO]; /* composing characters */ @@ -7399,28 +7391,29 @@ static int ins_bs(int c, int mode, int *inserted_space_p) return false; } - if (stop_arrow() == FAIL) - return FALSE; + if (stop_arrow() == FAIL) { + return false; + } in_indent = inindent(0); - if (in_indent) - can_cindent = FALSE; - end_comment_pending = NUL; /* After BS, don't auto-end comment */ - if (revins_on) /* put cursor after last inserted char */ + if (in_indent) { + can_cindent = false; + } + end_comment_pending = NUL; // After BS, don't auto-end comment + if (revins_on) { // put cursor after last inserted char inc_cursor(); - - /* Virtualedit: - * BACKSPACE_CHAR eats a virtual space - * BACKSPACE_WORD eats all coladd - * BACKSPACE_LINE eats all coladd and keeps going - */ + } + // Virtualedit: + // BACKSPACE_CHAR eats a virtual space + // BACKSPACE_WORD eats all coladd + // BACKSPACE_LINE eats all coladd and keeps going if (curwin->w_cursor.coladd > 0) { if (mode == BACKSPACE_CHAR) { - --curwin->w_cursor.coladd; - return TRUE; + curwin->w_cursor.coladd--; + return true; } if (mode == BACKSPACE_WORD) { curwin->w_cursor.coladd = 0; - return TRUE; + return true; } curwin->w_cursor.coladd = 0; } @@ -7432,10 +7425,10 @@ static int ins_bs(int c, int mode, int *inserted_space_p) lnum = Insstart.lnum; if (curwin->w_cursor.lnum == lnum || revins_on) { if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), - (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { - return FALSE; + (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { + return false; } - --Insstart.lnum; + Insstart.lnum--; Insstart.col = MAXCOL; } /* @@ -7677,12 +7670,12 @@ static int ins_bs(int c, int mode, int *inserted_space_p) if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) dollar_vcol = curwin->w_virtcol; - /* When deleting a char the cursor line must never be in a closed fold. - * E.g., when 'foldmethod' is indent and deleting the first non-white - * char before a Tab. */ - if (did_backspace) + // When deleting a char the cursor line must never be in a closed fold. + // E.g., when 'foldmethod' is indent and deleting the first non-white + // char before a Tab. + if (did_backspace) { foldOpenCursor(); - + } return did_backspace; } @@ -7748,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; } @@ -8001,35 +7996,37 @@ static void ins_pagedown(void) } } -/* - * Handle TAB in Insert or Replace mode. - * Return TRUE when the TAB needs to be inserted like a normal character. - */ -static int ins_tab(void) +/// Handle TAB in Insert or Replace mode. +/// +/// @return true when the TAB needs to be inserted like a normal character. +static bool ins_tab(void) + FUNC_ATTR_WARN_UNUSED_RESULT { - int ind; int i; int temp; - if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) + if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) { Insstart_blank_vcol = get_nolist_virtcol(); - if (echeck_abbr(TAB + ABBR_OFF)) - return FALSE; + } + if (echeck_abbr(TAB + ABBR_OFF)) { + return false; + } - ind = inindent(0); - if (ind) - can_cindent = FALSE; + int ind = inindent(0); + if (ind) { + can_cindent = false; + } - /* - * When nothing special, insert TAB like a normal character - */ + // When nothing special, insert TAB like a normal character if (!curbuf->b_p_et && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf)) - && get_sts_value() == 0) - return TRUE; + && get_sts_value() == 0) { + return true; + } - if (stop_arrow() == FAIL) - return TRUE; + if (stop_arrow() == FAIL) { + return true; + } did_ai = FALSE; did_si = FALSE; @@ -8037,12 +8034,13 @@ static int ins_tab(void) can_si_back = FALSE; AppendToRedobuff((char_u *)"\t"); - if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */ + if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); - else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */ + } else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set temp = get_sts_value(); - else /* otherwise use 'tabstop' */ + } else { // otherwise use "tabstop" temp = (int)curbuf->b_p_ts; + } temp -= get_nolist_virtcol() % temp; /* @@ -8182,21 +8180,20 @@ static int ins_tab(void) curwin->w_p_list = save_list; } - return FALSE; + return false; } -/* - * Handle CR or NL in insert mode. - * Return TRUE when it can't undo. - */ -static int ins_eol(int c) +/// Handle CR or NL in insert mode. +/// +/// @return true when it can't undo. +static bool ins_eol(int c) { - int i; - - if (echeck_abbr(c + ABBR_OFF)) - return FALSE; - if (stop_arrow() == FAIL) - return TRUE; + if (echeck_abbr(c + ABBR_OFF)) { + return false; + } + if (stop_arrow() == FAIL) { + return true; + } undisplay_dollar(); /* @@ -8229,9 +8226,9 @@ static int ins_eol(int c) curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr()); AppendToRedobuff(NL_STR); - i = open_line(FORWARD, - has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : - 0, old_indent); + bool i = open_line(FORWARD, + has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, + old_indent); old_indent = 0; can_cindent = TRUE; /* When inserting a line the cursor line must never be in a closed fold. */ @@ -8485,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)) { @@ -8511,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 b2370e7047..a68d09575b 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); @@ -160,6 +153,7 @@ static char *e_listdictarg = N_( static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); +static char *e_strreq = N_("E114: String required"); static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); static char *e_funcexts = N_( @@ -173,6 +167,7 @@ static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_float_as_string = N_("E806: using Float as a String"); static char_u * const empty_string = (char_u *)""; +static char_u * const namespace_char = (char_u *)"abglstvw"; static dictitem_T globvars_var; /* variable used for g: */ #define globvarht globvardict.dv_hashtab @@ -183,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. @@ -296,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 @@ -417,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; @@ -458,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", @@ -480,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, @@ -497,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); @@ -507,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) @@ -536,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); @@ -553,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) @@ -561,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); @@ -796,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; @@ -847,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; @@ -1731,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 *)"", @@ -2415,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: @@ -2487,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); } } @@ -3124,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; } @@ -3202,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; @@ -3264,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; @@ -4156,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; @@ -4345,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); @@ -4543,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 } } @@ -5087,10 +5110,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; } /* @@ -5491,9 +5522,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; @@ -5501,9 +5533,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 @@ -5555,6 +5584,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. * @@ -5590,8 +5635,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. @@ -5936,6 +5980,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; } @@ -6527,596 +6580,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 +/// Convert the string to a floating point number /// -/// 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. -/// -/// @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. @@ -7176,7 +6655,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 }, @@ -7193,9 +6672,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 }, @@ -7222,7 +6701,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 }, @@ -7239,7 +6718,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 }, @@ -7269,7 +6748,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 }, @@ -7293,7 +6772,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 @@ -7326,6 +6805,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 }, @@ -8100,19 +7581,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); @@ -8147,16 +7628,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); @@ -8878,25 +8364,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 @@ -9073,7 +8585,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: @@ -9095,9 +8607,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; @@ -10269,22 +9784,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); } @@ -10607,9 +10243,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); @@ -11103,12 +10740,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; + } } /* @@ -12116,6 +11839,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 */ @@ -12157,7 +11921,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; } @@ -12480,9 +12247,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); @@ -12880,289 +12648,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 @@ -13176,7 +12661,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]; @@ -13184,307 +12669,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 @@ -13502,7 +12693,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)); @@ -13516,7 +12707,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"); @@ -15291,43 +14482,51 @@ static void f_setline(typval_T *argvars, typval_T *rettv) /// replace its content or create a new one. /// @param[in] title_arg New list title. Defaults to caller function name. /// @param[out] rettv Return value: 0 in case of success, -1 otherwise. -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, - typval_T *title_arg, typval_T *rettv) - FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3) { - char_u *act; - int action = ' '; char_u *title = NULL; - + int action = ' '; rettv->vval.v_number = -1; - if (list_arg->v_type != VAR_LIST) + typval_T *list_arg = &args[0]; + if (list_arg->v_type != VAR_LIST) { EMSG(_(e_listreq)); - else { - list_T *l = list_arg->vval.v_list; + return; + } - if (action_arg->v_type != VAR_UNKNOWN) { - act = get_tv_string_chk(action_arg); - if (act == NULL) - return; /* type error; errmsg already given */ - if (*act == 'a' || *act == 'r') - action = *act; - } + typval_T *action_arg = &args[1]; + if (action_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } else if (action_arg->v_type != VAR_STRING) { + EMSG(_(e_strreq)); + return; + } + char_u *act = get_tv_string_chk(action_arg); + if (*act == 'a' || *act == 'r') { + action = *act; + } - if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); - if (!title) { - return; // type error; errmsg already given - } - } + typval_T *title_arg = &args[2]; + if (title_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } + title = get_tv_string_chk(title_arg); + if (!title) { + // Type error. Error already printed by get_tv_string_chk(). + return; + } - if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); - } +skip_args: + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } - if (l && set_errorlist(wp, l, action, title) == OK) { - rettv->vval.v_number = 0; - } + list_T *l = list_arg->vval.v_list; + if (l && set_errorlist(wp, l, action, title) == OK) { + rettv->vval.v_number = 0; } } @@ -15342,7 +14541,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) win = find_win_by_nr(&argvars[0], NULL); if (win != NULL) { - set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); + set_qf_ll_list(win, &argvars[1], rettv); } } @@ -15479,7 +14678,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv); + set_qf_ll_list(NULL, argvars, rettv); } /* @@ -15786,9 +14985,9 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } - // 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. + // 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 +14995,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 +15004,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 *)""; @@ -16249,7 +15448,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; } @@ -16380,7 +15579,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); } /* @@ -17113,9 +16312,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; @@ -17301,16 +16500,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; } @@ -17889,21 +17105,28 @@ static int get_env_len(char_u **arg) return len; } -/* - * Get the length of the name of a function or internal variable. - * "arg" is advanced to the first non-white character after the name. - * Return 0 if something is wrong. - */ -static int get_id_len(char_u **arg) -{ - char_u *p; +// Get the length of the name of a function or internal variable. +// "arg" is advanced to the first non-white character after the name. +// Return 0 if something is wrong. +static int get_id_len(char_u **arg) { + char_u *p; int len; - /* Find the end of the name. */ - for (p = *arg; eval_isnamec(*p); ++p) - ; - if (p == *arg) /* no name found */ + // Find the end of the name. + for (p = *arg; eval_isnamec(*p); p++) { + if (*p == ':') { + // "s:" is start of "s:var", but "n:" is not and can be used in + // slice "[n:]". Also "xx:" is not a namespace. + len = (int)(p - *arg); + if (len > 1 + || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) { + break; + } + } + } + if (p == *arg) { // no name found return 0; + } len = (int)(p - *arg); *arg = skipwhite(p); @@ -17974,28 +17197,29 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose) return len; } -/* - * Find the end of a variable or function name, taking care of magic braces. - * If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the - * start and end of the first magic braces item. - * "flags" can have FNE_INCL_BR and FNE_CHECK_START. - * Return a pointer to just after the name. Equal to "arg" if there is no - * valid name. - */ -static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags) +// Find the end of a variable or function name, taking care of magic braces. +// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the +// start and end of the first magic braces item. +// "flags" can have FNE_INCL_BR and FNE_CHECK_START. +// Return a pointer to just after the name. Equal to "arg" if there is no +// valid name. +static char_u *find_name_end(char_u *arg, char_u **expr_start, + char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; + char_u *p; + int len; if (expr_start != NULL) { *expr_start = NULL; *expr_end = NULL; } - /* Quick check for valid starting character. */ - if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') + // Quick check for valid starting character. + if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') { return arg; + } for (p = arg; *p != NUL && (eval_isnamec(*p) @@ -18010,30 +17234,44 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end if (*p == NUL) break; } else if (*p == '"') { - /* skip over "str\"ing" to avoid counting [ and ] inside it. */ - for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) - if (*p == '\\' && p[1] != NUL) + // skip over "str\"ing" to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) { + if (*p == '\\' && p[1] != NUL) { ++p; - if (*p == NUL) + } + } + if (*p == NUL) { + break; + } + } 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. But {ns}: is. */ + len = (int)(p - arg); + if ((len > 1 && p[-1] != '}') + || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) { break; + } } if (mb_nest == 0) { - if (*p == '[') + if (*p == '[') { ++br_nest; - else if (*p == ']') + } else if (*p == ']') { --br_nest; + } } if (br_nest == 0) { if (*p == '{') { mb_nest++; - if (expr_start != NULL && *expr_start == NULL) + if (expr_start != NULL && *expr_start == NULL) { *expr_start = p; + } } else if (*p == '}') { mb_nest--; - if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) + if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) { *expr_end = p; + } } } } @@ -18115,14 +17353,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 @@ -18159,11 +17389,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; } @@ -18182,47 +17412,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); } @@ -18233,15 +17484,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); + } } /* @@ -18471,25 +17724,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); } @@ -18527,10 +17778,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; } @@ -18585,8 +17837,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) { @@ -18718,8 +17981,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; @@ -18990,7 +18256,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); @@ -19267,42 +18533,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; } } @@ -19342,6 +18600,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: @@ -19388,8 +18647,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; @@ -19444,7 +18703,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) { @@ -21058,15 +20317,22 @@ call_user_func ( save_sourcing_name = sourcing_name; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 1; - // need space for function name + ("function " + 3) or "[number]" + // need space for new sourcing_name: + // * save_sourcing_name + // * "["number"].." or "function " + // * "<SNR>" + fp->uf_name - 3 + // * terminating NUL size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) - + STRLEN(fp->uf_name) + 20; + + STRLEN(fp->uf_name) + 27; sourcing_name = xmalloc(len); { if (save_sourcing_name != NULL && STRNCMP(save_sourcing_name, "function ", 9) == 0) { - vim_snprintf((char *)sourcing_name, len, "%s[%zu]..", - save_sourcing_name, save_sourcing_lnum); + vim_snprintf((char *)sourcing_name, + len, + "%s[%" PRId64 "]..", + save_sourcing_name, + (int64_t)save_sourcing_lnum); } else { STRCPY(sourcing_name, "function "); } @@ -21089,10 +20355,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); @@ -21183,10 +20449,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); @@ -21457,7 +20723,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 *)""; @@ -22036,7 +21302,15 @@ repeat: } if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { + // vim_strsave_shellescape() needs a NUL terminated string. + c = (*fnamep)[*fnamelen]; + if (c != NUL) { + (*fnamep)[*fnamelen] = NUL; + } p = vim_strsave_shellescape(*fnamep, false, false); + if (c != NUL) { + (*fnamep)[*fnamelen] = c; + } xfree(*bufp); *bufp = *fnamep = p; *fnamelen = STRLEN(p); @@ -22332,7 +21606,7 @@ static void on_process_exit(Process *proc, int status, void *d) TerminalJobData *data = d; if (data->term && !data->exited) { data->exited = true; - char msg[22]; + char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); } 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 bfaa6d8e2a..ecc6e2b7e8 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -51,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" @@ -1358,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 @@ -1374,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; @@ -1394,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, (int)len, p_srr, otmp); - - return buf; + if (otmp != NULL) { + append_redir(buf, len, (char *) p_srr, (char *) otmp); + } + 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, int 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) @@ -1538,8 +1540,11 @@ void ex_file(exarg_T *eap) if (rename_buffer(eap->arg) == FAIL) return; } - /* print full file name if :cd used */ - fileinfo(FALSE, FALSE, eap->forceit); + + if (!shortmess(SHM_FILEINFO)) { + // print full file name if :cd used + fileinfo(false, false, eap->forceit); + } } /* @@ -2122,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; @@ -2515,7 +2518,9 @@ do_ecmd ( msg_scroll = msg_scroll_save; msg_scrolled_ign = TRUE; - fileinfo(FALSE, TRUE, FALSE); + if (!shortmess(SHM_FILEINFO)) { + fileinfo(false, true, false); + } msg_scrolled_ign = FALSE; } 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..2c8271c696 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3168,27 +3168,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 a7e98e7f04..2d316cb106 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; @@ -6814,36 +6817,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; @@ -6884,10 +6906,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) @@ -7421,10 +7458,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) { @@ -7435,32 +7472,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; @@ -7504,8 +7543,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) @@ -7550,15 +7590,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 bf67047ae8..41ad96a378 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -378,7 +378,7 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should char_u *p, *val; if (type == ET_ERROR) { - *should_free = FALSE; + *should_free = true; mesg = ((struct msglist *)value)->throw_msg; if (cmdname != NULL && *cmdname != NUL) { size_t cmdlen = STRLEN(cmdname); @@ -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); @@ -1370,19 +1374,24 @@ void ex_catch(exarg_T *eap) } save_cpo = p_cpo; p_cpo = (char_u *)""; + // Disable error messages, it will make current exception + // invalid + emsg_off++; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - regmatch.rm_ic = FALSE; - if (end != NULL) + emsg_off--; + regmatch.rm_ic = false; + if (end != NULL) { *end = save_char; + } p_cpo = save_cpo; - if (regmatch.regprog == NULL) + if (regmatch.regprog == NULL) { EMSG2(_(e_invarg2), pat); - else { - /* - * Save the value of got_int and reset it. We don't want - * a previous interruption cancel matching, only hitting - * CTRL-C while matching should abort it. - */ + } else { + // + // Save the value of got_int and reset it. We don't want + // a previous interruption cancel matching, only hitting + // CTRL-C while matching should abort it. + // prev_got_int = got_int; got_int = FALSE; caught = vim_regexec_nl(®match, current_exception->value, diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 39bff9b2ad..cffda1ca55 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1130,7 +1130,7 @@ static int command_line_handle_key(CommandLineState *s) if (!mouse_has(MOUSE_COMMAND)) { return command_line_not_changed(s); // Ignore mouse } - cmdline_paste(0, true, true); + cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -2424,20 +2424,17 @@ void restore_cmdline_alloc(char_u *p) xfree(p); } -/* - * paste a yank register into the command line. - * used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -static int -cmdline_paste ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* remove trailing CR */ -) +/// Paste a yank register into the command line. +/// Used by CTRL-R command in command-line mode. +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, remove trailing CR. +/// +/// @returns FAIL for failure, OK otherwise +static bool cmdline_paste(int regname, bool literally, bool remcr) { long i; char_u *arg; 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 6c135ef47b..7f46a37315 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -762,6 +762,10 @@ void clearFolding(win_T *win) */ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) { + if (compl_busy) { + return; + } + fold_T *fp; if (wp->w_buffer->terminal) { return; @@ -1695,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". */ @@ -1712,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) { @@ -2672,7 +2676,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 69e65c3208..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" @@ -214,6 +204,10 @@ EXTERN int compl_length INIT(= 0); * stop looking for matches. */ EXTERN int compl_interrupted INIT(= FALSE); +// Set when doing something for completion that may call edit() recursively, +// which is not allowed. Also used to disable folding during completion +EXTERN int compl_busy INIT(= false); + /* List of flags for method of completion. */ EXTERN int compl_cont_status INIT(= 0); # define CONT_ADDING 1 /* "normal" or "adding" expansion */ @@ -918,8 +912,8 @@ EXTERN int KeyTyped; // TRUE if user typed current char EXTERN int KeyStuffed; // TRUE if current char from stuffbuf EXTERN int maptick INIT(= 0); // tick for each non-mapped char -EXTERN char_u chartab[256]; /* table used in charset.c; See - init_chartab() for explanation */ +EXTERN uint8_t chartab[256]; // table used in charset.c; See + // init_chartab() for explanation EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */ 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 7169a1d963..daddd7cee4 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" @@ -2081,12 +2080,13 @@ static int cs_show(exarg_T *eap) if (csinfo[i].fname == NULL) continue; - if (csinfo[i].ppath != NULL) - (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", - i, (long)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath); - else - (void)smsg("%2zu %-5" PRId64 " %-34s <none>", - i, (long)csinfo[i].pid, csinfo[i].fname); + if (csinfo[i].ppath != NULL) { + (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i, + (int64_t)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath); + } else { + (void)smsg("%2zu %-5" PRId64 " %-34s <none>", i, + (int64_t)csinfo[i].pid, csinfo[i].fname); + } } } 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 5b5c8a22aa..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(); /* @@ -335,7 +335,7 @@ int main(int argc, char **argv) source_startup_scripts(¶ms); // If using the runtime (-u is not NONE), enable syntax & filetype plugins. - if (params.use_vimrc != NULL && strcmp(params.use_vimrc, "NONE") != 0) { + if (params.use_vimrc == NULL || strcmp(params.use_vimrc, "NONE") != 0) { // Does ":filetype plugin indent on". filetype_maybe_enable(); // Sources syntax/syntax.vim, which calls `:filetype on`. @@ -753,6 +753,7 @@ static void command_line_scan(mparm_T *parmp) putchar(b->data[i]); } + msgpack_packer_free(p); mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { parmp->headless = true; @@ -1140,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/mbyte.c b/src/nvim/mbyte.c index f0a249919f..3495203c43 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1304,35 +1304,38 @@ int utfc_ptr2char(const char_u *p, int *pcc) */ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen) { - int len; - int c; - int cc; +#define IS_COMPOSING(s1, s2, s3) \ + (i == 0 ? UTF_COMPOSINGLIKE((s1), (s2)) : utf_iscomposing((s3))) + + assert(maxlen > 0); + int i = 0; - c = utf_ptr2char(p); - len = utf_ptr2len_len(p, maxlen); - /* Only accept a composing char when the first char isn't illegal. */ - if ((len > 1 || *p < 0x80) - && len < maxlen - && p[len] >= 0x80 - && UTF_COMPOSINGLIKE(p, p + len)) { - cc = utf_ptr2char(p + len); - for (;; ) { - pcc[i++] = cc; - if (i == MAX_MCO) - break; - len += utf_ptr2len_len(p + len, maxlen - len); - if (len >= maxlen - || p[len] < 0x80 - || !utf_iscomposing(cc = utf_ptr2char(p + len))) + int len = utf_ptr2len_len(p, maxlen); + // Is it safe to use utf_ptr2char()? + bool safe = len > 1 && len <= maxlen; + int c = safe ? utf_ptr2char(p) : *p; + + // Only accept a composing char when the first char isn't illegal. + if ((safe || c < 0x80) && len < maxlen && p[len] >= 0x80) { + for (; i < MAX_MCO; i++) { + int len_cc = utf_ptr2len_len(p + len, maxlen - len); + safe = len_cc > 1 && len_cc <= maxlen - len; + if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80 + || !IS_COMPOSING(p, p + len, pcc[i])) { break; + } + len += len_cc; } } - if (i < MAX_MCO) /* last composing char must be 0 */ + if (i < MAX_MCO) { + // last composing char must be 0 pcc[i] = 0; + } return c; +#undef ISCOMPOSING } /* diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 8cf5642a80..6599db787f 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -79,8 +79,6 @@ static size_t total_mem_used = 0; /// total memory used for memfiles /// - NULL, on failure. memfile_T *mf_open(char_u *fname, int flags) { - off_t size; - memfile_T *mfp = xmalloc(sizeof(memfile_T)); if (fname == NULL) { // no file, use memory only @@ -88,11 +86,9 @@ memfile_T *mf_open(char_u *fname, int flags) mfp->mf_ffname = NULL; mfp->mf_fd = -1; } else { // try to open the file - mf_do_open(mfp, fname, flags); - - if (mfp->mf_fd < 0) { // fail if file could not be opened + if (!mf_do_open(mfp, fname, flags)) { xfree(mfp); - return NULL; + return NULL; // fail if file could not be opened } } @@ -115,6 +111,8 @@ memfile_T *mf_open(char_u *fname, int flags) } } + off_t size; + // When recovering, the actual block size will be retrieved from block 0 // in ml_recover(). The size used here may be wrong, therefore mf_blocknr_max // must be rounded up. @@ -171,13 +169,12 @@ memfile_T *mf_open(char_u *fname, int flags) /// FAIL If file could not be opened. int mf_open_file(memfile_T *mfp, char_u *fname) { - mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); // try to open the file - - if (mfp->mf_fd < 0) - return FAIL; + if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) { + mfp->mf_dirty = true; + return OK; + } - mfp->mf_dirty = true; - return OK; + return FAIL; } /// Close a memory file and optionally delete the associated file. @@ -185,28 +182,28 @@ int mf_open_file(memfile_T *mfp, char_u *fname) /// @param del_file Whether to delete associated file. void mf_close(memfile_T *mfp, bool del_file) { - bhdr_T *hp, *nextp; - if (mfp == NULL) { // safety check return; } if (mfp->mf_fd >= 0 && close(mfp->mf_fd) < 0) { EMSG(_(e_swapclose)); } - if (del_file && mfp->mf_fname != NULL) + if (del_file && mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); + } + // free entries in used list - for (hp = mfp->mf_used_first; hp != NULL; hp = nextp) { + for (bhdr_T *hp = mfp->mf_used_first, *nextp; hp != NULL; hp = nextp) { total_mem_used -= hp->bh_page_count * mfp->mf_page_size; nextp = hp->bh_next; mf_free_bhdr(hp); } - while (mfp->mf_free_first != NULL) // free entries in free list + while (mfp->mf_free_first != NULL) { // free entries in free list xfree(mf_rem_free(mfp)); + } mf_hash_free(&mfp->mf_hash); mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); + mf_free_fnames(mfp); xfree(mfp); } @@ -216,28 +213,28 @@ void mf_close(memfile_T *mfp, bool del_file) void mf_close_file(buf_T *buf, bool getlines) { memfile_T *mfp = buf->b_ml.ml_mfp; - if (mfp == NULL || mfp->mf_fd < 0) // nothing to close + if (mfp == NULL || mfp->mf_fd < 0) { // nothing to close return; + } if (getlines) { // get all blocks in memory by accessing all lines (clumsy!) mf_dont_release = true; - for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) { (void)ml_get_buf(buf, lnum, false); + } mf_dont_release = false; // TODO(elmart): should check if all blocks are really in core } - if (close(mfp->mf_fd) < 0) // close the file + if (close(mfp->mf_fd) < 0) { // close the file EMSG(_(e_swapclose)); + } mfp->mf_fd = -1; if (mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); // delete the swap file - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; + mf_free_fnames(mfp); } } @@ -390,11 +387,11 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) /// Signal block as no longer used (may put it in the free list). void mf_free(memfile_T *mfp, bhdr_T *hp) { - xfree(hp->bh_data); // free data + xfree(hp->bh_data); // free data mf_rem_hash(mfp, hp); // get *hp out of the hash list mf_rem_used(mfp, hp); // get *hp out of the used list if (hp->bh_bnum < 0) { - xfree(hp); // don't want negative numbers in free list + xfree(hp); // don't want negative numbers in free list mfp->mf_neg_count--; } else { mf_ins_free(mfp, hp); // put *hp in the free list @@ -475,10 +472,11 @@ int mf_sync(memfile_T *mfp, int flags) /// These are blocks that need to be written to a newly created swapfile. void mf_set_dirty(memfile_T *mfp) { - bhdr_T *hp; - for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) - if (hp->bh_bnum > 0) + for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { + if (hp->bh_bnum > 0) { hp->bh_flags |= BH_DIRTY; + } + } mfp->mf_dirty = true; } @@ -506,10 +504,11 @@ static void mf_ins_used(memfile_T *mfp, bhdr_T *hp) hp->bh_next = mfp->mf_used_first; mfp->mf_used_first = hp; hp->bh_prev = NULL; - if (hp->bh_next == NULL) // list was empty, adjust last pointer + if (hp->bh_next == NULL) { // list was empty, adjust last pointer mfp->mf_used_last = hp; - else + } else { hp->bh_next->bh_prev = hp; + } mfp->mf_used_count += hp->bh_page_count; total_mem_used += hp->bh_page_count * mfp->mf_page_size; } @@ -615,9 +614,10 @@ bool mf_release_all(void) FOR_ALL_BUFFERS(buf) { memfile_T *mfp = buf->b_ml.ml_mfp; if (mfp != NULL) { - // If no swap file yet, may open one. - if (mfp->mf_fd < 0 && buf->b_may_swap) + // If no swap file yet, try to open one. + if (mfp->mf_fd < 0 && buf->b_may_swap) { ml_open_file(buf); + } // Flush as many blocks as possible, only if there is a swapfile. if (mfp->mf_fd >= 0) { @@ -752,7 +752,8 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) else page_count = hp2->bh_page_count; size = page_size * page_count; - if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL) { + void *data = (hp2 == NULL) ? hp->bh_data : hp2->bh_data; + if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) { /// Avoid repeating the error message, this mostly happens when the /// disk is full. We give the message again only after a successful /// write or when hitting a key. We keep on trying, in case some @@ -773,20 +774,6 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) return OK; } -/// Write block to memfile's file. -/// -/// @return OK On success. -/// FAIL On failure. -static int mf_write_block(memfile_T *mfp, bhdr_T *hp, - off_t offset, unsigned size) -{ - void *data = hp->bh_data; - int result = OK; - if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) - result = FAIL; - return result; -} - /// Make block number positive and add it to the translation list. /// /// @return OK On success. @@ -856,13 +843,23 @@ blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) return new_bnum; } -/// Set full file name of memfile's swapfile, out of simple file name and some -/// other considerations. +/// Frees mf_fname and mf_ffname. +void mf_free_fnames(memfile_T *mfp) +{ + xfree(mfp->mf_fname); + xfree(mfp->mf_ffname); + mfp->mf_fname = NULL; + mfp->mf_ffname = NULL; +} + +/// Set the simple file name and the full file name of memfile's swapfile, out +/// of simple file name and some other considerations. /// /// Only called when creating or renaming the swapfile. Either way it's a new /// name so we must work out the full path name. -void mf_set_ffname(memfile_T *mfp) +void mf_set_fnames(memfile_T *mfp, char_u *fname) { + mfp->mf_fname = fname; mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false); } @@ -878,7 +875,7 @@ void mf_fullname(memfile_T *mfp) } } -/// Return TRUE if there are any translations pending for memfile. +/// Return true if there are any translations pending for memfile. bool mf_need_trans(memfile_T *mfp) { return mfp->mf_fname != NULL && mfp->mf_neg_count > 0; @@ -889,11 +886,11 @@ bool mf_need_trans(memfile_T *mfp) /// "fname" must be in allocated memory, and is consumed (also when error). /// /// @param flags Flags for open(). -static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) +/// @return A bool indicating success of the `open` call. +static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags) { // fname cannot be NameBuff, because it must have been allocated. - mfp->mf_fname = fname; - mf_set_ffname(mfp); + mf_set_fnames(mfp, fname); /// Extra security check: When creating a swap file it really shouldn't /// exist yet. If there is a symbolic link, this is most likely an attack. @@ -904,26 +901,26 @@ static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) EMSG(_("E300: Swap file already exists (symlink attack?)")); } else { // try to open the file - flags |= O_NOFOLLOW; - mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags); + mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags | O_NOFOLLOW); } // If the file cannot be opened, use memory only if (mfp->mf_fd < 0) { - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; - } else { + mf_free_fnames(mfp); + return false; + } + #ifdef HAVE_FD_CLOEXEC - int fdflags = fcntl(mfp->mf_fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + int fdflags = fcntl(mfp->mf_fd, F_GETFD); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + } #endif #ifdef HAVE_SELINUX - mch_copy_sec(fname, mfp->mf_fname); + mch_copy_sec(fname, mfp->mf_fname); #endif - } + + return true; } // @@ -948,20 +945,21 @@ static void mf_hash_init(mf_hashtab_T *mht) /// The hash table must not be used again without another mf_hash_init() call. static void mf_hash_free(mf_hashtab_T *mht) { - if (mht->mht_buckets != mht->mht_small_buckets) + if (mht->mht_buckets != mht->mht_small_buckets) { xfree(mht->mht_buckets); + } } /// Free the array of a hash table and all the items it contains. static void mf_hash_free_all(mf_hashtab_T *mht) { - mf_hashitem_T *next; - - for (size_t idx = 0; idx <= mht->mht_mask; idx++) + for (size_t idx = 0; idx <= mht->mht_mask; idx++) { + mf_hashitem_T *next; for (mf_hashitem_T *mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next) { next = mhi->mhi_next; xfree(mhi); } + } mf_hash_free(mht); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f58b2ac38f..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" @@ -426,10 +425,8 @@ void ml_setname(buf_T *buf) /* try to rename the swap file */ if (vim_rename(mfp->mf_fname, fname) == 0) { success = TRUE; - xfree(mfp->mf_fname); - mfp->mf_fname = fname; - xfree(mfp->mf_ffname); - mf_set_ffname(mfp); + mf_free_fnames(mfp); + mf_set_fnames(mfp, fname); ml_upd_block0(buf, UB_SAME_DIR); break; } @@ -3196,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..9173ea2d17 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3927,6 +3927,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; @@ -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 8c7805ba04..601890d133 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1261,21 +1261,18 @@ get_spec_reg ( return FALSE; } -/* - * Paste a yank register into the command line. - * Only for non-special registers. - * Used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -int -cmdline_paste_reg ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* don't add trailing CR */ -) +/// Paste a yank register into the command line. +/// Only for non-special registers. +/// Used by CTRL-R command in command-line mode +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, don't add CR characters. +/// +/// @returns FAIL for failure, OK otherwise +bool cmdline_paste_reg(int regname, bool literally, bool remcr) { long i; @@ -1286,13 +1283,9 @@ cmdline_paste_reg ( for (i = 0; i < reg->y_size; i++) { cmdline_paste_str(reg->y_array[i], literally); - /* Insert ^M between lines and after last line if type is MLINE. - * Don't do this when "remcr" is TRUE and the next line is empty. */ - if (reg->y_type == MLINE - || (i < reg->y_size - 1 - && !(remcr - && i == reg->y_size - 2 - && *reg->y_array[i + 1] == NUL))) { + // Insert ^M between lines and after last line if type is MLINE. + // Don't do this when "remcr" is true. + if ((reg->y_type == MLINE || i < reg->y_size - 1) && !remcr) { cmdline_paste_str((char_u *)"\r", literally); } @@ -3996,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 5efd71444a..3376348d6f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1448,7 +1448,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; @@ -1725,7 +1725,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 @@ -1740,11 +1740,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, @@ -2058,6 +2057,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(); @@ -2145,6 +2145,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); @@ -2324,7 +2325,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 */ @@ -2340,7 +2341,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, @@ -2349,10 +2350,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); @@ -5112,6 +5131,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; @@ -5165,6 +5188,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); @@ -5202,6 +5226,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 @@ -5581,6 +5607,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 547bd9442c..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 @@ -172,6 +173,7 @@ enum { SHM_INTRO = 'I', ///< Intro messages. SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. SHM_RECORDING = 'q', ///< Short recording message. + SHM_FILEINFO = 'F', ///< No file info messages. }; /// Represented by 'a' flag. #define SHM_ALL_ABBREVIATIONS ((char_u[]) { \ @@ -183,7 +185,7 @@ enum { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \ SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \ - SHM_RECORDING, \ + SHM_RECORDING, SHM_FILEINFO, \ 0, \ }) @@ -595,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' @@ -735,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 2e671653ed..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. @@ -166,7 +183,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) // Glue together the given directory from $PATH with name and save into // buf. STRLCPY(buf, path, e - path + 1); - append_path((char *) buf, (const char *) name, (int)buf_len); + append_path((char *) buf, (const char *) name, buf_len); if (is_executable(buf)) { // Check if the caller asked for a copy of the path. diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index c9631a434c..81ceb919c4 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -22,9 +22,9 @@ static const char *xdg_env_vars[] = { static const char *const xdg_defaults[] = { #ifdef WIN32 // Windows - [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config", - [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data", - [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache", + [kXDGConfigHome] = "$LOCALAPPDATA", + [kXDGDataHome] = "$LOCALAPPDATA", + [kXDGCacheHome] = "$TEMP", [kXDGRuntimeDir] = NULL, [kXDGConfigDirs] = NULL, [kXDGDataDirs] = NULL, @@ -66,12 +66,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) /// @param[in] idx XDG directory to use. /// /// @return [allocated] `{xdg_directory}/nvim` +/// +/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to +/// avoid storing configuration and data files in the same path. static char *get_xdg_home(const XDGVarType idx) FUNC_ATTR_WARN_UNUSED_RESULT { char *dir = stdpaths_get_xdg_var(idx); if (dir) { +#if defined(WIN32) + dir = concat_fnames_realloc(dir, + (idx == kXDGDataHome ? "nvim-data" : "nvim"), + true); +#else dir = concat_fnames_realloc(dir, "nvim", true); +#endif } return dir; } 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 5cd93ab811..d3df9bc059 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -268,16 +268,13 @@ char_u *shorten_dir(char_u *str) */ bool dir_of_file_exists(char_u *fname) { - char_u *p; - int c; - bool retval; - - p = path_tail_with_sep(fname); - if (p == fname) + char_u *p = path_tail_with_sep(fname); + if (p == fname) { return true; - c = *p; + } + char_u c = *p; *p = NUL; - retval = os_isdir(fname); + bool retval = os_isdir(fname); *p = c; return retval; } @@ -539,15 +536,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff, int flags, bool didstar) FUNC_ATTR_NONNULL_ALL { - char_u *buf; - char_u *p, *s, *e; int start_len = gap->ga_len; - char_u *pat; - int starts_with_dot; - int matches; - int len; + size_t len; bool starstar = false; - static int stardepth = 0; /* depth for "**" expansion */ + static int stardepth = 0; // depth for "**" expansion /* Expanding "**" may take a long time, check for CTRL-C. */ if (stardepth > 0) { @@ -558,16 +550,14 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, // Make room for file name. When doing encoding conversion the actual // length may be quite a bit longer, thus use the maximum possible length. - buf = xmalloc(MAXPATHL); - - /* - * Find the first part in the path name that contains a wildcard. - * When EW_ICASE is set every letter is considered to be a wildcard. - * Copy it into "buf", including the preceding characters. - */ - p = buf; - s = buf; - e = NULL; + char_u *buf = xmalloc(MAXPATHL); + + // Find the first part in the path name that contains a wildcard. + // When EW_ICASE is set every letter is considered to be a wildcard. + // Copy it into "buf", including the preceding characters. + char_u *p = buf; + char_u *s = buf; + char_u *e = NULL; const char_u *path_end = path; while (*path_end != NUL) { /* May ignore a wildcard that has a backslash before it; it will @@ -588,7 +578,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, e = p; } if (has_mbyte) { - len = (*mb_ptr2len)(path_end); + len = (size_t)(*mb_ptr2len)(path_end); STRNCPY(p, path_end, len); p += len; path_end += len; @@ -613,9 +603,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (p[0] == '*' && p[1] == '*') starstar = true; - /* convert the file pattern to a regexp pattern */ - starts_with_dot = (*s == '.'); - pat = file_pat_to_reg_pat(s, e, NULL, FALSE); + // convert the file pattern to a regexp pattern + int starts_with_dot = *s == '.'; + char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); return 0; @@ -646,9 +636,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (!didstar && stardepth < 100 && starstar && e - s == 2 && *path_end == '/') { STRCPY(s, path_end + 1); - ++stardepth; - (void)do_path_expand(gap, buf, (int)(s - buf), flags, true); - --stardepth; + stardepth++; + (void)do_path_expand(gap, buf, (size_t)(s - buf), flags, true); + stardepth--; } *s = NUL; @@ -657,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))) { @@ -703,10 +696,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, xfree(buf); vim_regfree(regmatch.regprog); - matches = gap->ga_len - start_len; - if (matches > 0) + size_t matches = (size_t)(gap->ga_len - start_len); + if (matches > 0) { qsort(((char_u **)gap->ga_data) + start_len, matches, - sizeof(char_u *), pstrcmp); + sizeof(char_u *), pstrcmp); + } return matches; } @@ -736,27 +730,24 @@ static int find_previous_pathsep(char_u *path, char_u **psep) */ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) { - int candidate_len; - int other_path_len; - char_u **other_paths = (char_u **)gap->ga_data; - char_u *rival; + char_u **other_paths = (char_u **)gap->ga_data; for (int j = 0; j < gap->ga_len; j++) { - if (j == i) - continue; /* don't compare it with itself */ - - candidate_len = (int)STRLEN(maybe_unique); - other_path_len = (int)STRLEN(other_paths[j]); - if (other_path_len < candidate_len) - continue; /* it's different when it's shorter */ - - rival = other_paths[j] + other_path_len - candidate_len; + if (j == i) { + continue; // don't compare it with itself + } + size_t candidate_len = STRLEN(maybe_unique); + size_t other_path_len = STRLEN(other_paths[j]); + if (other_path_len < candidate_len) { + continue; // it's different when it's shorter + } + char_u *rival = other_paths[j] + other_path_len - candidate_len; if (fnamecmp(maybe_unique, rival) == 0 - && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) - return false; /* match */ + && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) { + return false; // match + } } - - return true; /* no match found */ + return true; // no match found } /* @@ -770,12 +761,8 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) */ static void expand_path_option(char_u *curdir, garray_T *gap) { - char_u *path_option = *curbuf->b_p_path == NUL - ? p_path : curbuf->b_p_path; - char_u *buf; - int len; - - buf = xmalloc(MAXPATHL); + char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + char_u *buf = xmalloc(MAXPATHL); while (*path_option != NUL) { copy_option_part(&path_option, buf, MAXPATHL, " ,"); @@ -787,26 +774,27 @@ static void expand_path_option(char_u *curdir, garray_T *gap) if (curbuf->b_ffname == NULL) continue; char_u *p = path_tail(curbuf->b_ffname); - len = (int)(p - curbuf->b_ffname); - if (len + (int)STRLEN(buf) >= MAXPATHL) + size_t len = (size_t)(p - curbuf->b_ffname); + if (len + STRLEN(buf) >= MAXPATHL) { continue; - if (buf[1] == NUL) + } + if (buf[1] == NUL) { buf[len] = NUL; - else + } else { STRMOVE(buf + len, buf + 2); + } memmove(buf, curbuf->b_ffname, len); simplify_filename(buf); - } else if (buf[0] == NUL) - /* relative to current directory */ - STRCPY(buf, curdir); - else if (path_with_url((char *)buf)) - /* URL can't be used here */ - continue; - else if (!path_is_absolute_path(buf)) { - /* Expand relative path to their full path equivalent */ - len = (int)STRLEN(curdir); - if (len + (int)STRLEN(buf) + 3 > MAXPATHL) + } else if (buf[0] == NUL) { + STRCPY(buf, curdir); // relative to current directory + } else if (path_with_url((char *)buf)) { + continue; // URL can't be used here + } else if (!path_is_absolute_path(buf)) { + // Expand relative path to their full path equivalent + size_t len = STRLEN(curdir); + if (len + STRLEN(buf) + 3 > MAXPATHL) { continue; + } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); buf[len] = PATHSEP; @@ -860,31 +848,25 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap) */ static void uniquefy_paths(garray_T *gap, char_u *pattern) { - int len; - char_u **fnames = (char_u **)gap->ga_data; + char_u **fnames = (char_u **)gap->ga_data; bool sort_again = false; - char_u *pat; - char_u *file_pattern; - char_u *curdir; regmatch_T regmatch; garray_T path_ga; - char_u **in_curdir = NULL; - char_u *short_name; + char_u **in_curdir = NULL; + char_u *short_name; ga_remove_duplicate_strings(gap); ga_init(&path_ga, (int)sizeof(char_u *), 1); - /* - * We need to prepend a '*' at the beginning of file_pattern so that the - * regex matches anywhere in the path. FIXME: is this valid for all - * possible patterns? - */ - len = (int)STRLEN(pattern); - file_pattern = xmalloc(len + 2); + // We need to prepend a '*' at the beginning of file_pattern so that the + // regex matches anywhere in the path. FIXME: is this valid for all + // possible patterns? + size_t len = STRLEN(pattern); + char_u *file_pattern = xmalloc(len + 2); file_pattern[0] = '*'; file_pattern[1] = NUL; STRCAT(file_pattern, pattern); - pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE); + char_u *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true); xfree(file_pattern); if (pat == NULL) return; @@ -895,11 +877,11 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) if (regmatch.regprog == NULL) return; - curdir = xmalloc(MAXPATHL); + char_u *curdir = xmalloc(MAXPATHL); os_dirname(curdir, MAXPATHL); expand_path_option(curdir, &path_ga); - in_curdir = xcalloc(gap->ga_len, sizeof(char_u *)); + in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { char_u *path = fnames[i]; @@ -908,7 +890,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) char_u *pathsep_p; char_u *path_cutoff; - len = (int)STRLEN(path); + len = STRLEN(path); is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0 && curdir[dir_end - path] == NUL; if (is_in_curdir) @@ -1113,9 +1095,8 @@ static bool has_special_wildchar(char_u *p) int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) { - int i; garray_T ga; - char_u *p; + char_u *p; static bool recursive = false; int add_pat; bool did_expand_in_path = false; @@ -1140,11 +1121,11 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, * avoids starting the shell for each argument separately. * For `=expr` do use the internal function. */ - for (i = 0; i < num_pat; i++) { + for (int i = 0; i < num_pat; i++) { if (has_special_wildchar(pat[i]) - && !(vim_backtick(pat[i]) && pat[i][1] == '=') - ) + && !(vim_backtick(pat[i]) && pat[i][1] == '=')) { return mch_expand_wildcards(num_pat, pat, num_file, file, flags); + } } #endif @@ -1155,7 +1136,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, */ ga_init(&ga, (int)sizeof(char_u *), 30); - for (i = 0; i < num_pat; ++i) { + for (int i = 0; i < num_pat; ++i) { add_pat = -1; p = pat[i]; @@ -1212,7 +1193,9 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = true; did_expand_in_path = true; } else { - add_pat = path_expand(&ga, p, flags); + size_t tmp_add_pat = path_expand(&ga, p, flags); + assert(tmp_add_pat <= INT_MAX); + add_pat = (int)tmp_add_pat; } } } @@ -1240,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; } @@ -1261,14 +1244,12 @@ static int expand_backtick( int flags /* EW_* flags */ ) { - char_u *p; - char_u *cmd; - char_u *buffer; + char_u *p; + char_u *buffer; int cnt = 0; - int i; - /* Create the command: lop off the backticks. */ - cmd = vim_strnsave(pat + 1, (int)STRLEN(pat) - 2); + // Create the command: lop off the backticks. + char_u *cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2); if (*cmd == '=') /* `={expr}`: Expand expression */ buffer = eval_to_string(cmd + 1, &p, TRUE); @@ -1288,7 +1269,7 @@ static int expand_backtick( ++p; /* add an entry if it is not empty */ if (p > cmd) { - i = *p; + char_u i = *p; *p = NUL; addfile(gap, cmd, flags); *p = i; @@ -1517,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; } @@ -1541,12 +1521,11 @@ find_file_name_in_path ( char_u *rel_fname /* file we are searching relative to */ ) { - char_u *file_name; - int c; - char_u *tofree = NULL; + char_u *file_name; + 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); @@ -1554,8 +1533,8 @@ find_file_name_in_path ( } if (options & FNAME_EXP) { - file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, - TRUE, rel_fname); + file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true, + rel_fname); /* * If the file could not be found in a normal way, try applying @@ -1563,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); @@ -1572,7 +1551,7 @@ find_file_name_in_path ( } } if (file_name == NULL && (options & FNAME_MESS)) { - c = ptr[len]; + char_u c = ptr[len]; ptr[len] = NUL; EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); ptr[len] = c; @@ -1631,7 +1610,7 @@ bool vim_isAbsName(char_u *name) /// @param force is a flag to force expanding even if the path is absolute /// /// @return FAIL for failure, OK otherwise -int vim_FullName(const char *fname, char *buf, int len, bool force) +int vim_FullName(const char *fname, char *buf, size_t len, bool force) FUNC_ATTR_NONNULL_ARG(2) { int retval = OK; @@ -2017,14 +1996,12 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, */ int match_suffix(char_u *fname) { - int fnamelen, setsuflen; - char_u *setsuf; -#define MAXSUFLEN 30 /* maximum length of a file suffix */ +#define MAXSUFLEN 30 // maximum length of a file suffix char_u suf_buf[MAXSUFLEN]; - fnamelen = (int)STRLEN(fname); - setsuflen = 0; - for (setsuf = p_su; *setsuf; ) { + size_t fnamelen = STRLEN(fname); + size_t setsuflen = 0; + for (char_u *setsuf = p_su; *setsuf; ) { setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,"); if (setsuflen == 0) { char_u *tail = path_tail(fname); @@ -2035,10 +2012,10 @@ int match_suffix(char_u *fname) break; } } else { - if (fnamelen >= setsuflen - && fnamencmp(suf_buf, fname + fnamelen - setsuflen, - (size_t)setsuflen) == 0) + if (fnamelen >= setsuflen && + fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) { break; + } setsuflen = 0; } } @@ -2049,7 +2026,7 @@ int match_suffix(char_u *fname) /// /// @param directory Directory name, relative to current directory. /// @return `FAIL` for failure, `OK` for success. -int path_full_dir_name(char *directory, char *buffer, int len) +int path_full_dir_name(char *directory, char *buffer, size_t len) { int SUCCESS = 0; int retval = OK; @@ -2091,10 +2068,10 @@ int path_full_dir_name(char *directory, char *buffer, int len) // Append to_append to path with a slash in between. // Append to_append to path with a slash in between. -int append_path(char *path, const char *to_append, int max_len) +int append_path(char *path, const char *to_append, size_t max_len) { - int current_length = STRLEN(path); - int to_append_length = STRLEN(to_append); + size_t current_length = strlen(path); + size_t to_append_length = strlen(to_append); // Do not append empty strings. if (to_append_length == 0) { @@ -2129,12 +2106,14 @@ int append_path(char *path, const char *to_append, int max_len) /// Expand a given file to its absolute path. /// -/// @param fname The filename which should be expanded. -/// @param buf Buffer to store the absolute path of `fname`. -/// @param len Length of `buf`. -/// @param force Also expand when `fname` is already absolute. -/// @return `FAIL` for failure, `OK` for success. -static int path_get_absolute_path(const char_u *fname, char_u *buf, int len, int force) +/// @param fname filename which should be expanded. +/// @param buf buffer to store the absolute path of "fname". +/// @param len length of "buf". +/// @param force also expand when "fname" is already absolute. +/// +/// @return FAIL for failure, OK for success. +static int path_get_absolute_path(const char_u *fname, char_u *buf, + size_t len, int force) { char_u *p; *buf = NUL; 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/eo.po b/src/nvim/po/eo.po index 8215b31e65..5b0cb2260b 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Esperanto)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-05-27 04:55+0200\n" +"POT-Creation-Date: 2015-07-30 17:54+0200\n" +"PO-Revision-Date: 2015-07-30 18:00+0200\n" "Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: eo\n" @@ -115,11 +115,6 @@ msgstr "E84: Neniu modifita bufro trovita" msgid "E85: There is no listed buffer" msgstr "E85: Estas neniu listigita bufro" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: La bufro %<PRId64> ne ekzistas" - #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" msgstr "E87: Ne eblas iri preter la lastan bufron" @@ -692,6 +687,10 @@ msgstr "E696: Mankas komo en Listo: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Mankas fino de Listo ']': %s" +#: ../eval.c:5750 +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "Ne sufiĉa memory por valorigi referencojn, senrubigado ĉesigita!" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -1616,14 +1615,13 @@ msgstr "E173: %<PRId64> pliaj redaktendaj dosieroj" msgid "E174: Command already exists: add ! to replace it" msgstr "E174: La komando jam ekzistas: aldonu ! por anstataÅigi Äin" -# DP: malfacilas traduki tion, kaj samtempe honori spacetojn #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nomo Arg Interv Kompleto Difino" +" Nomo Argumentoj Adreso Kompleto Difino" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1649,6 +1647,10 @@ msgstr "E178: Nevalida defaÅlta valoro de kvantoro" msgid "E179: argument required for -complete" msgstr "E179: argumento bezonata por -complete" +#: ../ex_docmd.c:4933 +msgid "E179: argument required for -addr" +msgstr "E179: argumento bezonata por -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1671,6 +1673,11 @@ msgstr "E841: Rezervita nomo, neuzebla por komando difinita de uzanto" msgid "E184: No such user-defined command: %s" msgstr "E184: Neniu komando-difinita-de-uzanto kiel: %s" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Nevalida valoro de tipo de adreso: %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -2951,6 +2958,11 @@ msgstr "E363: Åablono uzas pli da memoro ol 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: malplena bufro" +#: ../globals.h:1226 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: La bufro %<PRId64> ne ekzistas" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Nevalida serĉa Åablono aÅ disigilo" @@ -4599,6 +4611,11 @@ msgstr "E522: Netrovita en termcap" msgid "E539: Illegal character <%s>" msgstr "E539: Nevalida signo <%s>" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Por opcio %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: Ne eblas agordi 'term' al malplena ĉeno" @@ -6552,15 +6569,15 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "Reading from stdin..." #~ msgstr "Legado el stdin..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - #~ msgid "[crypted]" #~ msgstr "[ĉifrita]" #~ msgid "E821: File is encrypted with unknown method" #~ msgstr "E821: Dosiero estas ĉifrata per nekonata metodo" +#~ msgid "Warning: Using a weak encryption method; see :help 'cm'" +#~ msgstr "Averto: uzo de malfortika ĉifrada metodo; vidu :help 'cm'" + #~ msgid "NetBeans disallows writes of unmodified buffers" #~ msgstr "NetBeans malpermesas skribojn de neÅanÄitaj bufroj" @@ -6676,8 +6693,8 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "Vim: Received \"die\" request from session manager\n" #~ msgstr "Vim: Ricevis peton \"die\" (morti) el la seanca administrilo\n" -#~ msgid "Close" -#~ msgstr "Fermi" +#~ msgid "Close tab" +#~ msgstr "Fermi langeton" #~ msgid "New tab" #~ msgstr "Nova langeto" @@ -6733,9 +6750,6 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Ne eblas malfermi fenestron interne de aplikaĵo MDI" -#~ msgid "Close tab" -#~ msgstr "Fermi langeton" - #~ msgid "Open tab..." #~ msgstr "Malfermi langeton..." 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/fr.po b/src/nvim/po/fr.po index 9b2d44ce7d..41efd5c5e3 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -6,7 +6,7 @@ # FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000. # SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003. # THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008. -# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2013. +# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2015. # # Latest translation available at: # http://dominique.pelle.free.fr/vim-fr.php @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Français)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-05-27 10:22+0200\n" +"POT-Creation-Date: 2015-07-30 17:54+0200\n" +"PO-Revision-Date: 2015-07-30 18:00+0200\n" "Last-Translator: Dominique Pellé <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: fr\n" @@ -113,11 +113,6 @@ msgstr "E84: Aucun tampon n'est modifié" msgid "E85: There is no listed buffer" msgstr "E85: Aucun tampon n'est listé" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: Le tampon %<PRId64> n'existe pas" - # AB - Je ne suis pas sûr que l'on puisse obtenir ce message. #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" @@ -756,6 +751,11 @@ msgstr "E696: Il manque une virgule dans la Liste %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Il manque ']' à la fin de la Liste %s" +#: ../eval.c:5750 +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "" +"Pas assez de mémoire pour les références, arrêt du ramassage de miètes !" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -832,15 +832,15 @@ msgstr "E785: complete() n'est utilisable que dans le mode Insertion" msgid "&Ok" msgstr "&Ok" -#: ../eval.c:8676 -#, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: un mappage existe déjà pour %s" - #: ../eval.c:8692 msgid "extend() argument" msgstr "argument de extend()" +#: ../eval.c:9345 +#, c-format +msgid "E737: Key already exists: %s" +msgstr "E737: un mappage existe déjà pour %s" + #: ../eval.c:8915 msgid "map() argument" msgstr "argument de map()" @@ -1798,10 +1798,10 @@ msgstr "E174: La commande existe déjà : ajoutez ! pour la redéfinir" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nom Args Plage Complet. Définition" +" Nom Args Adresse Complet. Définition" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1827,6 +1827,10 @@ msgstr "E178: La valeur par défaut du quantificateur est invalide" msgid "E179: argument required for -complete" msgstr "E179: argument requis avec -complete" +#: ../ex_docmd.c:4933 +msgid "E179: argument required for -addr" +msgstr "E179: argument requis avec -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1850,6 +1854,11 @@ msgstr "" msgid "E184: No such user-defined command: %s" msgstr "E184: Aucune commande %s définie par l'utilisateur" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Valeur de type d'adresse invalide : %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -3155,6 +3164,11 @@ msgstr "E363: le motif utilise plus de mémoire que 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: tampon vide" +#: ../buffer.c:1587 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Le tampon %<PRId64> n'existe pas" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Délimiteur ou motif de recherche invalide" @@ -4308,7 +4322,7 @@ msgid "" " to recover the changes (see \":help recovery\").\n" msgstr "" "\"\n" -" pour récupérer le fichier (voir \":help recovery\").\n" +" pour récupérer le fichier (consultez \":help recovery\").\n" #: ../memline.c:3250 msgid " If you did this already, delete the swap file \"" @@ -4805,6 +4819,11 @@ msgstr "E522: Introuvable dans termcap" msgid "E539: Illegal character <%s>" msgstr "E539: Caractère <%s> invalide" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Pour l'option %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: 'term' ne doit pas être une chaîne vide" @@ -6779,9 +6798,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "Reading from stdin..." #~ msgstr "Lecture de stdin..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - #~ msgid "[crypted]" #~ msgstr "[chiffré]" @@ -6909,8 +6925,8 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "Vim : Une requête \"die\" a été reçue par le gestionnaire de session\n" -#~ msgid "Close" -#~ msgstr "Fermer" +#~ msgid "Close tab" +#~ msgstr "Fermer l'onglet" #~ msgid "New tab" #~ msgstr "Nouvel onglet" @@ -6968,13 +6984,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Impossible d'ouvrir une fenêtre dans une application MDI" -# DB - Les quelques messages qui suivent se retrouvent aussi ici : -# gui_gtk_x11.c:3170 et suivants. -# Les libellés changent un peu (majuscule par exemple). -# La VF tâche de les unifier. -#~ msgid "Close tab" -#~ msgstr "Fermer l'onglet" - #~ msgid "Open tab..." #~ msgstr "Ouvrir dans un onglet..." @@ -7148,9 +7157,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E836: This Vim cannot execute :python after using :py3" #~ msgstr "E836: Vim ne peut pas exécuter :python après avoir utilisé :py3" -#~ msgid "only string keys are allowed" -#~ msgstr "seule une chaine est autorisée comme clé" - #~ msgid "" #~ "E263: Sorry, this command is disabled, the Python library could not be " #~ "loaded." @@ -7684,12 +7690,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E338: Sorry, no file browser in console mode" #~ msgstr "E338: Désolé, pas de sélecteur de fichiers en mode console" -#~ msgid "Vim: preserving files...\n" -#~ msgstr "Vim : préservation des fichiers...\n" - -#~ msgid "Vim: Finished.\n" -#~ msgstr "Vim : Fini.\n" - #~ msgid "ERROR: " #~ msgstr "ERREUR : " @@ -7718,6 +7718,10 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E547: Illegal mouseshape" #~ msgstr "E547: Forme de curseur invalide" +#~ msgid "Warning: Using a weak encryption method; see :help 'cm'" +#~ msgstr "" +#~ "Alerte : utilisation d'une méthode de chiffrage faible ; consultez :help 'cm'" + #~ msgid "Enter encryption key: " #~ msgstr "Tapez la clé de chiffrement : " @@ -7861,15 +7865,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E245: Illegal char '%c' in font name \"%s\"" #~ msgstr "E245: Caractère '%c' invalide dans le nom de fonte \"%s\"" -#~ msgid "Vim: Double signal, exiting\n" -#~ msgstr "Vim : Double signal, sortie\n" - -#~ msgid "Vim: Caught deadly signal %s\n" -#~ msgstr "Vim : Signal mortel %s intercepté\n" - -#~ msgid "Vim: Caught deadly signal\n" -#~ msgstr "Vim : Signal mortel intercepté\n" - #~ msgid "Opening the X display took %<PRId64> msec" #~ msgstr "L'ouverture du display X a pris %<PRId64> ms" @@ -7970,7 +7965,7 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "VIMRUN.EXE est introuvable votre $PATH.\n" #~ "Les commandes externes ne feront pas de pause une fois terminées.\n" -#~ "Voir :help win32-vimrun pour plus d'informations." +#~ "Consultez :help win32-vimrun pour plus d'informations." #~ msgid "Vim Warning" #~ msgstr "Alerte Vim" @@ -7982,11 +7977,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "E868: Erreur lors de la construction du NFA avec classe d'équivalence" -#~ msgid "E999: (NFA regexp internal error) Should not process NOT node !" -#~ msgstr "" -#~ "E999: (erreur interne du regexp NFA) Un noeud 'NOT' ne devrait pas être " -#~ "traité !" - #~ msgid "E878: (NFA) Could not allocate memory for branch traversal!" #~ msgstr "" #~ "E878: (NFA) Impossible d'allouer la mémoire pour parcourir les branches!" @@ -8306,30 +8296,18 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "can't delete OutputObject attributes" #~ msgstr "impossible d'effacer les attributs d'OutputObject" -#~ msgid "softspace must be an integer" -#~ msgstr "softspace doit être un nombre entier" - #~ msgid "invalid attribute" #~ msgstr "attribut invalide" -#~ msgid "writelines() requires list of strings" -#~ msgstr "writelines() requiert une liste de chaînes" - #~ msgid "E264: Python: Error initialising I/O objects" #~ msgstr "E264: Python : Erreur d'initialisation des objets d'E/S" #~ msgid "empty keys are not allowed" #~ msgstr "les clés vides ne sont pas autorisées" -#~ msgid "Cannot delete DictionaryObject attributes" -#~ msgstr "Impossible d'effacer les attributs de DictionaryObject" - #~ msgid "Cannot modify fixed dictionary" #~ msgstr "Impossible de modifier un dictionnaire fixe" -#~ msgid "Cannot set this attribute" -#~ msgstr "Impossible d'initialiser cet attribut" - #~ msgid "dict is locked" #~ msgstr "dictionnaire est verrouillé" @@ -8348,15 +8326,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "Failed to add item to list" #~ msgstr "Ajout à la liste a échoué" -#~ msgid "can only assign lists to slice" -#~ msgstr "seules des tranches peuvent être assignées aux listes" - #~ msgid "internal error: failed to add item to list" #~ msgstr "erreur interne : ajout d'élément à la liste a échoué" -#~ msgid "can only concatenate with lists" -#~ msgstr "on ne peut que concaténer avec des listes" - #~ msgid "cannot delete vim.dictionary attributes" #~ msgstr "impossible d'effacer les attributs de vim.dictionary" @@ -8366,9 +8338,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "cannot set this attribute" #~ msgstr "impossible d'initialiser cet attribut" -#~ msgid "'self' argument must be a dictionary" -#~ msgstr "l'argument 'self' doit être un dictionnaire" - #~ msgid "failed to run function" #~ msgstr "exécution de la fonction a échoué" @@ -8378,24 +8347,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "unable to unset option without global value" #~ msgstr "impossible de désactiver une option sans une valeur globale" -#~ msgid "object must be integer" -#~ msgstr "objet doit être un nombre entier" - -#~ msgid "object must be string" -#~ msgstr "objet doit être de type string" - #~ msgid "attempt to refer to deleted tab page" #~ msgstr "tentative de référencer un onglet effacé" -#~ msgid "<tabpage object (deleted) at %p>" -#~ msgstr "<objet onglet (effacé) à %p>" - -#~ msgid "<tabpage object (unknown) at %p>" -#~ msgstr "<objet onglet (inconnu) à %p>" - -#~ msgid "<tabpage %d>" -#~ msgstr "<onglet %d>" - #~ msgid "no such tab page" #~ msgstr "cet onglet n'existe pas" @@ -8408,27 +8362,12 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "cursor position outside buffer" #~ msgstr "curseur positionné en dehors du tampon" -#~ msgid "<window object (deleted) at %p>" -#~ msgstr "<objet fenêtre (effacé) à %p>" - -#~ msgid "<window object (unknown) at %p>" -#~ msgstr "<objet fenêtre (inconnu) à %p>" - -#~ msgid "<window %d>" -#~ msgstr "<fenêtre %d>" - #~ msgid "no such window" #~ msgstr "Cette fenêtre n'existe pas" #~ msgid "attempt to refer to deleted buffer" #~ msgstr "tentative de référencer un tampon effacé" -#~ msgid "<buffer object (deleted) at %p>" -#~ msgstr "<objet tampon (effacé) à %p>" - -#~ msgid "key must be integer" -#~ msgstr "la clé doit être un nombre entier" - #~ msgid "expected vim.buffer object" #~ msgstr "objet vim.buffer attendu" @@ -8467,18 +8406,3 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "internal error: invalid value type" #~ msgstr "erreur interne : type de valeur invalide" - -#~ msgid "E860: Eval did not return a valid python 3 object" -#~ msgstr "E860: Eval n'a pas retourné un object python 3 valid" - -#~ msgid "E861: Failed to convert returned python 3 object to vim value" -#~ msgstr "E861: Conversion d'objet python 3 à une valeur de vim a échoué" - -#~ msgid "E863: return value must be an instance of str" -#~ msgstr "E863: valeur de retour doit être une instance de Str" - -#~ msgid "Only boolean objects are allowed" -#~ msgstr "Seuls les objets booléens sont autorisés" - -#~ msgid "no such key in dictionary" -#~ msgstr "cette clé est inexistante dans le dictionnaire" 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 4a8391430b..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" @@ -200,9 +199,8 @@ qf_init_ext ( char_u *pattern; char_u *fmtstr = NULL; int col = 0; - char_u use_viscol = FALSE; - int type = 0; - int valid; + bool use_viscol = false; + char_u type = 0; linenr_T buflnum = lnumfirst; long lnum = 0L; int enr = 0; @@ -220,16 +218,16 @@ qf_init_ext ( int i; int round; int idx = 0; - int multiline = FALSE; - int multiignore = FALSE; - int multiscan = FALSE; - int retval = -1; /* default: return error flag */ - char_u *directory = NULL; - char_u *currfile = NULL; - char_u *tail = NULL; - char_u *p_str = NULL; - listitem_T *p_li = NULL; - struct dir_stack_T *file_stack = NULL; + bool multiline = false; + bool multiignore = false; + bool multiscan = false; + int retval = -1; // default: return error flag + char_u *directory = NULL; + char_u *currfile = NULL; + char_u *tail = NULL; + char_u *p_str = NULL; + listitem_T *p_li = NULL; + struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; static struct fmtpattern { char_u convchar; @@ -278,15 +276,16 @@ qf_init_ext ( /* * Get some space to modify the format string into. */ - i = 3 * FMT_PATTERNS + 4 * (int)STRLEN(efm); - for (round = FMT_PATTERNS; round > 0; ) - i += (int)STRLEN(fmt_pat[--round].pattern); + size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm); + for (round = FMT_PATTERNS; round > 0; ) { + fmtstr_size += STRLEN(fmt_pat[--round].pattern); + } #ifdef COLON_IN_FILENAME - i += 12; /* "%f" can become twelve chars longer */ + fmtstr_size += 12; // "%f" can become twelve chars longer #else - i += 2; /* "%f" can become two chars longer */ + fmtstr_size += 2; // "%f" can become two chars longer #endif - fmtstr = xmalloc(i); + fmtstr = xmalloc(fmtstr_size); while (efm[0] != NUL) { /* @@ -530,11 +529,9 @@ qf_init_ext ( fmt_start = NULL; } - /* - * Try to match each part of 'errorformat' until we find a complete - * match or no match. - */ - valid = TRUE; + // Try to match each part of 'errorformat' until we find a complete + // match or no match. + bool valid = true; restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; @@ -546,7 +543,7 @@ restofline: errmsg[0] = NUL; lnum = 0; col = 0; - use_viscol = FALSE; + use_viscol = false; enr = -1; type = 0; tail = NULL; @@ -555,25 +552,23 @@ restofline: int r = vim_regexec(®match, IObuff, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !multiline) + if ((idx == 'C' || idx == 'Z') && !multiline) { continue; - if (vim_strchr((char_u *)"EWI", idx) != NULL) - type = idx; - else + } + if (vim_strchr((char_u *)"EWI", idx) != NULL) { + type = (char_u)idx; + } else { type = 0; - /* - * Extract error message data from matched line. - * We check for an actual submatch, because "\[" and "\]" in - * the 'errorformat' may cause the wrong submatch to be used. - */ - if ((i = (int)fmt_ptr->addr[0]) > 0) { /* %f */ - int c; - - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + } + // Extract error message data from matched line. + // We check for an actual submatch, because "\[" and "\]" in + // the 'errorformat' may cause the wrong submatch to be used. + if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) { continue; - - /* Expand ~/file and $HOME/file to full path. */ - c = *regmatch.endp[i]; + } + // Expand ~/file and $HOME/file to full path. + char_u c = *regmatch.endp[i]; *regmatch.endp[i] = NUL; expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE); *regmatch.endp[i] = c; @@ -629,14 +624,14 @@ restofline: col -= col % 8; } } - ++col; - use_viscol = TRUE; + col++; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[8]) > 0) { /* %v */ if (regmatch.startp[i] == NULL) continue; col = (int)atol((char *)regmatch.startp[i]); - use_viscol = TRUE; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[9]) > 0) { /* %s */ if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) @@ -653,7 +648,7 @@ restofline: break; } } - multiscan = FALSE; + multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -667,20 +662,21 @@ restofline: } else if (idx == 'X') /* leave directory */ directory = qf_pop_dir(&dir_stack); } - namebuf[0] = NUL; /* no match found, remove file name */ - lnum = 0; /* don't jump to this line */ - valid = FALSE; - STRCPY(errmsg, IObuff); /* copy whole line to error message */ - if (fmt_ptr == NULL) - multiline = multiignore = FALSE; + namebuf[0] = NUL; // no match found, remove file name + lnum = 0; // don't jump to this line + valid = false; + STRCPY(errmsg, IObuff); // copy whole line to error message + if (fmt_ptr == NULL) { + multiline = multiignore = false; + } } else if (fmt_ptr != NULL) { /* honor %> item */ if (fmt_ptr->conthere) fmt_start = fmt_ptr; if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - multiline = TRUE; /* start of a multi-line message */ - multiignore = FALSE; /* reset continuation */ + multiline = true; // start of a multi-line message + multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { /* continuation of multi-line msg */ if (qfprev == NULL) @@ -702,15 +698,17 @@ restofline: qfprev->qf_viscol = use_viscol; if (!qfprev->qf_fnum) qfprev->qf_fnum = qf_get_fnum(directory, - *namebuf || directory ? namebuf - : currfile && valid ? currfile : 0); - if (idx == 'Z') - multiline = multiignore = FALSE; + *namebuf + || directory ? namebuf : currfile + && valid ? currfile : 0); + if (idx == 'Z') { + multiline = multiignore = false; + } line_breakcheck(); continue; } else if (vim_strchr((char_u *)"OPQ", idx) != NULL) { - /* global file names */ - valid = FALSE; + // global file names + valid = false; if (*namebuf == NUL || os_file_exists(namebuf)) { if (*namebuf && idx == 'P') currfile = qf_push_dir(namebuf, &file_stack); @@ -719,14 +717,15 @@ restofline: *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - multiscan = TRUE; + multiscan = true; goto restofline; } } } - if (fmt_ptr->flags == '-') { /* generally exclude this line */ - if (multiline) - multiignore = TRUE; /* also exclude continuation lines */ + if (fmt_ptr->flags == '-') { // generally exclude this line + if (multiline) { + multiignore = true; // also exclude continuation lines + } continue; } } @@ -867,26 +866,27 @@ void qf_free_all(win_T *wp) qf_free(qi, i); } -/* - * Add an entry to the end of the list of errors. - * Returns OK or FAIL. - */ -static int -qf_add_entry ( - qf_info_T *qi, /* quickfix list */ - qfline_T **prevp, /* nonnull pointer (to previously added entry or NULL) */ - char_u *dir, /* optional directory name */ - char_u *fname, /* file name or NULL */ - int bufnum, /* buffer number or zero */ - char_u *mesg, /* message */ - long lnum, /* line number */ - int col, /* column */ - int vis_col, /* using visual column */ - char_u *pattern, /* search pattern */ - int nr, /* error number */ - int type, /* type character */ - int valid /* valid entry */ -) +/// Add an entry to the end of the list of errors. +/// +/// @param qi quickfix list +/// @param prevp nonnull pointer (to previously added entry or NULL) +/// @param dir optional directory name +/// @param fname file name or NULL +/// @param bufnum buffer number or zero +/// @param mesg message +/// @param lnum line number +/// @param col column +/// @param vis_col using visual column +/// @param pattern search pattern +/// @param nr error number +/// @param type type character +/// @param valid valid entry +/// +/// @returns OK or FAIL. +static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir, + char_u *fname, int bufnum, char_u *mesg, long lnum, + int col, char_u vis_col, char_u *pattern, int nr, + char_u type, char_u valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1657,12 +1657,13 @@ win_found: * flag is present in 'shortmess'; But when not jumping, print the * whole message. */ i = msg_scroll; - if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) - msg_scroll = TRUE; - else if (!msg_scrolled && shortmess(SHM_OVERALL)) - msg_scroll = FALSE; - msg_attr_keep(IObuff, 0, TRUE); - msg_scroll = i; + if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) { + msg_scroll = true; + } else if (!msg_scrolled && shortmess(SHM_OVERALL)) { + msg_scroll = false; + } + msg_attr_keep(IObuff, 0, true); + msg_scroll = (int)i; } } else { if (opened_window) @@ -1827,10 +1828,12 @@ void qf_age(exarg_T *eap) } } - if (eap->addr_count != 0) - count = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + count = (int)eap->line2; + } else { count = 1; + } while (count--) { if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { if (qi->qf_curlist == 0) { @@ -1882,6 +1885,8 @@ static void qf_free(qf_info_T *qi, int idx) --qi->qf_lists[idx].qf_count; } xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_start = NULL; + qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; qi->qf_lists[idx].qf_index = 0; } @@ -1948,7 +1953,7 @@ static char_u *qf_types(int c, int nr) p = (char_u *)""; else { cc[0] = ' '; - cc[1] = c; + cc[1] = (char_u)c; cc[2] = NUL; p = cc; } @@ -2036,12 +2041,13 @@ void ex_copen(exarg_T *eap) } } - if (eap->addr_count != 0) - height = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + height = (int)eap->line2; + } else { height = QF_WINHEIGHT; - - reset_VIsual_and_resel(); /* stop Visual mode */ + } + reset_VIsual_and_resel(); // stop Visual mode /* * Find existing quickfix window, or open a new one. @@ -2433,8 +2439,6 @@ int grep_internal(cmdidx_T cmdidx) void ex_make(exarg_T *eap) { char_u *fname; - char_u *cmd; - unsigned len; win_T *wp = NULL; qf_info_T *qi = &ql_info; int res; @@ -2472,29 +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 = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(eap->arg) + 1; - if (*p_sp != NUL) - len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3; - 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; + // 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) { + 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; + } 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 @@ -2548,11 +2551,11 @@ static char_u *get_mef_name(void) /* Keep trying until the name doesn't exist yet. */ for (;; ) { - if (start == -1) - start = os_get_pid(); - else + if (start == -1) { + start = (int)os_get_pid(); + } else { off += 19; - + } name = xmalloc(STRLEN(p_mef) + 30); STRCPY(name, p_mef); sprintf((char *)name + (p - p_mef), "%d%d", start, off); @@ -2899,7 +2902,7 @@ void ex_vimgrep(exarg_T *eap) int found_match; buf_T *first_match_buf = NULL; time_t seconds = 0; - int save_mls; + long save_mls; char_u *save_ei = NULL; aco_save_T aco; int flags = 0; @@ -3456,18 +3459,12 @@ int get_errorlist(win_T *wp, list_T *list) */ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) { - listitem_T *li; - dict_T *d; - char_u *filename, *pattern, *text, *type; - int bufnum; - long lnum; - int col, nr; - int vcol; - qfline_T *prevp = NULL; - int valid, status; + listitem_T *li; + dict_T *d; + qfline_T *prevp = NULL; int retval = OK; - qf_info_T *qi = &ql_info; - int did_bufnr_emsg = FALSE; + qf_info_T *qi = &ql_info; + bool did_bufnr_emsg = false; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); @@ -3494,21 +3491,22 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) if (d == NULL) continue; - filename = get_dict_string(d, (char_u *)"filename", TRUE); - bufnum = get_dict_number(d, (char_u *)"bufnr"); - lnum = get_dict_number(d, (char_u *)"lnum"); - col = get_dict_number(d, (char_u *)"col"); - vcol = get_dict_number(d, (char_u *)"vcol"); - nr = get_dict_number(d, (char_u *)"nr"); - type = get_dict_string(d, (char_u *)"type", TRUE); - pattern = get_dict_string(d, (char_u *)"pattern", TRUE); - text = get_dict_string(d, (char_u *)"text", TRUE); - if (text == NULL) + char_u *filename = get_dict_string(d, (char_u *)"filename", true); + int bufnum = (int)get_dict_number(d, (char_u *)"bufnr"); + long lnum = get_dict_number(d, (char_u *)"lnum"); + int col = (int)get_dict_number(d, (char_u *)"col"); + char_u vcol = (char_u)get_dict_number(d, (char_u *)"vcol"); + int nr = (int)get_dict_number(d, (char_u *)"nr"); + char_u *type = get_dict_string(d, (char_u *)"type", true); + char_u *pattern = get_dict_string(d, (char_u *)"pattern", true); + char_u *text = get_dict_string(d, (char_u *)"text", true); + if (text == NULL) { text = vim_strsave((char_u *)""); - - valid = TRUE; - if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) - valid = FALSE; + } + bool valid = true; + if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { + valid = false; + } /* Mark entries with non-existing buffer number as not valid. Give the * error message only once. */ @@ -3517,22 +3515,23 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) did_bufnr_emsg = TRUE; EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum); } - valid = FALSE; + valid = false; bufnum = 0; } - status = qf_add_entry(qi, &prevp, - NULL, /* dir */ - filename, - bufnum, - text, - lnum, - col, - vcol, /* vis_col */ - pattern, /* search pattern */ - nr, - type == NULL ? NUL : *type, - valid); + int status = qf_add_entry(qi, + &prevp, + NULL, // dir + filename, + bufnum, + text, + lnum, + col, + vcol, // vis_col + pattern, // search pattern + nr, + (char_u)(type == NULL ? NUL : *type), + valid); xfree(filename); xfree(pattern); 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 cc7dc6210c..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" @@ -2332,14 +2331,17 @@ static void spell_load_lang(char_u *lang) if (r == FAIL) { if (starting) { - // Some startup file sets &spell, but the necessary files don't exist: - // try to prompt the user at VimEnter. Also set spell again. #3027 - do_cmdline_cmd( - "autocmd VimEnter * call spellfile#LoadFile(&spelllang)|set spell"); + // Prompt the user at VimEnter if spell files are missing. #3027 + // Plugins aren't loaded yet, so spellfile.vim cannot handle this case. + char autocmd_buf[128] = { 0 }; + snprintf(autocmd_buf, sizeof(autocmd_buf), + "autocmd VimEnter * call spellfile#LoadFile('%s')|set spell", + lang); + do_cmdline_cmd(autocmd_buf); } else { smsg( _("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), - lang, spell_enc(), lang); + lang, spell_enc(), lang); } } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 478fa973a1..26f0a6c94b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5533,25 +5533,29 @@ char_u *get_syntax_name(expand_T *xp, int idx) } -/* - * Function called for expression evaluation: get syntax ID at file position. - */ -int -syn_get_id ( +// Function called for expression evaluation: get syntax ID at file position. +int syn_get_id( win_T *wp, long lnum, colnr_T col, - int trans, /* remove transparency */ - bool *spellp, /* return: can do spell checking */ - int keep_state /* keep state of char at "col" */ + int trans, // remove transparency + bool *spellp, // return: can do spell checking + int keep_state // keep state of char at "col" ) { - /* When the position is not after the current position and in the same - * line of the same buffer, need to restart parsing. */ + // When the position is not after the current position and in the same + // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum - || col < current_col) + || col < current_col) { syntax_start(wp, lnum); + } else if (wp->w_buffer == syn_buf + && lnum == current_lnum + && col > current_col) { + // next_match may not be correct when moving around, e.g. with the + // "skip" expression in searchpair() + next_match_idx = -1; + } (void)get_syntax_attr(col, spellp, keep_state); @@ -6391,7 +6395,7 @@ do_highlight ( HL_TABLE()[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; - { + if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { if (t_colors < 16) 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 a218c03fdb..0000000000 --- a/src/nvim/tempfile.c +++ /dev/null @@ -1,138 +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" - 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 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 42fd81f643..0440272eb9 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -411,6 +411,10 @@ static int terminal_execute(VimState *state, int key) apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); break; + // Temporary fix until paste events gets implemented + case K_PASTE: + break; + case K_LEFTMOUSE: case K_LEFTDRAG: case K_LEFTRELEASE: @@ -623,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 b760811585..4debdd9acc 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -32,14 +32,16 @@ SCRIPTS := \ test69.out \ test73.out \ test79.out \ - test88.out \ test_listlbr.out \ test_breakindent.out \ test_close_count.out \ test_marks.out \ -NEW_TESTS := \ - test_alot.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/runtest.vim b/src/nvim/testdir/runtest.vim index 8314a45d0c..1c610eab51 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -33,6 +33,15 @@ if &lines < 24 || &columns < 80 cquit endif +" This also enables use of line continuation. +set viminfo+=nviminfo + +" Avoid stopping at the "hit enter" prompt +set nomore + +" Output all messages in English. +lang mess C + " Source the test script. First grab the file name, in case the script " navigates away. let testname = expand('%') @@ -40,12 +49,17 @@ let done = 0 let fail = 0 let errors = [] let messages = [] -try +if expand('%') =~ 'test_viml.vim' + " this test has intentional errors, don't use try/catch. source % -catch - let fail += 1 - call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) -endtry +else + try + source % + catch + let fail += 1 + call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) + endtry +endif " Locate Test_ functions and execute them. redir @q 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.ok b/src/nvim/testdir/test49.ok index bf1ceed9af..50fc5d2cef 100644 --- a/src/nvim/testdir/test49.ok +++ b/src/nvim/testdir/test49.ok @@ -1,19 +1,4 @@ Results of test49.vim: -*** Test 1: OK (34695) -*** Test 2: OK (34695) -*** Test 3: OK (1384648195) -*** Test 4: OK (32883) -*** Test 5: OK (32883) -*** Test 6: OK (603978947) -*** Test 7: OK (90563) -*** Test 8: OK (562493431) -*** Test 9: OK (363) -*** Test 10: OK (559615) -*** Test 11: OK (2049) -*** Test 12: OK (352256) -*** Test 13: OK (145) -*** Test 14: OK (42413) -*** Test 15: OK (42413) *** Test 16: OK (8722) *** Test 17: OK (285127993) *** Test 18: OK (67224583) diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index 70d388b06a..edd49a2b63 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2015 Sep 25 +" Last Change: 2016 Feb 07 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -608,850 +608,8 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) " END_OF_TEST_ENVIRONMENT - do not change or remove this line. -"------------------------------------------------------------------------------- -" Test 1: :endwhile in function {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :return to prevent an endless loop, and make -" this test first to get a meaningful result on an error before other -" tests will hang. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 1 " X: 1 - let first = 1 - XloopINIT 2 8 - while 1 - Xloop 1 " X: 2 + 0 * 16 - if first - Xloop 2 " X: 4 + 0 * 32 - let first = 0 - XloopNEXT - break - else - Xloop 4 " X: 0 + 0 * 64 - return - endif - endwhile -endfunction - -call F() -Xpath 128 " X: 128 - -function! G() - Xpath 256 " X: 256 + 0 * 2048 - let first = 1 - XloopINIT 512 8 - while 1 - Xloop 1 " X: 512 + 0 * 4096 - if first - Xloop 2 " X: 1024 + 0 * 8192 - let first = 0 - XloopNEXT - break - else - Xloop 4 " X: 0 + 0 * 16384 - return - endif - if 1 " unmatched :if - endwhile -endfunction - -call G() -Xpath 32768 " X: 32768 - -Xcheck 34695 - -" Leave F and G for execution as scripts in the next test. - - -"------------------------------------------------------------------------------- -" Test 2: :endwhile in script {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :finish to prevent an endless loop, and place -" this test before others that might hang to get a meaningful result -" on an error. -" -" This test executes the bodies of the functions F and G from the -" previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -ExecAsScript F " X: 1 + 2 + 4 -Xpath 128 " X: 128 - -ExecAsScript G " X: 256 + 512 + 1024 -Xpath 32768 " X: 32768 - -unlet first -delfunction F -delfunction G - -Xcheck 34695 - - -"------------------------------------------------------------------------------- -" Test 3: :if, :elseif, :while, :continue, :break {{{1 -"------------------------------------------------------------------------------- - -XpathINIT -if 1 - Xpath 1 " X: 1 - let loops = 3 - XloopINIT 2 512 - while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) - if loops <= 0 - let break_err = 1 - let loops = -1 - else " 3: 2: 1: - Xloop 1 " X: 2 + 2*512 + 2*512*512 - endif - if (loops == 2) - while loops == 2 " dummy loop - Xloop 2 " X: 4*512 - let loops = loops - 1 - continue " stop dummy loop - Xloop 4 " X: 0 - endwhile - XloopNEXT - continue " continue main loop - Xloop 8 " X: 0 - elseif (loops == 1) - let p = 1 - while p " dummy loop - Xloop 16 " X: 32*512*512 - let p = 0 - break " break dummy loop - Xloop 32 " X: 0 - endwhile - Xloop 64 " X: 128*512*512 - unlet p - break " break main loop - Xloop 128 " X: 0 - endif - if (loops > 0) - Xloop 256 " X: 512 - endif - while loops == 3 " dummy loop - let loops = loops - 1 - endwhile " end dummy loop - XloopNEXT - endwhile " end main loop - Xpath 268435456 " X: 1024*512*512 -else - Xpath 536870912 " X: 0 -endif -Xpath 1073741824 " X: 4096*512*512 -if exists("break_err") - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - unlet break_err -endif - -unlet loops - -Xcheck 1384648195 - - -"------------------------------------------------------------------------------- -" Test 4: :return {{{1 -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - if 1 - Xpath 1 " X: 1 - let loops = 3 - XloopINIT 2 16 - while loops > 0 " 3: 2: 1: - Xloop 1 " X: 2 + 2*16 + 0*16*16 - if (loops == 2) - Xloop 2 " X: 4*16 - return - Xloop 4 " X: 0 - endif - Xloop 8 " X: 16 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 8192 " X: 0 - else - Xpath 16384 " X: 0 - endif -endfunction - -call F() -Xpath 32768 " X: 8*16*16*16 - -Xcheck 32883 - -" Leave F for execution as a script in the next test. - - -"------------------------------------------------------------------------------- -" Test 5: :finish {{{1 -" -" This test executes the body of the function F from the previous test -" as a script file (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -ExecAsScript F " X: 1 + 2 + 2*16 + 4*16 + 16 -Xpath 32768 " X: 32768 - -unlet loops -delfunction F - -Xcheck 32883 - - -"------------------------------------------------------------------------------- -" Test 6: Defining functions in :while loops {{{1 -" -" Functions can be defined inside other functions. An inner function -" gets defined when the outer function is executed. Functions may -" also be defined inside while loops. Expressions in braces for -" defining the function name are allowed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - " The command CALL collects the argument of all its invocations in "calls" - " when used from a function (that is, when the global variable "calls" needs - " the "g:" prefix). This is to check that the function code is skipped when - " the function is defined. For inner functions, do so only if the outer - " function is not being executed. - " - let calls = "" - com! -nargs=1 CALL - \ if !exists("calls") && !exists("outer") | - \ let g:calls = g:calls . <args> | - \ endif - - - XloopINIT! 1 16 - - let i = 0 - while i < 3 - - XloopNEXT - let i = i + 1 - - if i == 1 - Xloop 1 " X: 1 - function! F1(arg) - CALL a:arg - let outer = 1 - - XloopINIT! 4096 4 - let j = 0 - while j < 1 - XloopNEXT - Xloop 1 " X: 4096 - let j = j + 1 - function! G1(arg) - CALL a:arg - endfunction - Xloop 2 " X: 8192 - endwhile - endfunction - Xloop 2 " X: 2 - - continue - endif - - Xloop 4 " X: 4 * (16 + 256) - function! F{i}(i, arg) - CALL a:arg - let outer = 1 - - XloopINIT! 16384 4 - if a:i == 3 - XloopNEXT - XloopNEXT - XloopNEXT - endif - let k = 0 - while k < 3 - XloopNEXT - Xloop 1 " X: 16384*(1+4+16+64+256+1024) - let k = k + 1 - function! G{a:i}{k}(arg) - CALL a:arg - endfunction - Xloop 2 " X: 32768*(1+4+16+64+256+1024) - endwhile - endfunction - Xloop 8 " X: 8 * (16 + 256) - - endwhile - - if exists("*G1") - Xpath 67108864 " X: 0 - endif - if exists("*F1") - call F1("F1") - if exists("*G1") - call G1("G1") - endif - endif - - if exists("G21") || exists("G21") || exists("G21") - Xpath 134217728 " X: 0 - endif - if exists("*F2") - call F2(2, "F2") - if exists("*G21") - call G21("G21") - endif - if exists("*G22") - call G22("G22") - endif - if exists("*G23") - call G23("G23") - endif - endif - - if exists("G31") || exists("G31") || exists("G31") - Xpath 268435456 " X: 0 - endif - if exists("*F3") - call F3(3, "F3") - if exists("*G31") - call G31("G31") - endif - if exists("*G32") - call G32("G32") - endif - if exists("*G33") - call G33("G33") - endif - endif - - Xpath 536870912 " X: 536870912 - - if calls != "F1G1F2G21G22G23F3G31G32G33" - Xpath 1073741824 " X: 0 - Xout "calls is" calls - endif - - delfunction F1 - delfunction G1 - delfunction F2 - delfunction G21 - delfunction G22 - delfunction G23 - delfunction G31 - delfunction G32 - delfunction G33 - -endif - -Xcheck 603978947 - - -"------------------------------------------------------------------------------- -" Test 7: Continuing on errors outside functions {{{1 -" -" On an error outside a function, the script processing continues -" at the line following the outermost :endif or :endwhile. When not -" inside an :if or :while, the script processing continues at the next -" line. -"------------------------------------------------------------------------------- - -XpathINIT - -if 1 - Xpath 1 " X: 1 - while 1 - Xpath 2 " X: 2 - asdf - Xpath 4 " X: 0 - break - endwhile | Xpath 8 " X: 0 - Xpath 16 " X: 0 -endif | Xpath 32 " X: 0 -Xpath 64 " X: 64 - -while 1 - Xpath 128 " X: 128 - if 1 - Xpath 256 " X: 256 - asdf - Xpath 512 " X: 0 - endif | Xpath 1024 " X: 0 - Xpath 2048 " X: 0 - break -endwhile | Xpath 4096 " X: 0 -Xpath 8192 " X: 8192 - -asdf -Xpath 16384 " X: 16384 - -asdf | Xpath 32768 " X: 0 -Xpath 65536 " X: 65536 - -Xcheck 90563 - - -"------------------------------------------------------------------------------- -" Test 8: Aborting and continuing on errors inside functions {{{1 -" -" On an error inside a function without the "abort" attribute, the -" script processing continues at the next line (unless the error was -" in a :return command). On an error inside a function with the -" "abort" attribute, the function is aborted and the script processing -" continues after the function call; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - if 1 - Xpath 1 " X: 1 - while 1 - Xpath 2 " X: 2 - asdf - Xpath 4 " X: 4 - asdf | Xpath 8 " X: 0 - Xpath 16 " X: 16 - break - endwhile - Xpath 32 " X: 32 - endif | Xpath 64 " X: 64 - Xpath 128 " X: 128 - - while 1 - Xpath 256 " X: 256 - if 1 - Xpath 512 " X: 512 - asdf - Xpath 1024 " X: 1024 - asdf | Xpath 2048 " X: 0 - Xpath 4096 " X: 4096 - endif - Xpath 8192 " X: 8192 - break - endwhile | Xpath 16384 " X: 16384 - Xpath 32768 " X: 32768 - - return novar " returns (default return value 0) - Xpath 65536 " X: 0 - return 1 " not reached -endfunction - -function! G() abort - if 1 - Xpath 131072 " X: 131072 - while 1 - Xpath 262144 " X: 262144 - asdf " returns -1 - Xpath 524288 " X: 0 - break - endwhile - Xpath 1048576 " X: 0 - endif | Xpath 2097152 " X: 0 - Xpath Xpath 4194304 " X: 0 - - return -4 " not reached -endfunction - -function! H() abort - while 1 - Xpath 8388608 " X: 8388608 - if 1 - Xpath 16777216 " X: 16777216 - asdf " returns -1 - Xpath 33554432 " X: 0 - endif - Xpath 67108864 " X: 0 - break - endwhile | Xpath 134217728 " X: 0 - Xpath 268435456 " X: 0 - - return -4 " not reached -endfunction - -" Aborted functions (G and H) return -1. -let sum = (F() + 1) - 4*G() - 8*H() -Xpath 536870912 " X: 536870912 -if sum != 13 - Xpath 1073741824 " X: 0 - Xout "sum is" sum -endif - -unlet sum -delfunction F -delfunction G -delfunction H - -Xcheck 562493431 - - -"------------------------------------------------------------------------------- -" Test 9: Continuing after aborted functions {{{1 -" -" When a function with the "abort" attribute is aborted due to an -" error, the next function back in the call hierarchy without an -" "abort" attribute continues; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() abort - Xpath 1 " X: 1 - let result = G() " not aborted - Xpath 2 " X: 2 - if result != 2 - Xpath 4 " X: 0 - endif - return 1 -endfunction - -function! G() " no abort attribute - Xpath 8 " X: 8 - if H() != -1 " aborted - Xpath 16 " X: 0 - endif - Xpath 32 " X: 32 - return 2 -endfunction - -function! H() abort - Xpath 64 " X: 64 - call I() " aborted - Xpath 128 " X: 0 - return 4 -endfunction - -function! I() abort - Xpath 256 " X: 256 - asdf " error - Xpath 512 " X: 0 - return 8 -endfunction - -if F() != 1 - Xpath 1024 " X: 0 -endif - -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 363 - - -"------------------------------------------------------------------------------- -" Test 10: :if, :elseif, :while argument parsing {{{1 -" -" A '"' or '|' in an argument expression must not be mixed up with -" a comment or a next command after a bar. Parsing errors should -" be recognized. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -if 1 || strlen("\"") | Xpath 1 " X: 1 - Xpath 2 " X: 2 -endif -Xpath 4 " X: 4 - -if 0 -elseif 1 || strlen("\"") | Xpath 8 " X: 8 - Xpath 16 " X: 16 -endif -Xpath 32 " X: 32 - -while 1 || strlen("\"") | Xpath 64 " X: 64 - Xpath 128 " X: 128 - break -endwhile -Xpath 256 " X: 256 - -let v:errmsg = "" -if 1 ||| strlen("\"") | Xpath 512 " X: 0 - Xpath 1024 " X: 0 -endif -Xpath 2048 " X: 2048 -if !MSG('E15', "Invalid expression") - Xpath 4096 " X: 0 -endif - -let v:errmsg = "" -if 0 -elseif 1 ||| strlen("\"") | Xpath 8192 " X: 0 - Xpath 16384 " X: 0 -endif -Xpath 32768 " X: 32768 -if !MSG('E15', "Invalid expression") - Xpath 65536 " X: 0 -endif - -let v:errmsg = "" -while 1 ||| strlen("\"") | Xpath 131072 " X: 0 - Xpath 262144 " X: 0 - break -endwhile -Xpath 524288 " X: 524288 -if !MSG('E15', "Invalid expression") - Xpath 1048576 " X: 0 -endif - -delfunction MSG - -Xcheck 559615 - - -"------------------------------------------------------------------------------- -" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 -" -" When code is skipped over due to an error, the boolean argument to -" an :if, :elseif, or :while must not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -let calls = 0 - -function! P(num) - let g:calls = g:calls + a:num " side effect on call - return 0 -endfunction - -if 1 - Xpath 1 " X: 1 - asdf " error - Xpath 2 " X: 0 - if P(1) " should not be called - Xpath 4 " X: 0 - elseif !P(2) " should not be called - Xpath 8 " X: 0 - else - Xpath 16 " X: 0 - endif - Xpath 32 " X: 0 - while P(4) " should not be called - Xpath 64 " X: 0 - endwhile - Xpath 128 " X: 0 -endif - -if calls % 2 - Xpath 256 " X: 0 -endif -if (calls/2) % 2 - Xpath 512 " X: 0 -endif -if (calls/4) % 2 - Xpath 1024 " X: 0 -endif -Xpath 2048 " X: 2048 - -unlet calls -delfunction P - -Xcheck 2049 - - -"------------------------------------------------------------------------------- -" Test 12: Expressions in braces in skipped code {{{1 -" -" In code skipped over due to an error or inactive conditional, -" an expression in braces as part of a variable or function name -" should not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 8 - -function! NULL() - Xloop 1 " X: 0 - return 0 -endfunction - -function! ZERO() - Xloop 2 " X: 0 - return 0 -endfunction - -function! F0() - Xloop 4 " X: 0 -endfunction - -function! F1(arg) - Xpath 4096 " X: 0 -endfunction - -let V0 = 1 - -Xpath 8192 " X: 8192 -echo 0 ? F{NULL() + V{ZERO()}}() : 1 -XloopNEXT - -Xpath 16384 " X: 16384 -if 0 - Xpath 32768 " X: 0 - call F{NULL() + V{ZERO()}}() -endif -XloopNEXT - -Xpath 65536 " X: 65536 -if 1 - asdf " error - Xpath 131072 " X: 0 - call F1(F{NULL() + V{ZERO()}}()) -endif -XloopNEXT - -Xpath 262144 " X: 262144 -if 1 - asdf " error - Xpath 524288 " X: 0 - call F{NULL() + V{ZERO()}}() -endif - -Xcheck 352256 - - -"------------------------------------------------------------------------------- -" Test 13: Failure in argument evaluation for :while {{{1 -" -" A failure in the expression evaluation for the condition of a :while -" causes the whole :while loop until the matching :endwhile being -" ignored. Continuation is at the next following line. -"------------------------------------------------------------------------------- - -XpathINIT - -Xpath 1 " X: 1 -while asdf - Xpath 2 " X: 0 - while 1 - Xpath 4 " X: 0 - break - endwhile - Xpath 8 " X: 0 - break -endwhile -Xpath 16 " X: 16 - -while asdf | Xpath 32 | endwhile | Xpath 64 " X: 0 -Xpath 128 " X: 128 - -Xcheck 145 - - -"------------------------------------------------------------------------------- -" Test 14: Failure in argument evaluation for :if {{{1 -" -" A failure in the expression evaluation for the condition of an :if -" does not cause the corresponding :else or :endif being matched to -" a previous :if/:elseif. Neither of both branches of the failed :if -" are executed. -"------------------------------------------------------------------------------- - -XpathINIT -XloopINIT 1 256 - -function! F() - Xloop 1 " X: 1 + 256 * 1 - let x = 0 - if x " false - Xloop 2 " X: 0 + 256 * 0 - elseif !x " always true - Xloop 4 " X: 4 + 256 * 4 - let x = 1 - if g:boolvar " possibly undefined - Xloop 8 " X: 8 + 256 * 0 - else - Xloop 16 " X: 0 + 256 * 0 - endif - Xloop 32 " X: 32 + 256 * 32 - elseif x " never executed - Xloop 64 " X: 0 + 256 * 0 - endif - Xloop 128 " X: 128 + 256 * 128 -endfunction - -let boolvar = 1 -call F() - -XloopNEXT -unlet boolvar -call F() - -delfunction F - -Xcheck 42413 - - -"------------------------------------------------------------------------------- -" Test 15: Failure in argument evaluation for :if (bar) {{{1 -" -" Like previous test, except that the failing :if ... | ... | :endif -" is in a single line. -"------------------------------------------------------------------------------- - -XpathINIT -XloopINIT 1 256 - -function! F() - Xloop 1 " X: 1 + 256 * 1 - let x = 0 - if x " false - Xloop 2 " X: 0 + 256 * 0 - elseif !x " always true - Xloop 4 " X: 4 + 256 * 4 - let x = 1 - if g:boolvar | Xloop 8 | else | Xloop 16 | endif " X: 8 - Xloop 32 " X: 32 + 256 * 32 - elseif x " never executed - Xloop 64 " X: 0 + 256 * 0 - endif - Xloop 128 " X: 128 + 256 * 128 -endfunction - -let boolvar = 1 -call F() - -XloopNEXT -unlet boolvar -call F() - -delfunction F - -Xcheck 42413 - +" Tests 1 to 15 were moved to test_viml.vim +let Xtest = 16 "------------------------------------------------------------------------------- " Test 16: Double :else or :elseif after :else {{{1 @@ -6591,8 +5749,7 @@ function! F() if !caught && !$VIMNOERRTHROW Xpath 8192 " X: 0 endif - if caught ? !MSG('E55', 'Unmatched \\)') - \ : !MSG('E475', "Invalid argument") + if !MSG('E475', "Invalid argument") Xpath 16384 " X: 0 endif if !caught diff --git a/src/nvim/testdir/test88.in b/src/nvim/testdir/test88.in deleted file mode 100644 index 9e43f703e9..0000000000 --- a/src/nvim/testdir/test88.in +++ /dev/null @@ -1,99 +0,0 @@ -vim: set ft=vim - -Tests for correct display (cursor column position) with +conceal and -tabulators. - -STARTTEST -:so small.vim -:if !has('conceal') - e! test.ok - wq! test.out -:endif -:" Conceal settings. -:set conceallevel=2 -:set concealcursor=nc -:syntax match test /|/ conceal -:" Save current cursor position. Only works in <expr> mode, can't be used -:" with :normal because it moves the cursor to the command line. Thanks to ZyX -:" <zyx.vim@gmail.com> for the idea to use an <expr> mapping. -:let positions = [] -:nnoremap <expr> GG ":let positions += ['".screenrow().":".screencol()."']\n" -:" Start test. -/^start: -:normal ztj -GGk -:" We should end up in the same column when running these commands on the two -:" lines. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" Same for next test block. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" And check W with multiple tabs and conceals in a line. -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:normal 0j -GGk -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:set lbr -:normal $ -GGk -:set list listchars=tab:>- -:normal 0 -GGk -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:" Display result. -:call append('$', 'end:') -:call append('$', positions) -:/^end/,$wq! test.out -ENDTEST - -start: -.concealed. text -|concealed| text - - .concealed. text - |concealed| text - -.a. .b. .c. .d. -|a| |b| |c| |d| diff --git a/src/nvim/testdir/test88.ok b/src/nvim/testdir/test88.ok deleted file mode 100644 index 12949f274a..0000000000 --- a/src/nvim/testdir/test88.ok +++ /dev/null @@ -1,29 +0,0 @@ -end: -2:1 -2:17 -2:20 -3:1 -3:17 -3:20 -5:8 -5:25 -5:28 -6:8 -6:25 -6:28 -8:1 -8:9 -8:17 -8:25 -8:27 -9:1 -9:9 -9:17 -9:25 -9:26 -9:26 -9:1 -9:9 -9:17 -9:25 -9:26 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 new file mode 100644 index 0000000000..9f0618bd45 --- /dev/null +++ b/src/nvim/testdir/test_viml.vim @@ -0,0 +1,930 @@ +" Test various aspects of the Vim language. +" Most of this was formerly in test49. + +"------------------------------------------------------------------------------- +" Test environment {{{1 +"------------------------------------------------------------------------------- + +com! XpathINIT let g:Xpath = '' +com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args> + +" Append a message to the "messages" file +func! Xout(text) + split messages + $put =a:text + wq +endfunc + +com! -nargs=1 Xout call Xout(<args>) + +" MakeScript() - Make a script file from a function. {{{2 +" +" Create a script that consists of the body of the function a:funcname. +" Replace any ":return" by a ":finish", any argument variable by a global +" variable, and and every ":call" by a ":source" for the next following argument +" in the variable argument list. This function is useful if similar tests are +" to be made for a ":return" from a function call or a ":finish" in a script +" file. +function! MakeScript(funcname, ...) + let script = tempname() + execute "redir! >" . script + execute "function" a:funcname + redir END + execute "edit" script + " Delete the "function" and the "endfunction" lines. Do not include the + " word "function" in the pattern since it might be translated if LANG is + " set. When MakeScript() is being debugged, this deletes also the debugging + " output of its line 3 and 4. + exec '1,/.*' . a:funcname . '(.*)/d' + /^\d*\s*endfunction\>/,$d + %s/^\d*//e + %s/return/finish/e + %s/\<a:\(\h\w*\)/g:\1/ge + normal gg0 + let cnt = 0 + while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0 + let cnt = cnt + 1 + s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/ + endwhile + g/^\s*$/d + write + bwipeout + return script +endfunction + +" ExecAsScript - Source a temporary script made from a function. {{{2 +" +" Make a temporary script file from the function a:funcname, ":source" it, and +" delete it afterwards. +function! ExecAsScript(funcname) + " Make a script from the function passed as argument. + let script = MakeScript(a:funcname) + + " Source and delete the script. + exec "source" script + call delete(script) +endfunction + +com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) + + +"------------------------------------------------------------------------------- +" Test 1: :endwhile in function {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :return to prevent an endless loop, and make +" this test first to get a meaningful result on an error before other +" tests will hang. +"------------------------------------------------------------------------------- + +function! T1_F() + Xpath 'a' + let first = 1 + while 1 + Xpath 'b' + if first + Xpath 'c' + let first = 0 + break + else + Xpath 'd' + return + endif + endwhile +endfunction + +function! T1_G() + Xpath 'h' + let first = 1 + while 1 + Xpath 'i' + if first + Xpath 'j' + let first = 0 + break + else + Xpath 'k' + return + endif + if 1 " unmatched :if + endwhile +endfunction + +func Test_endwhile_function() + XpathINIT + call T1_F() + Xpath 'F' + + try + call T1_G() + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171') + Xpath 'x' + endtry + Xpath 'G' + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 2: :endwhile in script {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :finish to prevent an endless loop, and place +" this test before others that might hang to get a meaningful result +" on an error. +" +" This test executes the bodies of the functions T1_F and T1_G from +" the previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_endwhile_script() + XpathINIT + ExecAsScript T1_F + Xpath 'F' + + try + ExecAsScript T1_G + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171') + Xpath 'x' + endtry + Xpath 'G' + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 3: :if, :elseif, :while, :continue, :break {{{1 +"------------------------------------------------------------------------------- + +function Test_if_while() + XpathINIT + if 1 + Xpath 'a' + let loops = 3 + while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) + if loops <= 0 + let break_err = 1 + let loops = -1 + else + Xpath 'b' . loops + endif + if (loops == 2) + while loops == 2 " dummy loop + Xpath 'c' . loops + let loops = loops - 1 + continue " stop dummy loop + Xpath 'd' . loops + endwhile + continue " continue main loop + Xpath 'e' . loops + elseif (loops == 1) + let p = 1 + while p " dummy loop + Xpath 'f' . loops + let p = 0 + break " break dummy loop + Xpath 'g' . loops + endwhile + Xpath 'h' . loops + unlet p + break " break main loop + Xpath 'i' . loops + endif + if (loops > 0) + Xpath 'j' . loops + endif + while loops == 3 " dummy loop + let loops = loops - 1 + endwhile " end dummy loop + endwhile " end main loop + Xpath 'k' + else + Xpath 'l' + endif + Xpath 'm' + if exists("break_err") + Xpath 'm' + unlet break_err + endif + + unlet loops + + call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 4: :return {{{1 +"------------------------------------------------------------------------------- + +function! T4_F() + if 1 + Xpath 'a' + let loops = 3 + while loops > 0 " 3: 2: 1: + Xpath 'b' . loops + if (loops == 2) + Xpath 'c' . loops + return + Xpath 'd' . loops + endif + Xpath 'e' . loops + let loops = loops - 1 + endwhile + Xpath 'f' + else + Xpath 'g' + endif +endfunction + +function Test_return() + XpathINIT + call T4_F() + Xpath '4' + + call assert_equal('ab3e3b2c24', g:Xpath) +endfunction + + +"------------------------------------------------------------------------------- +" Test 5: :finish {{{1 +" +" This test executes the body of the function T4_F from the previous +" test as a script file (:return replaced by :finish). +"------------------------------------------------------------------------------- + +function Test_finish() + XpathINIT + ExecAsScript T4_F + Xpath '5' + + call assert_equal('ab3e3b2c25', g:Xpath) +endfunction + + + +"------------------------------------------------------------------------------- +" Test 6: Defining functions in :while loops {{{1 +" +" Functions can be defined inside other functions. An inner function +" gets defined when the outer function is executed. Functions may +" also be defined inside while loops. Expressions in braces for +" defining the function name are allowed. +" +" The functions are defined when sourcing the script, only the +" resulting path is checked in the test function. +"------------------------------------------------------------------------------- + +XpathINIT + +" The command CALL collects the argument of all its invocations in "calls" +" when used from a function (that is, when the global variable "calls" needs +" the "g:" prefix). This is to check that the function code is skipped when +" the function is defined. For inner functions, do so only if the outer +" function is not being executed. +" +let calls = "" +com! -nargs=1 CALL + \ if !exists("calls") && !exists("outer") | + \ let g:calls = g:calls . <args> | + \ endif + +let i = 0 +while i < 3 + let i = i + 1 + if i == 1 + Xpath 'a' + function! F1(arg) + CALL a:arg + let outer = 1 + + let j = 0 + while j < 1 + Xpath 'b' + let j = j + 1 + function! G1(arg) + CALL a:arg + endfunction + Xpath 'c' + endwhile + endfunction + Xpath 'd' + + continue + endif + + Xpath 'e' . i + function! F{i}(i, arg) + CALL a:arg + let outer = 1 + + if a:i == 3 + Xpath 'f' + endif + let k = 0 + while k < 3 + Xpath 'g' . k + let k = k + 1 + function! G{a:i}{k}(arg) + CALL a:arg + endfunction + Xpath 'h' . k + endwhile + endfunction + Xpath 'i' + +endwhile + +if exists("*G1") + Xpath 'j' +endif +if exists("*F1") + call F1("F1") + if exists("*G1") + call G1("G1") + endif +endif + +if exists("G21") || exists("G22") || exists("G23") + Xpath 'k' +endif +if exists("*F2") + call F2(2, "F2") + if exists("*G21") + call G21("G21") + endif + if exists("*G22") + call G22("G22") + endif + if exists("*G23") + call G23("G23") + endif +endif + +if exists("G31") || exists("G32") || exists("G33") + Xpath 'l' +endif +if exists("*F3") + call F3(3, "F3") + if exists("*G31") + call G31("G31") + endif + if exists("*G32") + call G32("G32") + endif + if exists("*G33") + call G33("G33") + endif +endif + +Xpath 'm' + +let g:test6_result = g:Xpath +let g:test6_calls = calls + +unlet calls +delfunction F1 +delfunction G1 +delfunction F2 +delfunction G21 +delfunction G22 +delfunction G23 +delfunction G31 +delfunction G32 +delfunction G33 + +function Test_defining_functions() + call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result) + call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls) +endfunc + +"------------------------------------------------------------------------------- +" Test 7: Continuing on errors outside functions {{{1 +" +" On an error outside a function, the script processing continues +" at the line following the outermost :endif or :endwhile. When not +" inside an :if or :while, the script processing continues at the next +" line. +"------------------------------------------------------------------------------- + +XpathINIT + +if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + break + endwhile | Xpath 'd' + Xpath 'e' +endif | Xpath 'f' +Xpath 'g' + +while 1 + Xpath 'h' + if 1 + Xpath 'i' + asdf + Xpath 'j' + endif | Xpath 'k' + Xpath 'l' + break +endwhile | Xpath 'm' +Xpath 'n' + +asdf +Xpath 'o' + +asdf | Xpath 'p' +Xpath 'q' + +let g:test7_result = g:Xpath + +func Test_error_in_script() + call assert_equal('abghinoq', g:test7_result) +endfunc + +"------------------------------------------------------------------------------- +" Test 8: Aborting and continuing on errors inside functions {{{1 +" +" On an error inside a function without the "abort" attribute, the +" script processing continues at the next line (unless the error was +" in a :return command). On an error inside a function with the +" "abort" attribute, the function is aborted and the script processing +" continues after the function call; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +function! T8_F() + if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + asdf | Xpath 'd' + Xpath 'e' + break + endwhile + Xpath 'f' + endif | Xpath 'g' + Xpath 'h' + + while 1 + Xpath 'i' + if 1 + Xpath 'j' + asdf + Xpath 'k' + asdf | Xpath 'l' + Xpath 'm' + endif + Xpath 'n' + break + endwhile | Xpath 'o' + Xpath 'p' + + return novar " returns (default return value 0) + Xpath 'q' + return 1 " not reached +endfunction + +function! T8_G() abort + if 1 + Xpath 'r' + while 1 + Xpath 's' + asdf " returns -1 + Xpath 't' + break + endwhile + Xpath 'v' + endif | Xpath 'w' + Xpath 'x' + + return -4 " not reached +endfunction + +function! T8_H() abort + while 1 + Xpath 'A' + if 1 + Xpath 'B' + asdf " returns -1 + Xpath 'C' + endif + Xpath 'D' + break + endwhile | Xpath 'E' + Xpath 'F' + + return -4 " not reached +endfunction + +" Aborted functions (T8_G and T8_H) return -1. +let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H() +Xpath 'X' +let g:test8_result = g:Xpath + +func Test_error_in_function() + call assert_equal(13, g:test8_sum) + call assert_equal('abcefghijkmnoprsABX', g:test8_result) + + delfunction T8_F + delfunction T8_G + delfunction T8_H +endfunc + + +"------------------------------------------------------------------------------- +" Test 9: Continuing after aborted functions {{{1 +" +" When a function with the "abort" attribute is aborted due to an +" error, the next function back in the call hierarchy without an +" "abort" attribute continues; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() abort + Xpath 'a' + let result = G() " not aborted + Xpath 'b' + if result != 2 + Xpath 'c' + endif + return 1 +endfunction + +function! G() " no abort attribute + Xpath 'd' + if H() != -1 " aborted + Xpath 'e' + endif + Xpath 'f' + return 2 +endfunction + +function! H() abort + Xpath 'g' + call I() " aborted + Xpath 'h' + return 4 +endfunction + +function! I() abort + Xpath 'i' + asdf " error + Xpath 'j' + return 8 +endfunction + +if F() != 1 + Xpath 'k' +endif + +let g:test9_result = g:Xpath + +delfunction F +delfunction G +delfunction H +delfunction I + +func Test_func_abort() + call assert_equal('adgifb', g:test9_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 10: :if, :elseif, :while argument parsing {{{1 +" +" A '"' or '|' in an argument expression must not be mixed up with +" a comment or a next command after a bar. Parsing errors should +" be recognized. +"------------------------------------------------------------------------------- + +XpathINIT + +function! MSG(enr, emsg) + let english = v:lang == "C" || v:lang =~ '^[Ee]n' + if a:enr == "" + Xout "TODO: Add message number for:" a:emsg + let v:errmsg = ":" . v:errmsg + endif + let match = 1 + if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) + let match = 0 + if v:errmsg == "" + Xout "Message missing." + else + let v:errmsg = escape(v:errmsg, '"') + Xout "Unexpected message:" v:errmsg + endif + endif + return match +endfunction + +if 1 || strlen("\"") | Xpath 'a' + Xpath 'b' +endif +Xpath 'c' + +if 0 +elseif 1 || strlen("\"") | Xpath 'd' + Xpath 'e' +endif +Xpath 'f' + +while 1 || strlen("\"") | Xpath 'g' + Xpath 'h' + break +endwhile +Xpath 'i' + +let v:errmsg = "" +if 1 ||| strlen("\"") | Xpath 'j' + Xpath 'k' +endif +Xpath 'l' +if !MSG('E15', "Invalid expression") + Xpath 'm' +endif + +let v:errmsg = "" +if 0 +elseif 1 ||| strlen("\"") | Xpath 'n' + Xpath 'o' +endif +Xpath 'p' +if !MSG('E15', "Invalid expression") + Xpath 'q' +endif + +let v:errmsg = "" +while 1 ||| strlen("\"") | Xpath 'r' + Xpath 's' + break +endwhile +Xpath 't' +if !MSG('E15', "Invalid expression") + Xpath 'u' +endif + +let g:test10_result = g:Xpath +delfunction MSG + +func Test_expr_parsing() + call assert_equal('abcdefghilpt', g:test10_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 +" +" When code is skipped over due to an error, the boolean argument to +" an :if, :elseif, or :while must not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +let calls = 0 + +function! P(num) + let g:calls = g:calls + a:num " side effect on call + return 0 +endfunction + +if 1 + Xpath 'a' + asdf " error + Xpath 'b' + if P(1) " should not be called + Xpath 'c' + elseif !P(2) " should not be called + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + while P(4) " should not be called + Xpath 'g' + endwhile + Xpath 'h' +endif +Xpath 'x' + +let g:test11_calls = calls +let g:test11_result = g:Xpath + +unlet calls +delfunction P + +func Test_arg_abort() + call assert_equal(0, g:test11_calls) + call assert_equal('ax', g:test11_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 12: Expressions in braces in skipped code {{{1 +" +" In code skipped over due to an error or inactive conditional, +" an expression in braces as part of a variable or function name +" should not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +function! NULL() + Xpath 'a' + return 0 +endfunction + +function! ZERO() + Xpath 'b' + return 0 +endfunction + +function! F0() + Xpath 'c' +endfunction + +function! F1(arg) + Xpath 'e' +endfunction + +let V0 = 1 + +Xpath 'f' +echo 0 ? F{NULL() + V{ZERO()}}() : 1 + +Xpath 'g' +if 0 + Xpath 'h' + call F{NULL() + V{ZERO()}}() +endif + +Xpath 'i' +if 1 + asdf " error + Xpath 'j' + call F1(F{NULL() + V{ZERO()}}()) +endif + +Xpath 'k' +if 1 + asdf " error + Xpath 'l' + call F{NULL() + V{ZERO()}}() +endif + +let g:test12_result = g:Xpath + +func Test_braces_skipped() + call assert_equal('fgik', g:test12_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 13: Failure in argument evaluation for :while {{{1 +" +" A failure in the expression evaluation for the condition of a :while +" causes the whole :while loop until the matching :endwhile being +" ignored. Continuation is at the next following line. +"------------------------------------------------------------------------------- + +XpathINIT + +Xpath 'a' +while asdf + Xpath 'b' + while 1 + Xpath 'c' + break + endwhile + Xpath 'd' + break +endwhile +Xpath 'e' + +while asdf | Xpath 'f' | endwhile | Xpath 'g' +Xpath 'h' +let g:test13_result = g:Xpath + +func Test_while_fail() + call assert_equal('aeh', g:test13_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 14: Failure in argument evaluation for :if {{{1 +" +" A failure in the expression evaluation for the condition of an :if +" does not cause the corresponding :else or :endif being matched to +" a previous :if/:elseif. Neither of both branches of the failed :if +" are executed. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar " possibly undefined + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test14_result = g:Xpath + +delfunction F + +func Test_if_fail() + call assert_equal('acdfh-acfh', g:test14_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 15: Failure in argument evaluation for :if (bar) {{{1 +" +" Like previous test, except that the failing :if ... | ... | :endif +" is in a single line. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test15_result = g:Xpath + +delfunction F + +func Test_if_bar_fail() + call assert_equal('acdfh-acfh', g:test15_result) +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/undo.c b/src/nvim/undo.c index 4a8a24d79d..7d9b2c7c3b 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1782,7 +1782,13 @@ void undo_time(long step, int sec, int file, int absolute) /* "target" is the node below which we want to be. * Init "closest" to a value we can't reach. */ if (absolute) { - target = step; + if (step == 0) { + // target 0 does not exist, got to 1 and above it. + target = 1; + above = true; + } else { + target = step; + } closest = -1; } else { /* When doing computations with time_t subtract starttime, because diff --git a/src/nvim/version.c b/src/nvim/version.c index d04bc42db5..ddc1b5a9a8 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,8 +69,395 @@ static char *features[] = { // clang-format off static int included_patches[] = { - 1366, + 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 NA + 1282, + // 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 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 @@ -102,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, @@ -149,14 +536,14 @@ static int included_patches[] = { 1144, 1143, // 1142, - // 1141, + 1141, // 1140, // 1139 NA // 1138 NA 1137, // 1136, - // 1135 NA, - // 1134 NA, + // 1135 NA + // 1134 NA // 1133 NA // 1132, // 1131 NA @@ -170,22 +557,22 @@ 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, + 1105, // 1104 NA // 1103 NA // 1102, @@ -207,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, @@ -245,52 +632,52 @@ 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, + 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, // 1004 NA, // 1003 NA, // 1002 NA, - // 1001, - // 1000, + 1001, + 1000, // 999 NA // 998, // 997 NA @@ -317,7 +704,7 @@ static int included_patches[] = { // 976 NA 975, 974, - // 973, + 973, 972, // 971 NA // 970 NA @@ -332,8 +719,8 @@ static int included_patches[] = { 961, // 960 NA // 959 NA - // 958, - // 957, + 958, + 957, // 956 NA 955, // 954 NA @@ -348,8 +735,8 @@ static int included_patches[] = { 945, 944, // 943 NA - // 942, - // 941, + 942, + 941, // 940 NA 939, // 938 NA @@ -471,7 +858,7 @@ static int included_patches[] = { // 822, // 821 NA 820, - // 819, + 819, 818, 817, 816, @@ -1046,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..93a7a58678 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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. */ |