diff options
Diffstat (limited to 'src')
129 files changed, 11123 insertions, 7689 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 ea5125e4e7..6b2ce08d36 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -1,4 +1,5 @@ include(CheckLibraryExists) +include(CheckCCompilerFlag) option(USE_GCOV "Enable gcov support" OFF) @@ -41,25 +42,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,23 +81,17 @@ 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 - ex_cmds2.c ex_cmds.c ex_docmd.c ex_getln.c fileio.c - getchar.c mbyte.c memline.c message.c - misc1.c ops.c - path.c - quickfix.c regexp.c screen.c search.c @@ -256,8 +259,14 @@ install_helper(TARGETS nvim) if(CLANG_ASAN_UBSAN) message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") + check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL) + if(SANITIZE_RECOVER_ALL) + set(SANITIZE_RECOVER -fno-sanitize-recover=all) # Clang 3.6+ + else() + set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5- + endif() set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") 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..46ac3c9022 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -116,8 +116,14 @@ String vim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, } char *ptr = NULL; - replace_termcodes((char_u *)str.data, (char_u **)&ptr, - from_part, do_lt, special); + // Set 'cpoptions' the way we want it. + // FLAG_CPO_BSLASH set - backslashes are *not* treated specially + // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered + // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted + // The third from end parameter of replace_termcodes() is true so that the + // <lt> sequence is recognised - needed for a real backslash. + replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, + from_part, do_lt, special, CPO_TO_CPO_FLAGS); return cstr_as_string(ptr); } @@ -291,7 +297,7 @@ void vim_change_directory(String dir, Error *err) return; } - post_chdir(false); + post_chdir(kCdScopeGlobal); try_end(err); } @@ -331,15 +337,31 @@ Object vim_get_var(String name, Error *err) return dict_get_value(&globvardict, name, err); } -/// Sets a global variable. Passing 'nil' as value deletes the variable. +/// Sets a global variable /// /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return the old value if any +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object vim_set_var(String name, Object value, Error *err) { - return dict_set_value(&globvardict, name, value, err); + return dict_set_value(&globvardict, name, value, false, err); +} + +/// Removes a global variable +/// +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object vim_del_var(String name, Error *err) +{ + return dict_set_value(&globvardict, name, NIL, true, err); } /// Gets a vim variable diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index aad616c7bf..f644453358 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -57,8 +57,8 @@ void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) { win_T *win = find_window_by_handle(window, err); - if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger || - pos.items[1].type != kObjectTypeInteger) { + if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger + || pos.items[1].type != kObjectTypeInteger) { api_set_error(err, Validation, _("Argument \"pos\" must be a [row, col] array")); @@ -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/arabic.c b/src/nvim/arabic.c index b432e12c02..db97bd9dc4 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -1367,8 +1367,8 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int prev_laa = A_firstc_laa(prev_c, prev_c1); if (curr_laa) { - if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) && - !prev_laa) { + if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) + && !prev_laa) { curr_c = chg_c_laa2f(curr_laa); } else { curr_c = chg_c_laa2i(curr_laa); @@ -1454,19 +1454,19 @@ static bool A_is_harakat(int c) // (alphabet/number/punctuation) static bool A_is_iso(int c) { - return (c >= a_HAMZA && c <= a_GHAIN) || - (c >= a_TATWEEL && c <= a_HAMZA_BELOW) || - c == a_MINI_ALEF; + return ((c >= a_HAMZA && c <= a_GHAIN) + || (c >= a_TATWEEL && c <= a_HAMZA_BELOW) + || c == a_MINI_ALEF); } // A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character. // (alphabet/number/punctuation) static bool A_is_formb(int c) { - return (c >= a_s_FATHATAN && c <= a_s_DAMMATAN) || - c == a_s_KASRATAN || - (c >= a_s_FATHA && c <= a_f_LAM_ALEF) || - c == a_BYTE_ORDER_MARK; + return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN) + || c == a_s_KASRATAN + || (c >= a_s_FATHA && c <= a_f_LAM_ALEF) + || c == a_BYTE_ORDER_MARK); } // A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B). diff --git a/src/nvim/assert.h b/src/nvim/assert.h index 3a900aca65..761636305e 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,81 @@ // 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(cond, msg) \ + do { \ + STATIC_ASSERT_PRAGMA_START \ + STATIC_ASSERT_STATEMENT(cond, msg); \ + 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 + +#if __GNUC__ >= 6 +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") +#else +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-pedantic\"") +#endif + +# 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/auevents.lua b/src/nvim/auevents.lua index aa4a8d8332..8d891effae 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -83,6 +83,7 @@ return { 'TermResponse', -- after setting "v:termresponse" 'TextChanged', -- text was modified 'TextChangedI', -- text was modified in Insert mode + 'TextYankPost', -- after a yank or delete was done (y, d, c) 'User', -- user defined autocommand 'VimEnter', -- after starting Vim 'VimLeave', -- before exiting Vim diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 62ab7495da..72716daf0e 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; } /* @@ -401,10 +407,9 @@ close_buffer ( buf->b_nwindows = nwindows; buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - if ( - win_valid(win) && - win->w_buffer == buf) - win->w_buffer = NULL; /* make sure we don't use the buffer now */ + if (win_valid(win) && win->w_buffer == buf) { + win->w_buffer = NULL; // make sure we don't use the buffer now + } /* Autocommands may have deleted the buffer. */ if (!buf_valid(buf)) @@ -1288,14 +1293,15 @@ void enter_buffer(buf_T *buf) redraw_later(NOT_VALID); } -/* - * Change to the directory of the current buffer. - */ +// Change to the directory of the current buffer. +// Don't do this while still starting up. void do_autochdir(void) { if (p_acd) { - if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) { - shorten_fnames(TRUE); + if (starting == 0 + && curbuf->b_ffname != NULL + && vim_chdirfile(curbuf->b_ffname) == OK) { + shorten_fnames(true); } } } @@ -1333,8 +1339,8 @@ buflist_new ( /* We can use inode numbers when the file exists. Works better * for hard links. */ FileID file_id; - bool file_id_valid = (sfname != NULL && - os_fileid((char *)sfname, &file_id)); + bool file_id_valid = (sfname != NULL + && os_fileid((char *)sfname, &file_id)); if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) { @@ -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; } @@ -2189,15 +2195,16 @@ void buflist_list(exarg_T *eap) len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", buf->b_fnum, buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : - (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : - (buf->b_nwindows == 0 ? 'h' : 'a'), + buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), + buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), !MODIFIABLE(buf) ? '-' : (buf->b_p_ro ? '=' : ' '), - (buf->b_flags & BF_READERR) ? 'x' - : (bufIsChanged(buf) ? '+' : ' '), + (buf->b_flags & BF_READERR) ? 'x' : (bufIsChanged(buf) ? '+' : ' '), NameBuff); + if (len > IOSIZE - 20) { + len = IOSIZE - 20; + } + /* put "line 999" in column 40 or after the file name */ i = 40 - vim_strsize(IObuff); do { @@ -2421,52 +2428,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 +2500,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 +2790,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 +3039,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 +3947,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 +3977,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 +4614,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 +5137,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..0324f6b88a 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -141,8 +141,8 @@ struct buffblock { struct buffheader { buffblock_T bh_first; // first (dummy) block of list buffblock_T *bh_curr; // buffblock for appending - int bh_index; // index for reading - int bh_space; // space in bh_curr for appending + size_t bh_index; // index for reading + size_t bh_space; // space in bh_curr for appending }; /* @@ -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..d0dc7b66fc 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 @@ -1794,10 +1794,11 @@ bool vim_isblankline(char_u *lbuf) /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. -void vim_str2nr(char_u *start, int *prep, int *len, int what, - long *nptr, unsigned long *unptr, int maxlen) +void vim_str2nr(const char_u *const start, int *const prep, int *const len, + const int what, long *const nptr, unsigned long *const unptr, + const int maxlen) { - char_u *ptr = start; + const char_u *ptr = start; int pre = 0; // default is decimal bool negative = false; unsigned long un = 0; 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..e131da8fe0 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) { @@ -1365,6 +1359,9 @@ ins_redraw ( update_screen(0); } if (has_event(EVENT_CURSORMOVEDI)) { + // Make sure curswant is correct, an autocommand may call + // getcurpos() + update_curswant(); apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, false, curbuf); } if (curwin->w_p_cole > 0) { @@ -1795,33 +1792,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 +1847,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 +1926,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 +2201,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; } /* @@ -2314,6 +2322,22 @@ static int ins_compl_make_cyclic(void) return count; } + +// Set variables that store noselect and noinsert behavior from the +// 'completeopt' value. +void completeopt_was_set(void) +{ + compl_no_insert = false; + compl_no_select = false; + if (strstr((char *)p_cot, "noselect") != NULL) { + compl_no_select = true; + } + if (strstr((char *)p_cot, "noinsert") != NULL) { + compl_no_insert = true; + } +} + + /* * Start completion for the complete() function. * "startcol" is where the matched text starts (1 is first column). @@ -2353,13 +2377,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 +2427,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 +2884,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 +2930,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 +3084,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 +3103,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 @@ -3103,17 +3116,6 @@ static int ins_compl_prep(int c) } - if (strstr((char *)p_cot, "noselect") != NULL) { - compl_no_insert = FALSE; - compl_no_select = TRUE; - } else if (strstr((char *)p_cot, "noinsert") != NULL) { - compl_no_insert = TRUE; - compl_no_select = FALSE; - } else { - compl_no_insert = FALSE; - compl_no_select = FALSE; - } - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { /* * We have just typed CTRL-X and aren't quite sure which CTRL-X mode @@ -3247,11 +3249,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 +3271,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); @@ -3304,6 +3306,12 @@ static int ins_compl_prep(int c) showmode(); } + // Avoid the popup menu remains displayed when leaving the + // command line window. + if (c == Ctrl_C && cmdwin_type != 0) { + update_screen(0); + } + /* * Indent now if a key was typed that is in 'cinkeys'. */ @@ -4245,22 +4253,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 +4288,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 +4304,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; } /* @@ -4421,11 +4430,10 @@ static int ins_complete(int c, bool enable_pum) prefix = (char_u *)""; STRCPY((char *)compl_pattern, prefix); (void)quote_meta(compl_pattern + STRLEN(prefix), - line + compl_col, compl_length); - } else if (--startcol < 0 || - !vim_iswordp(mb_prevptr(line, line + startcol + 1)) - ) { - /* Match any word of at least two chars */ + line + compl_col, compl_length); + } else if (--startcol < 0 + || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { + // Match any word of at least two chars compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); compl_col += curs_col; compl_length = 0; @@ -6313,18 +6321,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 +6572,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 +6606,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 +6648,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; + if (skipwhite(p) == p + curwin->w_cursor.col - 4 + && 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 +6715,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 +6806,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 +6825,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) */ look = skip_to_option_part(look); } - return FALSE; + return false; } /* @@ -7046,27 +7052,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 +7099,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 +7152,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 +7198,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 +7218,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 +7252,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 +7362,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 */ @@ -7384,43 +7389,43 @@ static int ins_bs(int c, int mode, int *inserted_space_p) * can't backup past starting point unless 'backspace' > 1 * can backup to a previous line if 'backspace' == 0 */ - if ( bufempty() - || ( - !revins_on && - ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) - || (!can_bs(BS_START) - && (arrow_used - || (curwin->w_cursor.lnum == Insstart_orig.lnum - && curwin->w_cursor.col <= Insstart_orig.col))) - || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 - && curwin->w_cursor.col <= ai_col) - || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { + if (bufempty() + || (!revins_on + && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) + || (!can_bs(BS_START) + && (arrow_used + || (curwin->w_cursor.lnum == Insstart_orig.lnum + && curwin->w_cursor.col <= Insstart_orig.col))) + || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 + && curwin->w_cursor.col <= ai_col) + || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { vim_beep(BO_BS); 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 +7437,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; } /* @@ -7639,14 +7644,14 @@ static int ins_bs(int c, int mode, int *inserted_space_p) if (revins_on && gchar_cursor() == NUL) break; } - /* Just a single backspace?: */ - if (mode == BACKSPACE_CHAR) + // Just a single backspace?: + if (mode == BACKSPACE_CHAR) { break; - } while ( - revins_on || - (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); + } + } while (revins_on + || (curwin->w_cursor.col > mincol + && (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col))); } did_backspace = true; } @@ -7677,12 +7682,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 +7753,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 +8008,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 +8046,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 +8192,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 +8238,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 +8494,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 +8520,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 31175773f0..201a71facb 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,93 +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 }, + 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 @@ -416,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; @@ -457,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", @@ -479,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, @@ -496,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); @@ -506,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) @@ -535,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); @@ -545,10 +516,19 @@ void eval_init(void) set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + + dict_T *v_event = dict_alloc(); + v_event->dv_lock = VAR_FIXED; + set_vim_var_dict(VV_EVENT, v_event); 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) @@ -556,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); @@ -791,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; @@ -842,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; @@ -1726,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 *)"", @@ -2280,8 +2266,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, true, false) == OK) { if ((di == NULL - || (!var_check_ro(di->di_flags, lp->ll_name, false) && - !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false))) + || (!var_check_ro(di->di_flags, lp->ll_name, false) + && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false))) && tv_op(&tv, rettv, op) == OK) { set_var(lp->ll_name, &tv, false); } @@ -2410,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: @@ -2482,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); } } @@ -3119,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; } @@ -3197,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; @@ -3259,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; @@ -4010,12 +4010,22 @@ eval6 ( * When either side is a float the result is a float. */ if (use_float) { - if (op == '*') + if (op == '*') { f1 = f1 * f2; - else if (op == '/') { - /* We rely on the floating point library to handle divide - * by zero to result in "inf" and not a crash. */ - f1 = f2 != 0 ? f1 / f2 : INFINITY; + } else if (op == '/') { + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 + ? ( +#ifdef NAN + f1 == 0 + ? NAN + : +#endif + (f1 > 0 + ? INFINITY + : -INFINITY) + ) + : f1 / f2); } else { EMSG(_("E804: Cannot use '%' with Float")); return FAIL; @@ -4141,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; @@ -4330,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); @@ -4528,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 } } @@ -4702,13 +4740,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) ++name; break; - /* Special key, e.g.: "\<C-W>" */ - case '<': extra = trans_special(&p, name, TRUE); + // Special key, e.g.: "\<C-W>" + case '<': + extra = trans_special((const char_u **) &p, STRLEN(p), name, true); if (extra != 0) { name += extra; break; } - /* FALLTHROUGH */ + // FALLTHROUGH default: MB_COPY_CHAR(p, name); break; @@ -5072,10 +5111,18 @@ tv_equal ( s1 = get_tv_string_buf(tv1, buf1); s2 = get_tv_string_buf(tv2, buf2); return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; + + case VAR_SPECIAL: + return tv1->vval.v_special == tv2->vval.v_special; + + case VAR_UNKNOWN: + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does + // not equal anything, not even self. + return false; } - EMSG2(_(e_intern2), "tv_equal()"); - return TRUE; + assert(false); + return false; } /* @@ -5476,9 +5523,10 @@ static int list_join_inner(garray_T *const gap, list_T *const l, for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { char *s; size_t len; - s = echo_string(&item->li_tv, &len); - if (s == NULL) + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { return FAIL; + } sumlen += (int) len; @@ -5486,9 +5534,6 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *) s; line_breakcheck(); - if (did_echo_string_emsg) { // recursion error, bail out - break; - } } /* Allocate result buffer with its total size, avoid re-allocation and @@ -5540,6 +5585,22 @@ static int list_join(garray_T *const gap, list_T *const l, return retval; } +/// Get next (unique) copy ID +/// +/// Used for traversing nested structures e.g. when serializing them or garbage +/// collecting. +int get_copyID(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // CopyID for recursively traversing lists and dicts + // + // This value is needed to avoid endless recursiveness. Last bit is used for + // previous_funccal and normally ignored when comparing. + static int current_copyID = 0; + current_copyID += COPYID_INC; + return current_copyID; +} + /* * Garbage collection for lists and dictionaries. * @@ -5575,8 +5636,7 @@ bool garbage_collect(void) // We advance by two because we add one for items referenced through // previous_funccal. - current_copyID += COPYID_INC; - int copyID = current_copyID; + const int copyID = get_copyID(); // 1. Go through all accessible variables and mark all lists and dicts // with copyID. @@ -5921,6 +5981,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, } break; } + + case VAR_FUNC: + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_NUMBER: + case VAR_STRING: { + break; + } } return abort; } @@ -6007,6 +6076,27 @@ static void rettv_dict_alloc(typval_T *rettv) ++d->dv_refcount; } +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void dict_clear(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + size_t todo = d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + dictitem_free(HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + todo--; + } + } + + hash_unlock(&d->dv_hashtab); +} + /* * Unreference a Dictionary: decrement the reference count and free it when it @@ -6248,6 +6338,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list) return OK; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen +void dict_set_keys_readonly(dict_T *dict) + FUNC_ATTR_NONNULL_ALL +{ + size_t todo = dict->dv_hashtab.ht_used; + for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { + if (HASHITEM_EMPTY(hi)) { + continue; + } + todo--; + HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + } +} + /* * Get the number of items in a Dictionary. */ @@ -6473,580 +6581,22 @@ failret: return OK; } -#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ - do { \ - if ((val)->copyID_attr == copyID) { \ - CONV_RECURSE((val), conv_type); \ - } \ - (val)->copyID_attr = copyID; \ - } while (0) - -/// Define functions which convert VimL value to something else -/// -/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const -/// tv)` which returns OK or FAIL and helper functions. +/// Convert the string to a floating point number /// -/// @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 { \ - 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(). +/// 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 *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. -/// -/// @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 *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. @@ -7106,7 +6656,7 @@ static struct fst { } functions[] = { { "abs", 1, 1, f_abs }, - { "acos", 1, 1, f_acos }, // WJMc + { "acos", 1, 1, f_acos }, // WJMc { "add", 2, 2, f_add }, { "and", 2, 2, f_and }, { "append", 2, 2, f_append }, @@ -7116,6 +6666,7 @@ static struct fst { { "argv", 0, 1, f_argv }, { "asin", 1, 1, f_asin }, // WJMc { "assert_equal", 2, 3, f_assert_equal }, + { "assert_exception", 1, 2, f_assert_exception }, { "assert_false", 1, 2, f_assert_false }, { "assert_true", 1, 2, f_assert_true }, { "atan", 1, 1, f_atan }, @@ -7123,9 +6674,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 }, @@ -7152,7 +6703,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 }, @@ -7169,7 +6720,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 }, @@ -7199,7 +6750,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 }, @@ -7223,7 +6774,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 @@ -7256,6 +6807,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 }, @@ -7290,13 +6843,14 @@ static struct fst { { "pathshorten", 1, 1, f_pathshorten }, { "pow", 2, 2, f_pow }, { "prevnonblank", 1, 1, f_prevnonblank }, - { "printf", 2, 19, f_printf }, + { "printf", 2, MAX_FUNC_ARGS, f_printf }, { "pumvisible", 0, 0, f_pumvisible }, { "py3eval", 1, 1, f_py3eval }, { "pyeval", 1, 1, f_pyeval }, { "range", 1, 3, f_range }, { "readfile", 1, 3, f_readfile }, { "reltime", 0, 2, f_reltime }, + { "reltimefloat", 1, 1, f_reltimefloat }, { "reltimestr", 1, 1, f_reltimestr }, { "remove", 2, 3, f_remove }, { "rename", 2, 2, f_rename }, @@ -7304,8 +6858,8 @@ static struct fst { { "resolve", 1, 1, f_resolve }, { "reverse", 1, 1, f_reverse }, { "round", 1, 1, f_round }, - { "rpcnotify", 2, 64, f_rpcnotify }, - { "rpcrequest", 2, 64, f_rpcrequest }, + { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify }, + { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest }, { "rpcstart", 1, 2, f_rpcstart }, { "rpcstop", 1, 1, f_rpcstop }, { "screenattr", 2, 2, f_screenattr }, @@ -7396,6 +6950,7 @@ static struct fst { { "winrestview", 1, 1, f_winrestview }, { "winsaveview", 0, 0, f_winsaveview }, { "winwidth", 1, 1, f_winwidth }, + { "wordcount", 0, 0, f_wordcount }, { "writefile", 2, 3, f_writefile }, { "xor", 2, 2, f_xor }, }; @@ -8030,19 +7585,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); @@ -8076,17 +7631,43 @@ static void f_assert_equal(typval_T *argvars, typval_T *rettv) } } +/// "assert_exception(string[, msg])" function +static void f_assert_exception(typval_T *argvars, typval_T *rettv) +{ + garray_T ga; + + char *error = (char *)get_tv_string_chk(&argvars[0]); + if (vimvars[VV_EXCEPTION].vv_str == NULL) { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"v:exception is not set"); + assert_error(&ga); + ga_clear(&ga); + } else if (strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], + &vimvars[VV_EXCEPTION].vv_tv); + assert_error(&ga); + ga_clear(&ga); + } +} + // 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); @@ -8748,16 +8329,17 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = cs_connection(num, dbpath, prepend); } -/* - * "cursor(lnum, col)" function - * - * Moves the cursor to the specified line and column. - * Returns 0 when the position could be set, -1 otherwise. - */ +/// "cursor(lnum, col)" function, or +/// "cursor(list)" +/// +/// Moves the cursor to the specified line and column. +/// +/// @returns 0 when the position could be set, -1 otherwise. static void f_cursor(typval_T *argvars, typval_T *rettv) { long line, col; long coladd = 0; + bool set_curswant = true; rettv->vval.v_number = -1; if (argvars[1].v_type == VAR_UNKNOWN) { @@ -8765,37 +8347,44 @@ static void f_cursor(typval_T *argvars, typval_T *rettv) colnr_T curswant = -1; if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) { + EMSG(_(e_invarg)); return; } + line = pos.lnum; col = pos.col; coladd = pos.coladd; if (curswant >= 0) { curwin->w_curswant = curswant - 1; + set_curswant = false; } } else { line = get_tv_lnum(argvars); col = get_tv_number_chk(&argvars[1], NULL); - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { coladd = get_tv_number_chk(&argvars[2], NULL); + } } if (line < 0 || col < 0 - || coladd < 0 - ) - return; /* type error; errmsg already given */ - if (line > 0) + || coladd < 0) { + return; // type error; errmsg already given + } + if (line > 0) { curwin->w_cursor.lnum = line; - if (col > 0) + } + if (col > 0) { curwin->w_cursor.col = col - 1; + } curwin->w_cursor.coladd = coladd; - /* Make sure the cursor is in a valid position. */ + // Make sure the cursor is in a valid position. check_cursor(); - /* Correct cursor for multi-byte character. */ - if (has_mbyte) + // Correct cursor for multi-byte character. + if (has_mbyte) { mb_adjust_cursor(); + } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = set_curswant; rettv->vval.v_number = 0; } @@ -8808,25 +8397,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 @@ -9003,7 +8618,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: @@ -9025,9 +8640,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; @@ -9082,7 +8700,11 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv) */ static void f_executable(typval_T *argvars, typval_T *rettv) { - rettv->vval.v_number = os_can_exe(get_tv_string(&argvars[0]), NULL); + char_u *name = get_tv_string(&argvars[0]); + + // Check in $PATH and also check directly if there is a directory name + rettv->vval.v_number = os_can_exe(name, NULL, true) + || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); } /// "exepath()" function @@ -9091,7 +8713,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv) char_u *arg = get_tv_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path); + (void)os_can_exe(arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -10199,22 +9821,130 @@ 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 = MIN_CD_SCOPE; + + // 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 = MIN_CD_SCOPE; 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; + } + 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; + // It is an error for the scope number to be less than `-1`. + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // Normalize scope, the number of the new scope will be 0. + if (scope_number[scope] < 0) { + // Arguments to `getcwd` always end at second-highest scope, so scope will + // always be <= `MAX_CD_SCOPE`. + 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.")); + 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; + } + } + } + cwd = xmalloc(MAXPATHL); - if (os_dirname(cwd, MAXPATHL) != FAIL) { - rettv->vval.v_string = vim_strsave(cwd); + + switch (scope) { + case kCdScopeWindow: + assert(win); + from = win->w_localdir; + if (from) { + break; + } + case kCdScopeTab: // FALLTHROUGH + assert(tp); + from = tp->localdir; + if (from) { + break; + } + case kCdScopeGlobal: // FALLTHROUGH + // The `globaldir` variable is not always set. + if (globaldir) { + from = globaldir; + } else { + // We have to copy the OS path directly into output string. + 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); } @@ -10465,6 +10195,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { + update_curswant(); list_append_number(l, curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1); @@ -10537,9 +10268,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); @@ -10553,8 +10285,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) { char_u *strregname; int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -10571,18 +10301,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) if (regname == 0) regname = '"'; - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1)); - break; - } + colnr_T reglen = 0; + char buf[NUMBUFLEN + 2]; + MotionType reg_type = get_reg_type(regname, ®len); + format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf)); + rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)xstrdup(buf); } /* @@ -10959,6 +10684,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "tablineat", "tag_binary", "tag_old_static", + "termguicolors", "termresponse", "textobjects", "title", @@ -11040,12 +10766,103 @@ 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 = MIN_CD_SCOPE; + + // 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 = MIN_CD_SCOPE; i < MAX_CD_SCOPE; 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; + } + } + + // Normalize scope, the number of the new scope will be 0. + if (scope_number[scope] < 0) { + // Arguments to `haslocaldir` always end at second-highest scope, so scope + // will always be <= `MAX_CD_SCOPE`. + 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: + assert(win); + rettv->vval.v_number = win->w_localdir ? 1 : 0; + break; + case kCdScopeTab: + assert(tp); + rettv->vval.v_number = tp->localdir ? 1 : 0; + break; + case kCdScopeGlobal: + // The global scope never has a local directory + rettv->vval.v_number = 0; + break; + } } /* @@ -11078,21 +10895,22 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv) */ static void f_histadd(typval_T *argvars, typval_T *rettv) { - int histype; + HistoryType histype; char_u *str; char_u buf[NUMBUFLEN]; - rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) + rettv->vval.v_number = false; + if (check_restricted() || check_secure()) { return; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - histype = str != NULL ? get_histtype(str) : -1; - if (histype >= 0) { + } + str = get_tv_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + if (histype != HIST_INVALID) { str = get_tv_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, FALSE, NUL); - rettv->vval.v_number = TRUE; + add_to_history(histype, str, false, NUL); + rettv->vval.v_number = true; return; } } @@ -11107,20 +10925,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) char_u buf[NUMBUFLEN]; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { n = 0; - else if (argvars[1].v_type == VAR_UNKNOWN) - /* only one argument: clear entire history */ - n = clr_history(get_histtype(str)); - else if (argvars[1].v_type == VAR_NUMBER) - /* index given: remove that entry */ - n = del_history_idx(get_histtype(str), - (int)get_tv_number(&argvars[1])); - else - /* string given: remove all matching entries */ - n = del_history_entry(get_histtype(str), - get_tv_string_buf(&argvars[1], buf)); + } else if (argvars[1].v_type == VAR_UNKNOWN) { + // only one argument: clear entire history + n = clr_history(get_histtype(str, STRLEN(str), false)); + } else if (argvars[1].v_type == VAR_NUMBER) { + // index given: remove that entry + n = del_history_idx(get_histtype(str, STRLEN(str), false), + (int) get_tv_number(&argvars[1])); + } else { + // string given: remove all matching entries + n = del_history_entry(get_histtype(str, STRLEN(str), false), + get_tv_string_buf(&argvars[1], buf)); + } rettv->vval.v_number = n; } @@ -11129,20 +10948,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) */ static void f_histget(typval_T *argvars, typval_T *rettv) { - int type; + HistoryType type; int idx; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { rettv->vval.v_string = NULL; - else { - type = get_histtype(str); - if (argvars[1].v_type == VAR_UNKNOWN) + } else { + type = get_histtype(str, STRLEN(str), false); + if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); - else + } else { idx = (int)get_tv_number_chk(&argvars[1], NULL); - /* -1 on type error */ + } + // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); } rettv->v_type = VAR_STRING; @@ -11157,11 +10977,13 @@ static void f_histnr(typval_T *argvars, typval_T *rettv) char_u *history = get_tv_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history); - if (i >= HIST_CMD && i < HIST_COUNT) + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + false); + if (i != HIST_INVALID) { i = get_history_idx(i); - else + } else { i = -1; + } rettv->vval.v_number = i; } @@ -11797,7 +11619,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd) assert(argl->lv_first); const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL)) { + if (!exe || !os_can_exe(exe, NULL, true)) { // String is not executable if (exe) { EMSG2(e_jobexe, exe); @@ -12053,6 +11875,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 */ @@ -12094,7 +11957,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; } @@ -12254,8 +12120,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode(&which, 0); - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); - rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, + CPO_TO_CPO_FLAGS); + rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); xfree(keys_buf); if (!get_dict) { @@ -12417,9 +12284,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); @@ -12817,289 +12685,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 @@ -13113,7 +12698,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]; @@ -13121,307 +12706,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 @@ -13439,7 +12730,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)); @@ -13453,7 +12744,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"); @@ -14282,14 +13573,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) } } -#define SP_NOMOVE 0x01 /* don't move cursor */ -#define SP_REPEAT 0x02 /* repeat to find outer pair */ -#define SP_RETCOUNT 0x04 /* return matchcount */ -#define SP_SETPCMARK 0x08 /* set previous context mark */ -#define SP_START 0x10 /* accept match at start position */ -#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */ -#define SP_END 0x40 /* leave cursor at end of match */ - +#define SP_NOMOVE 0x01 ///< don't move cursor +#define SP_REPEAT 0x02 ///< repeat to find outer pair +#define SP_RETCOUNT 0x04 ///< return matchcount +#define SP_SETPCMARK 0x08 ///< set previous context mark +#define SP_START 0x10 ///< accept match at start position +#define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern +#define SP_END 0x40 ///< leave cursor at end of match +#define SP_COLUMN 0x80 ///< start at cursor column /* * Get flags for a search function. @@ -14315,13 +13606,14 @@ static int get_search_arg(typval_T *varp, int *flagsp) default: mask = 0; if (flagsp != NULL) switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; } if (mask == 0) { EMSG2(_(e_invarg2), flags); @@ -14337,9 +13629,7 @@ static int get_search_arg(typval_T *varp, int *flagsp) return dir; } -/* - * Shared by search() and searchpos() functions - */ +// Shared by search() and searchpos() functions. static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; @@ -14360,10 +13650,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (dir == 0) goto theend; flags = *flagsp; - if (flags & SP_START) + if (flags & SP_START) { options |= SEARCH_START; - if (flags & SP_END) + } + if (flags & SP_END) { options |= SEARCH_END; + } + if (flags & SP_COLUMN) { + options |= SEARCH_COL; + } /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { @@ -15228,43 +14523,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_UNKNOWN) { - 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; } } @@ -15279,7 +14582,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); } } @@ -15388,25 +14691,30 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) name = get_tv_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { - if (--pos.col < 0) + if (--pos.col < 0) { pos.col = 0; + } if (name[0] == '.' && name[1] == NUL) { - /* set cursor */ + // set cursor if (fnum == curbuf->b_fnum) { curwin->w_cursor = pos; if (curswant >= 0) { curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; } check_cursor(); rettv->vval.v_number = 0; - } else + } else { EMSG(_(e_invarg)); + } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { - /* set mark */ - if (setmark_pos(name[1], &pos, fnum) == OK) + // set mark + if (setmark_pos(name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; - } else + } + } else { EMSG(_(e_invarg)); + } } } } @@ -15416,7 +14724,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); } /* @@ -15428,11 +14736,11 @@ static void f_setreg(typval_T *argvars, typval_T *rettv) char_u *strregname; char_u *stropt; bool append = false; - char_u yank_type; + MotionType yank_type; long block_len; block_len = -1; - yank_type = MAUTO; + yank_type = kMTUnknown; strregname = get_tv_string_chk(argvars); rettv->vval.v_number = 1; /* FAIL is default */ @@ -15449,17 +14757,17 @@ static void f_setreg(typval_T *argvars, typval_T *rettv) return; /* type error */ for (; *stropt != NUL; ++stropt) switch (*stropt) { - case 'a': case 'A': /* append */ + case 'a': case 'A': // append append = true; break; - case 'v': case 'c': /* character-wise selection */ - yank_type = MCHAR; + case 'v': case 'c': // character-wise selection + yank_type = kMTCharWise; break; - case 'V': case 'l': /* line-wise selection */ - yank_type = MLINE; + case 'V': case 'l': // line-wise selection + yank_type = kMTLineWise; break; - case 'b': case Ctrl_V: /* block-wise selection */ - yank_type = MBLOCK; + case 'b': case Ctrl_V: // block-wise selection + yank_type = kMTBlockWise; if (ascii_isdigit(stropt[1])) { ++stropt; block_len = getdigits_long(&stropt) - 1; @@ -15685,6 +14993,8 @@ typedef struct { static int item_compare_ic; static bool item_compare_numeric; +static bool item_compare_numbers; +static bool item_compare_float; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; @@ -15706,9 +15016,24 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) si2 = (sortItem_T *)s2; typval_T *tv1 = &si1->item->li_tv; typval_T *tv2 = &si2->item->li_tv; - // tv2string() puts quotes around a string and allocates memory. Don't do - // that for string variables. Use a single quote when comparing with a - // non-string to do what the docs promise. + + if (item_compare_numbers) { + long v1 = get_tv_number(tv1); + long v2 = get_tv_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + if (item_compare_float) { + float_T v1 = get_tv_float(tv1); + float_T v2 = get_tv_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || item_compare_numeric) { p1 = (char_u *)"'"; @@ -15716,7 +15041,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) { @@ -15725,7 +15050,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 *)""; @@ -15834,12 +15159,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { l = argvars[0].vval.v_list; - if (l == NULL || - tv_check_lock(l->lv_lock, - (char_u *)(sort - ? N_("sort() argument") - : N_("uniq() argument")), - true)) { + if (l == NULL + || tv_check_lock(l->lv_lock, + (char_u *)(sort + ? N_("sort() argument") + : N_("uniq() argument")), + true)) { return; } rettv->vval.v_list = l; @@ -15852,6 +15177,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_ic = FALSE; item_compare_numeric = false; + item_compare_numbers = false; + item_compare_float = false; item_compare_func = NULL; item_compare_selfdict = NULL; @@ -15873,6 +15200,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (STRCMP(item_compare_func, "n") == 0) { item_compare_func = NULL; item_compare_numeric = true; + } else if (STRCMP(item_compare_func, "N") == 0) { + item_compare_func = NULL; + item_compare_numbers = true; + } else if (STRCMP(item_compare_func, "f") == 0) { + item_compare_func = NULL; + item_compare_float = true; } else if (STRCMP(item_compare_func, "i") == 0) { item_compare_func = NULL; item_compare_ic = TRUE; @@ -15977,6 +15310,21 @@ static void f_uniq(typval_T *argvars, typval_T *rettv) do_sort_uniq(argvars, rettv, false); } +// +// "reltimefloat()" function +// +static void f_reltimefloat(typval_T *argvars , typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + proftime_T tm; + + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = 0; + if (list2proftime(&argvars[0], &tm) == OK) { + rettv->vval.v_float = ((float_T)tm) / 1000000000; + } +} + /* * "soundfold({word})" function */ @@ -16161,7 +15509,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; } @@ -16292,7 +15640,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); } /* @@ -17025,9 +16373,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; @@ -17213,16 +16561,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; } @@ -17574,6 +16939,13 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = wp->w_width; } +/// "wordcount()" function +static void f_wordcount(typval_T *argvars, typval_T *rettv) +{ + rettv_dict_alloc(rettv); + cursor_pos_info(rettv->vval.v_dict); +} + /// "writefile()" function static void f_writefile(typval_T *argvars, typval_T *rettv) { @@ -17801,21 +17173,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); @@ -17886,28 +17265,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) @@ -17922,30 +17302,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; + } } } } @@ -18027,14 +17421,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 @@ -18071,11 +17457,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; } @@ -18094,56 +17480,70 @@ 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 - size_t todo = val->dv_hashtab.ht_used; - for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - } + dict_set_keys_readonly(val); } } @@ -18152,15 +17552,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); + } } /* @@ -18390,25 +17792,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); } @@ -18446,10 +17846,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; } @@ -18504,8 +17905,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) { @@ -18517,6 +17929,33 @@ long get_tv_number_chk(typval_T *varp, int *denote) return n; } +static float_T get_tv_float(typval_T *varp) +{ + switch (varp->v_type) { + case VAR_NUMBER: + return (float_T)(varp->vval.v_number); + case VAR_FLOAT: + return varp->vval.v_float; + break; + case VAR_FUNC: + EMSG(_("E891: Using a Funcref as a Float")); + break; + case VAR_STRING: + EMSG(_("E892: Using a String as a Float")); + break; + case VAR_LIST: + EMSG(_("E893: Using a List as a Float")); + break; + case VAR_DICT: + EMSG(_("E894: Using a Dictionary as a Float")); + break; + default: + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + return 0; +} + /* * Get the lnum from the first argument. * Also accepts ".", "$", etc., but that only works for the current buffer. @@ -18610,8 +18049,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; @@ -18681,6 +18123,25 @@ static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, in return HI2DI(hi); } +// Get function call environment based on backtrace debug level +static funccall_T *get_funccal(void) +{ + funccall_T *funccal = current_funccal; + if (debug_backtrace_level > 0) { + for (int i = 0; i < debug_backtrace_level; i++) { + funccall_T *temp_funccal = funccal->caller; + if (temp_funccal) { + funccal = temp_funccal; + } else { + // backtrace level overflow. reset to max + debug_backtrace_level = i; + } + } + } + + return funccal; +} + // Find the dict and hashtable used for a variable name. Set "varname" to the // start of name without ':'. static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) @@ -18705,7 +18166,11 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) return &compat_hashtab; } - *d = current_funccal ? ¤t_funccal->l_vars : &globvardict; + if (current_funccal == NULL) { + *d = &globvardict; + } else { + *d = &get_funccal()->l_vars; // l: variable + } goto end; } @@ -18727,9 +18192,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) } else if (*name == 'v') { // v: variable *d = &vimvardict; } else if (*name == 'a' && current_funccal != NULL) { // function argument - *d = ¤t_funccal->l_avars; + *d = &get_funccal()->l_avars; } else if (*name == 'l' && current_funccal != NULL) { // local variable - *d = ¤t_funccal->l_vars; + *d = &get_funccal()->l_vars; } else if (*name == 's' // script variable && current_SID > 0 && current_SID <= ga_scripts.ga_len) { *d = &SCRIPT_SV(current_SID)->sv_dict; @@ -18882,7 +18347,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); @@ -19159,42 +18624,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; } } @@ -19234,6 +18691,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: @@ -19280,8 +18738,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; @@ -19336,7 +18794,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) { @@ -19871,9 +19329,10 @@ void ex_function(exarg_T *eap) if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) || (p[0] == 'i' && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' - && (!ASCII_ISALPHA(p[2]) || - (p[2] == 's')))))) + && (!ASCII_ISALPHA(p[2]) + || (p[2] == 's')))))) { skip_until = vim_strsave((char_u *)"."); + } // Check for ":python <<EOF", ":lua <<EOF", etc. arg = skipwhite(skiptowhite(p)); @@ -20140,11 +19599,12 @@ trans_function_name ( *pp = end; } else { if (!skip && !(flags & TFN_QUIET) && (fdp == NULL - || lv.ll_dict == NULL || - fdp->fd_newkey == NULL)) + || lv.ll_dict == NULL + || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); - else + } else { *pp = end; + } name = NULL; } goto theend; @@ -20950,15 +20410,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 "); } @@ -20981,10 +20448,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); @@ -21037,9 +20504,9 @@ call_user_func ( --RedrawingDisabled; - /* when the function was aborted because of an error, return -1 */ - if ((did_emsg && - (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { + // when the function was aborted because of an error, return -1 + if ((did_emsg + && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { clear_tv(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; @@ -21075,10 +20542,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); @@ -21349,7 +20816,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 *)""; @@ -21928,7 +21395,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); @@ -22224,7 +21699,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); } @@ -22249,6 +21724,18 @@ static void term_resize(uint16_t width, uint16_t height, void *d) pty_process_resize(&data->proc.pty, width, height); } +static inline void term_delayed_free(void **argv) +{ + TerminalJobData *j = argv[0]; + if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { + queue_put(j->events, term_delayed_free, 1, j); + return; + } + + terminal_destroy(j->term); + term_job_data_decref(j); +} + static void term_close(void *d) { TerminalJobData *data = d; @@ -22256,8 +21743,7 @@ static void term_close(void *d) data->exited = true; process_stop((Process *)&data->proc); } - terminal_destroy(data->term); - term_job_data_decref(d); + queue_put(data->events, term_delayed_free, 1, data); } static void term_job_data_decref(TerminalJobData *data) @@ -22504,4 +21990,3 @@ static bool is_watched(dict_T *d) { return d && !QUEUE_EMPTY(&d->watchers); } - diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 79a1341d98..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, @@ -113,15 +118,36 @@ enum { VV_OPTION_TYPE, VV_ERRORS, VV_MSGPACK_TYPES, - VV_LEN, /* number of v: vars */ -}; + VV_EVENT, + 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/event/process.c b/src/nvim/event/process.c index e6012595fd..9bb62891c7 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -187,9 +187,9 @@ int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) // being freed) before we have a chance to get the status. proc->refcount++; LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms, - // Until... - got_int || // interrupted by the user - proc->refcount == 1); // job exited + // Until... + got_int // interrupted by the user + || proc->refcount == 1); // job exited // we'll assume that a user frantically hitting interrupt doesn't like // the current job. Signal that it has to be killed. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ad21e71c51..86f1a16216 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3,6 +3,7 @@ */ #include <assert.h> +#include <float.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> @@ -50,7 +51,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -274,17 +274,26 @@ static int linelen(int *has_tab) static char_u *sortbuf1; static char_u *sortbuf2; -static int sort_ic; /* ignore case */ -static int sort_nr; /* sort on number */ -static int sort_rx; /* sort on regex instead of skipping it */ +static int sort_ic; ///< ignore case +static int sort_nr; ///< sort on number +static int sort_rx; ///< sort on regex instead of skipping it +static int sort_flt; ///< sort on floating number -static int sort_abort; /* flag to indicate if sorting has been interrupted */ +static int sort_abort; ///< flag to indicate if sorting has been interrupted -/* Struct to store info to be sorted. */ +/// Struct to store info to be sorted. typedef struct { - linenr_T lnum; /* line number */ - long start_col_nr; /* starting column number or number */ - long end_col_nr; /* ending column number */ + linenr_T lnum; ///< line number + long start_col_nr; ///< starting column number or number + long end_col_nr; ///< ending column number + union { + struct { + long start_col_nr; ///< starting column number + long end_col_nr; ///< ending column number + } line; + long value; ///< value if sorting by integer + float_T value_flt; ///< value if sorting by float + } st_u; } sorti_T; @@ -303,21 +312,26 @@ static int sort_compare(const void *s1, const void *s2) if (got_int) sort_abort = TRUE; - /* When sorting numbers "start_col_nr" is the number, not the column - * number. */ - if (sort_nr) - result = l1.start_col_nr == l2.start_col_nr ? 0 - : l1.start_col_nr > l2.start_col_nr ? 1 : -1; - else { - /* We need to copy one line into "sortbuf1", because there is no - * guarantee that the first pointer becomes invalid when obtaining the - * second one. */ - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, - l1.end_col_nr - l1.start_col_nr + 1); - sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, - l2.end_col_nr - l2.start_col_nr + 1); - sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0; + // When sorting numbers "start_col_nr" is the number, not the column + // number. + if (sort_nr) { + result = l1.st_u.value == l2.st_u.value + ? 0 : l1.st_u.value > l2.st_u.value + ? 1 : -1; + } else if (sort_flt) { + result = l1.st_u.value_flt == l2.st_u.value_flt + ? 0 : l1.st_u.value_flt > l2.st_u.value_flt + ? 1 : -1; + } else { + // We need to copy one line into "sortbuf1", because there is no + // guarantee that the first pointer becomes invalid when obtaining the + // second one. + STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; + STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0; result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2); @@ -361,7 +375,7 @@ void ex_sort(exarg_T *eap) regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_rx = sort_nr = 0; + sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0; size_t format_found = 0; for (p = eap->arg; *p != NUL; ++p) { @@ -371,7 +385,10 @@ void ex_sort(exarg_T *eap) } else if (*p == 'r') { sort_rx = true; } else if (*p == 'n') { - sort_nr = 2; + sort_nr = 1; + format_found++; + } else if (*p == 'f') { + sort_flt = 1; format_found++; } else if (*p == 'b') { sort_what = STR2NR_BIN + STR2NR_FORCE; @@ -424,7 +441,8 @@ void ex_sort(exarg_T *eap) goto sortend; } - // From here on "sort_nr" is used as a flag for any number sorting. + // From here on "sort_nr" is used as a flag for any integer number + // sorting. sort_nr += sort_what; // Make an array with all line numbers. This avoids having to copy all @@ -453,7 +471,7 @@ void ex_sort(exarg_T *eap) end_col = 0; } - if (sort_nr) { + if (sort_nr || sort_flt) { // Make sure vim_str2nr doesn't read any digits past the end // of the match, by temporarily terminating the string there s2 = s + end_col; @@ -461,29 +479,42 @@ void ex_sort(exarg_T *eap) *s2 = NUL; // Sorting on number: Store the number itself. p = s + start_col; - if (sort_what & STR2NR_HEX) { - s = skiptohex(p); - } else if (sort_what & STR2NR_BIN) { - s = (char_u*) skiptobin((char*) p); - } else { - s = skiptodigit(p); - } - if (s > p && s[-1] == '-') { - // include preceding negative sign - s--; - } - if (*s == NUL) { - // empty line should sort before any number - nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; + if (sort_nr) { + if (sort_what & STR2NR_HEX) { + s = skiptohex(p); + } else if (sort_what & STR2NR_BIN) { + s = (char_u *)skiptobin((char *)p); + } else { + s = skiptodigit(p); + } + if (s > p && s[-1] == '-') { + s--; // include preceding negative sign + } + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value = -MAXLNUM; + } else { + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].st_u.value, NULL, 0); + } } else { - vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].start_col_nr, NULL, 0); + s = skipwhite(p); + if (*s == '+') { + s = skipwhite(s + 1); + } + + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX; + } else { + nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL); + } } *s2 = c; } else { // Store the column to sort at. - nrs[lnum - eap->line1].start_col_nr = start_col; - nrs[lnum - eap->line1].end_col_nr = end_col; + nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col; + nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col; } nrs[lnum - eap->line1].lnum = lnum; @@ -619,13 +650,13 @@ void ex_retab(exarg_T *eap) num_tabs += num_spaces / new_ts; num_spaces -= (num_spaces / new_ts) * new_ts; } - if (curbuf->b_p_et || got_tab || - (num_spaces + num_tabs < len)) { - if (did_undo == FALSE) { - did_undo = TRUE; + if (curbuf->b_p_et || got_tab + || (num_spaces + num_tabs < len)) { + if (did_undo == false) { + did_undo = true; if (u_save((linenr_T)(lnum - 1), - (linenr_T)(lnum + 1)) == FAIL) { - new_line = NULL; /* flag out-of-memory */ + (linenr_T)(lnum + 1)) == FAIL) { + new_line = NULL; // flag out-of-memory break; } } @@ -1326,15 +1357,17 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #endif size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. - + len += is_fish_shell ? sizeof("begin; ""; end") - 1 : sizeof("("")") - 1; - if (itmp != NULL) + if (itmp != NULL) { len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1; - if (otmp != NULL) + } + if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), - char_u *buf = xmalloc(len); + } + char *const buf = xmalloc(len); #if defined(UNIX) // Put delimiters around the command (for concatenated commands) when @@ -1342,19 +1375,19 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; - vim_snprintf((char *)buf, len, fmt, (char *)cmd); + vim_snprintf(buf, len, fmt, (char *)cmd); } else { - STRCPY(buf, cmd); + strncpy(buf, (char *) cmd, len); } if (itmp != NULL) { - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - STRCPY(buf, cmd); + strncpy(buf, cmd, len); if (itmp != NULL) { char_u *p; @@ -1362,55 +1395,56 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) // Don't do this when 'shellquote' is not empty, otherwise the // redirection would be inside the quotes. if (*p_shq == NUL) { - p = vim_strchr(buf, '|'); - if (p != NULL) + p = strchr(buf, '|'); + if (p != NULL) { *p = NUL; + } } - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); if (*p_shq == NUL) { - p = vim_strchr(cmd, '|'); + p = strchr(cmd, '|'); if (p != NULL) { - STRCAT(buf, " "); // Insert a space before the '|' for DOS - STRCAT(buf, p); + strncat(buf, " ", len); // Insert a space before the '|' for DOS + strncat(buf, p, len); } } } #endif - if (otmp != NULL) - append_redir(buf, (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) @@ -1506,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); + } } /* @@ -1586,15 +1623,14 @@ int do_write(exarg_T *eap) } } - /* - * Writing to the current file is not allowed in readonly mode - * and a file name is required. - * "nofile" and "nowrite" buffers cannot be written implicitly either. - */ - if (!other && ( - bt_dontwrite_msg(curbuf) || - check_fname() == FAIL || check_readonly(&eap->forceit, curbuf))) + // Writing to the current file is not allowed in readonly mode + // and a file name is required. + // "nofile" and "nowrite" buffers cannot be written implicitly either. + if (!other && (bt_dontwrite_msg(curbuf) + || check_fname() == FAIL + || check_readonly(&eap->forceit, curbuf))) { goto theend; + } if (!other) { ffname = curbuf->b_ffname; @@ -2090,15 +2126,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; @@ -2223,16 +2257,15 @@ do_ecmd ( delbuf_msg(new_name); /* frees new_name */ goto theend; } - if (buf == curbuf) /* already in new buffer */ - auto_buf = TRUE; - else { - /* - * <VN> We could instead free the synblock - * and re-attach to buffer, perhaps. - */ - if (curwin->w_buffer != NULL && - curwin->w_s == &(curwin->w_buffer->b_s)) + if (buf == curbuf) { // already in new buffer + auto_buf = true; + } else { + // <VN> We could instead free the synblock + // and re-attach to buffer, perhaps. + if (curwin->w_buffer != NULL + && curwin->w_s == &(curwin->w_buffer->b_s)) { curwin->w_s = &(buf->b_s); + } curwin->w_buffer = buf; curbuf = buf; @@ -2259,11 +2292,11 @@ do_ecmd ( curwin->w_pcmark.lnum = 1; curwin->w_pcmark.col = 0; - } else { /* !other_file */ - if ( - (flags & ECMD_ADDBUF) || - check_fname() == FAIL) + } else { // !other_file + if ((flags & ECMD_ADDBUF) + || check_fname() == FAIL) { goto theend; + } oldbuf = (flags & ECMD_OLDBUF); } @@ -2483,7 +2516,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; } @@ -3017,7 +3052,7 @@ void do_sub(exarg_T *eap) // The number of lines joined is the number of lines in the range linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 // plus one extra line if not at the end of file. - + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0; + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); if (joined_lines_count > 1) { do_join(joined_lines_count, FALSE, TRUE, FALSE, true); sub_nsubs = joined_lines_count - 1; @@ -3781,16 +3816,17 @@ skip: EMSG2(_(e_patnotf2), get_search_pat()); } - if (do_ask && hasAnyFolding(curwin)) - /* Cursor position may require updating */ + if (do_ask && hasAnyFolding(curwin)) { + // Cursor position may require updating changed_window_setting(); + } - vim_regfree(regmatch.regprog); + vim_regfree(regmatch.regprog); - // Restore the flag values, they can be used for ":&&". - do_all = save_do_all; - do_ask = save_do_ask; - } + // Restore the flag values, they can be used for ":&&". + do_all = save_do_all; + do_ask = save_do_ask; +} /* * Give message for number of substitutions. @@ -4378,17 +4414,20 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la || (arg[0] == '\\' && arg[1] == '{')) *d++ = '\\'; - for (s = arg; *s; ++s) { - /* - * Replace "|" with "bar" and '"' with "quote" to match the name of - * the tags for these commands. - * Replace "*" with ".*" and "?" with "." to match command line - * completion. - * Insert a backslash before '~', '$' and '.' to avoid their - * special meaning. - */ - if (d - IObuff > IOSIZE - 10) /* getting too long!? */ + // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'. + if (*arg == '(' && arg[1] == '\'') { + arg++; + } + for (s = arg; *s; s++) { + // Replace "|" with "bar" and '"' with "quote" to match the name of + // the tags for these commands. + // Replace "*" with ".*" and "?" with "." to match command line + // completion. + // Insert a backslash before '~', '$' and '.' to avoid their + // special meaning. + if (d - IObuff > IOSIZE - 10) { // getting too long!? break; + } switch (*s) { case '|': STRCPY(d, "bar"); d += 3; @@ -4449,6 +4488,12 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la *d++ = *s; + // If tag contains "({" or "([", tag terminates at the "(". + // This is for help on functions, e.g.: abs({expr}). + if (*s == '(' && (s[1] == '{' || s[1] =='[')) { + break; + } + /* * If tag starts with ', toss everything after a second '. Fixes * CTRL-] on 'option'. (would include the trailing '.'). @@ -5011,7 +5056,9 @@ helptags_one ( /* * Sort the tags. */ - sort_strings((char_u **)ga.ga_data, ga.ga_len); + if (ga.ga_data != NULL) { + sort_strings((char_u **)ga.ga_data, ga.ga_len); + } /* * Check for duplicates. @@ -5779,13 +5826,14 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) switch (cmd_idx) { case SIGNCMD_DEFINE: - if (STRNCMP(last, "texthl", p - last) == 0 || - STRNCMP(last, "linehl", p - last) == 0) + if (STRNCMP(last, "texthl", p - last) == 0 + || STRNCMP(last, "linehl", p - last) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else if (STRNCMP(last, "icon", p - last) == 0) + } else if (STRNCMP(last, "icon", p - last) == 0) { xp->xp_context = EXPAND_FILES; - else + } else { xp->xp_context = EXPAND_NOTHING; + } break; case SIGNCMD_PLACE: if (STRNCMP(last, "name", p - last) == 0) 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 47774f5a99..5fe6209a0a 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -57,23 +57,23 @@ typedef struct scriptitem_S { char_u *sn_name; bool file_id_valid; FileID file_id; - int sn_prof_on; /* TRUE when script is/was profiled */ - int sn_pr_force; /* forceit: profile functions in this script */ - proftime_T sn_pr_child; /* time set when going into first child */ - int sn_pr_nest; /* nesting for sn_pr_child */ - /* profiling the script as a whole */ - int sn_pr_count; /* nr of times sourced */ - proftime_T sn_pr_total; /* time spent in script + children */ - proftime_T sn_pr_self; /* time spent in script itself */ - proftime_T sn_pr_start; /* time at script start */ - proftime_T sn_pr_children; /* time in children after script start */ - /* profiling the script per line */ - garray_T sn_prl_ga; /* things stored for every line */ - proftime_T sn_prl_start; /* start time for current line */ - proftime_T sn_prl_children; /* time spent in children for this line */ - proftime_T sn_prl_wait; /* wait start time for current line */ - int sn_prl_idx; /* index of line being timed; -1 if none */ - int sn_prl_execed; /* line being timed was executed */ + bool sn_prof_on; ///< true when script is/was profiled + int sn_pr_force; ///< forceit: profile functions in this script + proftime_T sn_pr_child; ///< time set when going into first child + int sn_pr_nest; ///< nesting for sn_pr_child + // profiling the script as a whole + int sn_pr_count; ///< nr of times sourced + proftime_T sn_pr_total; ///< time spent in script + children + proftime_T sn_pr_self; ///< time spent in script itself + proftime_T sn_pr_start; ///< time at script start + proftime_T sn_pr_children; ///< time in children after script start + // profiling the script per line + garray_T sn_prl_ga; ///< things stored for every line + proftime_T sn_prl_start; ///< start time for current line + proftime_T sn_prl_children; ///< time spent in children for this line + proftime_T sn_prl_wait; ///< wait start time for current line + linenr_T sn_prl_idx; ///< index of line being timed; -1 if none + int sn_prl_execed; ///< line being timed was executed } scriptitem_T; static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; @@ -81,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; /* Struct used in sn_prl_ga for every line of a script. */ typedef struct sn_prl_S { - int snp_count; /* nr of times line was executed */ - proftime_T sn_prl_total; /* time spent in a line + children */ - proftime_T sn_prl_self; /* time spent in a line itself */ + int snp_count; ///< nr of times line was executed + proftime_T sn_prl_total; ///< time spent in a line + children + proftime_T sn_prl_self; ///< time spent in a line itself } sn_prl_T; /* @@ -93,18 +93,18 @@ typedef struct sn_prl_S { * sourcing can be done recursively. */ struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ + FILE *fp; ///< opened file for sourcing + char_u *nextline; ///< if not NULL: line that was read ahead + int finished; ///< ":finish" used #if defined(USE_CRNL) - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ + int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; ///< TRUE if LF found after CR-LF #endif - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ - vimconv_T conv; /* type of conversion */ + linenr_T breakpoint; ///< next line with breakpoint or zero + char_u *fname; ///< name of sourced file + int dbg_tick; ///< debug_tick when breakpoint was set + int level; ///< top nesting level of sourced file + vimconv_T conv; ///< type of conversion }; # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) @@ -144,6 +144,10 @@ void do_debug(char_u *cmd) #define CMD_FINISH 4 #define CMD_QUIT 5 #define CMD_INTERRUPT 6 +#define CMD_BACKTRACE 7 +#define CMD_FRAME 8 +#define CMD_UP 9 +#define CMD_DOWN 10 ++RedrawingDisabled; /* don't redisplay the window */ @@ -185,6 +189,7 @@ void do_debug(char_u *cmd) ignore_script = TRUE; } + xfree(cmdline); cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); if (typeahead_saved) { @@ -194,6 +199,7 @@ void do_debug(char_u *cmd) ex_normal_busy = save_ex_normal_busy; cmdline_row = msg_row; + msg_starthere(); if (cmdline != NULL) { /* If this is a debug command, set "last_cmd". * If not, reset "last_cmd". @@ -210,8 +216,15 @@ void do_debug(char_u *cmd) case 's': last_cmd = CMD_STEP; tail = "tep"; break; - case 'f': last_cmd = CMD_FINISH; - tail = "inish"; + case 'f': + last_cmd = 0; + if (p[1] == 'r') { + last_cmd = CMD_FRAME; + tail = "rame"; + } else { + last_cmd = CMD_FINISH; + tail = "inish"; + } break; case 'q': last_cmd = CMD_QUIT; tail = "uit"; @@ -219,6 +232,26 @@ void do_debug(char_u *cmd) case 'i': last_cmd = CMD_INTERRUPT; tail = "nterrupt"; break; + case 'b': + last_cmd = CMD_BACKTRACE; + if (p[1] == 't') { + tail = "t"; + } else { + tail = "acktrace"; + } + break; + case 'w': + last_cmd = CMD_BACKTRACE; + tail = "here"; + break; + case 'u': + last_cmd = CMD_UP; + tail = "p"; + break; + case 'd': + last_cmd = CMD_DOWN; + tail = "own"; + break; default: last_cmd = 0; } if (last_cmd != 0) { @@ -228,8 +261,9 @@ void do_debug(char_u *cmd) ++p; ++tail; } - if (ASCII_ISALPHA(*p)) + if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) { last_cmd = 0; + } } } @@ -259,7 +293,28 @@ void do_debug(char_u *cmd) /* Do not repeat ">interrupt" cmd, continue stepping. */ last_cmd = CMD_STEP; break; + case CMD_BACKTRACE: + do_showbacktrace(cmd); + continue; + case CMD_FRAME: + if (*p == NUL) { + do_showbacktrace(cmd); + } else { + p = skipwhite(p); + do_setdebugtracelevel(p); + } + continue; + case CMD_UP: + debug_backtrace_level++; + do_checkbacktracelevel(); + continue; + case CMD_DOWN: + debug_backtrace_level--; + do_checkbacktracelevel(); + continue; } + // Going out reset backtrace_level + debug_backtrace_level = 0; break; } @@ -269,10 +324,8 @@ void do_debug(char_u *cmd) (void)do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET); debug_break_level = n; - - xfree(cmdline); } - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); } xfree(cmdline); @@ -281,7 +334,7 @@ void do_debug(char_u *cmd) redraw_all_later(NOT_VALID); need_wait_return = FALSE; msg_scroll = save_msg_scroll; - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); State = save_State; did_emsg = save_did_emsg; cmd_silent = save_cmd_silent; @@ -294,6 +347,78 @@ void do_debug(char_u *cmd) debug_did_msg = TRUE; } +static int get_maxbacktrace_level(void) +{ + int maxbacktrace = 0; + + if (sourcing_name != NULL) { + char *p = (char *)sourcing_name; + char *q; + while ((q = strstr(p, "..")) != NULL) { + p = q + 2; + maxbacktrace++; + } + } + return maxbacktrace; +} + +static void do_setdebugtracelevel(char_u *arg) +{ + int level = atoi((char *)arg); + if (*arg == '+' || level < 0) { + debug_backtrace_level += level; + } else { + debug_backtrace_level = level; + } + + do_checkbacktracelevel(); +} + +static void do_checkbacktracelevel(void) +{ + if (debug_backtrace_level < 0) { + debug_backtrace_level = 0; + MSG(_("frame is zero")); + } else { + int max = get_maxbacktrace_level(); + if (debug_backtrace_level > max) { + debug_backtrace_level = max; + smsg(_("frame at highest level: %d"), max); + } + } +} + +static void do_showbacktrace(char_u *cmd) +{ + if (sourcing_name != NULL) { + int i = 0; + int max = get_maxbacktrace_level(); + char *cur = (char *)sourcing_name; + while (!got_int) { + char *next = strstr(cur, ".."); + if (next != NULL) { + *next = NUL; + } + if (i == max - debug_backtrace_level) { + smsg("->%d %s", max - i, cur); + } else { + smsg(" %d %s", max - i, cur); + } + i++; + if (next == NULL) { + break; + } + *next = '.'; + cur = next + 2; + } + } + if (sourcing_lnum != 0) { + smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd); + } else { + smsg(_("cmd: %s"), cmd); + } +} + /* * ":debug". */ @@ -392,12 +517,12 @@ int dbg_check_skipped(exarg_T *eap) * This is a grow-array of structs. */ struct debuggy { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC or DBG_FILE */ - char_u *dbg_name; /* function or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ + int dbg_nr; ///< breakpoint number + int dbg_type; ///< DBG_FUNC or DBG_FILE + char_u *dbg_name; ///< function or file name + regprog_T *dbg_prog; ///< regexp program + linenr_T dbg_lnum; ///< line number in function or file + int dbg_forceit; ///< ! used }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; @@ -432,14 +557,12 @@ dbg_parsearg ( bp = &DEBUGGY(gap, gap->ga_len); - /* Find "func" or "file". */ - if (STRNCMP(p, "func", 4) == 0) + // Find "func" or "file". + if (STRNCMP(p, "func", 4) == 0) { bp->dbg_type = DBG_FUNC; - else if (STRNCMP(p, "file", 4) == 0) + } else if (STRNCMP(p, "file", 4) == 0) { bp->dbg_type = DBG_FILE; - else if ( - gap != &prof_ga && - STRNCMP(p, "here", 4) == 0) { + } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) { if (curbuf->b_ffname == NULL) { EMSG(_(e_noname)); return FAIL; @@ -452,16 +575,15 @@ dbg_parsearg ( } p = skipwhite(p + 4); - /* Find optional line number. */ - if (here) + // Find optional line number. + if (here) { bp->dbg_lnum = curwin->w_cursor.lnum; - else if ( - gap != &prof_ga && - ascii_isdigit(*p)) { + } else if (gap != &prof_ga && ascii_isdigit(*p)) { bp->dbg_lnum = getdigits_long(&p); p = skipwhite(p); - } else + } else { bp->dbg_lnum = 0; + } /* Find the function or file name. Don't accept a function name with (). */ if ((!here && *p == NUL) @@ -563,13 +685,14 @@ void ex_breakdel(exarg_T *eap) } if (ascii_isdigit(*eap->arg)) { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (int i = 0; i < gap->ga_len; ++i) + // ":breakdel {nr}" + nr = atoi((char *)eap->arg); + for (int i = 0; i < gap->ga_len; ++i) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; break; } + } } else if (*eap->arg == '*') { todel = 0; del_all = TRUE; @@ -602,11 +725,13 @@ void ex_breakdel(exarg_T *eap) --gap->ga_len; if (todel < gap->ga_len) memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - if (eap->cmdidx == CMD_breakdel) + (size_t)(gap->ga_len - todel) * sizeof(struct debuggy)); + if (eap->cmdidx == CMD_breakdel) { ++debug_tick; - if (!del_all) + } + if (!del_all) { break; + } } /* If all breakpoints were removed clear the array. */ @@ -697,14 +822,13 @@ debuggy_find ( /* Skip entries that are not useful or are for a line that is beyond * an already found breakpoint. */ bp = &DEBUGGY(gap, i); - if (((bp->dbg_type == DBG_FILE) == file && ( - gap == &prof_ga || - (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) { - /* - * 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. - */ + if (((bp->dbg_type == DBG_FILE) == file + && (gap == &prof_ga + || (bp->dbg_lnum > after + && (lnum == 0 || bp->dbg_lnum < lnum))))) { + // 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; if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) { @@ -810,8 +934,8 @@ void ex_pydo3(exarg_T *eap) /* Command line expansion for :profile. */ static enum { - PEXP_SUBCMD, /* expand :profile sub-commands */ - PEXP_FUNC /* expand :profile func {funcname} */ + PEXP_SUBCMD, ///< expand :profile sub-commands + PEXP_FUNC ///< expand :profile func {funcname} } pexpand_what; static char *pexpand_cmds[] = { @@ -892,7 +1016,7 @@ static void profile_reset(void) for (int id = 1; id <= script_items.ga_len; id++) { scriptitem_T *si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { - si->sn_prof_on = 0; + si->sn_prof_on = false; si->sn_pr_force = 0; si->sn_pr_child = profile_zero(); si->sn_pr_nest = 0; @@ -949,7 +1073,7 @@ static void profile_init(scriptitem_T *si) ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); si->sn_prl_idx = -1; - si->sn_prof_on = TRUE; + si->sn_prof_on = true; si->sn_pr_nest = 0; } @@ -1242,28 +1366,31 @@ static void add_bufnum(int *bufnrs, int *bufnump, int nr) *bufnump = *bufnump + 1; } -/* - * Return TRUE if any buffer was changed and cannot be abandoned. - * That changed buffer becomes the current buffer. - */ -int -check_changed_any ( - int hidden /* Only check hidden buffers */ -) +/// Check if any buffer was changed and cannot be abandoned. +/// That changed buffer becomes the current buffer. +/// When "unload" is true the current buffer is unloaded instead of making it +/// hidden. This is used for ":q!". +/// +/// @param[in] hidden specifies whether to check only hidden buffers. +/// @param[in] unload specifies whether to unload, instead of hide, the buffer. +/// +/// @returns true if any buffer is changed and cannot be abandoned +int check_changed_any(bool hidden, bool unload) { - int ret = FALSE; + bool ret = false; int save; int i; int bufnum = 0; - int bufcount = 0; + size_t bufcount = 0; int *bufnrs; FOR_ALL_BUFFERS(buf) { ++bufcount; } - if (bufcount == 0) - return FALSE; + if (bufcount == 0) { + return false; + } bufnrs = xmalloc(sizeof(*bufnrs) * bufcount); @@ -1347,9 +1474,10 @@ check_changed_any ( } buf_found: - /* Open the changed buffer in the current window. */ - if (buf != curbuf) - set_curbuf(buf, DOBUF_GOTO); + // Open the changed buffer in the current window. + if (buf != curbuf) { + set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO); + } theend: xfree(bufnrs); @@ -1520,7 +1648,7 @@ do_arglist ( didone = TRUE; xfree(ARGLIST[match].ae_fname); memmove(ARGLIST + match, ARGLIST + match + 1, - (ARGCOUNT - match - 1) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T)); --ALIST(curwin)->al_ga.ga_len; if (curwin->w_arg_idx > match) --curwin->w_arg_idx; @@ -1537,9 +1665,7 @@ do_arglist ( int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); - if (i == FAIL) - return FAIL; - if (exp_count == 0) { + if (i == FAIL || exp_count == 0) { EMSG(_(e_nomatch)); return FAIL; } @@ -1702,10 +1828,11 @@ void ex_argument(exarg_T *eap) { int i; - if (eap->addr_count > 0) - i = eap->line2 - 1; - else + if (eap->addr_count > 0) { + i = (int)eap->line2 - 1; + } else { i = curwin->w_arg_idx; + } do_argfile(eap, i); } @@ -1844,22 +1971,25 @@ void ex_argadd(exarg_T *eap) void ex_argdelete(exarg_T *eap) { if (eap->addr_count > 0) { - /* ":1,4argdel": Delete all arguments in the range. */ - if (eap->line2 > ARGCOUNT) + // ":1,4argdel": Delete all arguments in the range. + if (eap->line2 > ARGCOUNT) { eap->line2 = ARGCOUNT; - int n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) + } + linenr_T n = eap->line2 - eap->line1 + 1; + if (*eap->arg != NUL || n <= 0) { EMSG(_(e_invarg)); - else { - for (int i = eap->line1; i <= eap->line2; ++i) + } else { + for (linenr_T i = eap->line1; i <= eap->line2; ++i) { xfree(ARGLIST[i - 1].ae_fname); + } memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T))); - ALIST(curwin)->al_ga.ga_len -= n; - if (curwin->w_arg_idx >= eap->line2) - curwin->w_arg_idx -= n; - else if (curwin->w_arg_idx > eap->line1) - curwin->w_arg_idx = eap->line1; + (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T)); + ALIST(curwin)->al_ga.ga_len -= (int)n; + if (curwin->w_arg_idx >= eap->line2) { + curwin->w_arg_idx -= (int)n; + } else if (curwin->w_arg_idx > eap->line1) { + curwin->w_arg_idx = (int)eap->line1; + } } } else if (*eap->arg == NUL) EMSG(_(e_argreq)); @@ -1909,7 +2039,7 @@ void ex_listdo(exarg_T *eap) } break; case CMD_argdo: - i = eap->line1 - 1; + i = (int)eap->line1 - 1; break; default: break; @@ -1932,8 +2062,8 @@ void ex_listdo(exarg_T *eap) if (buf != NULL) { goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum); } - } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || - eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { qf_size = qf_get_size(eap); assert(eap->line1 >= 0); if (qf_size == 0 || (size_t)eap->line1 > qf_size) { @@ -1942,10 +2072,11 @@ void ex_listdo(exarg_T *eap) ex_cc(eap); buf = curbuf; - i = eap->line1 - 1; + i = (int)eap->line1 - 1; if (eap->addr_count <= 0) { // Default to all quickfix/location list entries. - eap->line2 = qf_size; + assert(qf_size < MAXLNUM); + eap->line2 = (linenr_T)qf_size; } } } else { @@ -2034,8 +2165,8 @@ void ex_listdo(exarg_T *eap) } } - if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || - eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { assert(i >= 0); if ((size_t)i >= qf_size || i >= eap->line2) { break; @@ -2098,7 +2229,7 @@ alist_add_list ( after = ARGCOUNT; if (after < ARGCOUNT) memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (ARGCOUNT - after) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); for (int i = 0; i < count; ++i) { ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); @@ -2367,8 +2498,9 @@ static FILE *fopen_noinh_readbin(char *filename) # ifdef HAVE_FD_CLOEXEC { int fdflags = fcntl(fd_tmp, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + } } # endif @@ -2571,7 +2703,7 @@ do_source ( while (script_items.ga_len < current_SID) { ++script_items.ga_len; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; } si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; @@ -3162,27 +3294,30 @@ 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(); +# elif defined(LC_MESSAGES) + loc = get_locale_val(LC_MESSAGES); # else - loc = (char_u *)get_locale_val(LC_MESSAGES); + // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE + loc = get_locale_val(LC_CTYPE); # 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); } @@ -3386,8 +3521,8 @@ static void script_host_execute(char *name, exarg_T *eap) // script list_append_string(args, script ? script : eap->arg, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3403,16 +3538,16 @@ static void script_host_execute_file(char *name, exarg_T *eap) // filename list_append_string(args, buffer, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { list_T *args = list_alloc(); - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); list_append_string(args, eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3a24f194c1..870284a0f7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1654,16 +1654,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, * If we got a line, but no command, then go to the line. * If we find a '|' or '\n' we set ea.nextcmd. */ - if (*ea.cmd == NUL || *ea.cmd == '"' || - (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { - /* - * strange vi behaviour: - * ":3" jumps to line 3 - * ":3|..." prints line 3 - * ":|" prints current line - */ - if (ea.skip) /* skip this if inside :if */ + if (*ea.cmd == NUL || *ea.cmd == '"' + || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { + // strange vi behaviour: + // ":3" jumps to line 3 + // ":3|..." prints line 3 + // ":|" prints current line + if (ea.skip) { // skip this if inside :if goto doend; + } if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) { ea.cmdidx = CMD_print; ea.argt = RANGE | COUNT | TRLBAR; @@ -1826,9 +1825,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, correct_range(&ea); - if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy) { - /* Put the first line at the start of a closed fold, put the last line - * at the end of a closed fold. */ + if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy + && ea.addr_type == ADDR_LINES) { + // Put the first line at the start of a closed fold, put the last line + // at the end of a closed fold. (void)hasFolding(ea.line1, &ea.line1, NULL); (void)hasFolding(ea.line2, NULL, &ea.line2); } @@ -2823,10 +2823,11 @@ set_one_cmd_context ( } } - /* no arguments allowed */ - if (!(ea.argt & EXTRA) && *arg != NUL && - vim_strchr((char_u *)"|\"", *arg) == NULL) + // no arguments allowed + if (!(ea.argt & EXTRA) && *arg != NUL + && vim_strchr((char_u *)"|\"", *arg) == NULL) { return NULL; + } /* Find start of last argument (argument just before cursor): */ p = buff; @@ -2958,8 +2959,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; @@ -4553,7 +4557,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, char_u *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); + replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false, + CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { /* Can't replace termcodes - try using the string as is */ rep_buf = vim_strsave(rep); @@ -4767,14 +4772,15 @@ static void uc_list(char_u *name, size_t name_len) IObuff[len++] = ' '; } while (len < 11); - /* Address Type */ - for (j = 0; addr_type_complete[j].expand != -1; ++j) - if (addr_type_complete[j].expand != ADDR_LINES && - addr_type_complete[j].expand == cmd->uc_addr_type) { + // Address Type + for (j = 0; addr_type_complete[j].expand != -1; j++) { + if (addr_type_complete[j].expand != ADDR_LINES + && addr_type_complete[j].expand == cmd->uc_addr_type) { STRCPY(IObuff + len, addr_type_complete[j].name); len += (int)STRLEN(IObuff + len); break; } + } do { IObuff[len++] = ' '; @@ -5499,7 +5505,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, int *addr_type_arg) { int i, a, b; - for (i = 0; addr_type_complete[i].expand != -1; ++i) { + + for (i = 0; addr_type_complete[i].expand != -1; i++) { a = (int)STRLEN(addr_type_complete[i].name) == vallen; b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; if (a && b) { @@ -5510,8 +5517,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, if (addr_type_complete[i].expand == -1) { char_u *err = value; - for (i = 0; err[i] == NUL || !ascii_iswhite(err[i]); i++) - ; + + for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} err[i] = NUL; EMSG2(_("E180: Invalid address type value: %s"), err); return FAIL; @@ -5648,12 +5655,13 @@ static void ex_quit(exarg_T *eap) wp = curwin; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || - (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) { return; + } /* @@ -5663,10 +5671,10 @@ static void ex_quit(exarg_T *eap) exiting = TRUE; if ((!P_HID(curbuf) && check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) + || check_more(true, eap->forceit) == FAIL + || (only_one_window() && check_changed_any(eap->forceit, true))) { not_exiting(); } else { // quit last window @@ -5715,9 +5723,10 @@ static void ex_quit_all(exarg_T *eap) if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) return; - exiting = TRUE; - if (eap->forceit || !check_changed_any(FALSE)) + exiting = true; + if (eap->forceit || !check_changed_any(false, false)) { getout(0); + } not_exiting(); } @@ -6002,21 +6011,22 @@ static void ex_exit(exarg_T *eap) if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) return; - /* - * if more files or windows we won't exit - */ - if (check_more(FALSE, eap->forceit) == OK && only_one_window()) - exiting = TRUE; - if ( ((eap->cmdidx == CMD_wq - || curbufIsChanged()) - && do_write(eap) == FAIL) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { + // if more files or windows we won't exit + if (check_more(false, eap->forceit) == OK && only_one_window()) { + exiting = true; + } + if (((eap->cmdidx == CMD_wq + || curbufIsChanged()) + && do_write(eap) == FAIL) + || check_more(true, eap->forceit) == FAIL + || (only_one_window() && check_changed_any(eap->forceit, false))) { not_exiting(); } else { - if (only_one_window()) /* quit last window, exit Vim */ + if (only_one_window()) { + // quit last window, exit Vim getout(0); - /* Quit current window, may free the buffer. */ + } + // Quit current window, may free the buffer. win_close(curwin, !P_HID(curwin->w_buffer)); } } @@ -6290,10 +6300,8 @@ void ex_splitview(exarg_T *eap) if (eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_tabfind || eap->cmdidx == CMD_tabnew) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab - : eap->addr_count == 0 ? 0 - : (int)eap->line2 + 1) != FAIL) { - apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf); + if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 + ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); @@ -6816,36 +6824,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; @@ -6886,10 +6913,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) @@ -7033,9 +7075,9 @@ static void ex_operators(exarg_T *eap) oa.start.lnum = eap->line1; oa.end.lnum = eap->line2; oa.line_count = eap->line2 - eap->line1 + 1; - oa.motion_type = MLINE; - virtual_op = FALSE; - if (eap->cmdidx != CMD_yank) { /* position cursor for undo */ + oa.motion_type = kMTLineWise; + virtual_op = false; + if (eap->cmdidx != CMD_yank) { // position cursor for undo setpcmark(); curwin->w_cursor.lnum = eap->line1; beginline(BL_SOL | BL_FIX); @@ -7423,10 +7465,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) { @@ -7437,32 +7479,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; @@ -7506,8 +7550,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) @@ -7552,15 +7597,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 @@ -9145,16 +9189,15 @@ static char *get_view_file(int c) */ int put_eol(FILE *fd) { - if ( -#ifdef USE_CRNL - ( -# ifdef MKSESSION_NL - !mksession_nl && -# endif - (putc('\r', fd) < 0)) || +#if defined(USE_CRNL) && defined(MKSESSION_NL) + if ((!mksession_nl && putc('\r', fd) < 0) || putc('\n', fd) < 0) { +#elif defined(USE_CRNL) + if (putc('\r', fd) < 0 || putc('\n', fd) < 0) { +#else + if (putc('\n', fd) < 0) { #endif - (putc('\n', fd) < 0)) return FAIL; + } return OK; } @@ -9456,12 +9499,14 @@ static void ex_folddo(exarg_T *eap) static void ex_terminal(exarg_T *eap) { - // We will call termopen() with ['shell'] if not given a {cmd}. - char *name = (char *)p_sh; + char *name = (char *)p_sh; // Default to 'shell' if {cmd} is not given. + bool mustfree = false; char *lquote = "['"; char *rquote = "']"; + if (*eap->arg != NUL) { name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); + mustfree = true; lquote = rquote = "\""; } @@ -9471,7 +9516,7 @@ static void ex_terminal(exarg_T *eap) eap->forceit==TRUE ? "!" : "", lquote, name, rquote); do_cmdline_cmd(ex_cmd); - if (name != (char *)p_sh) { + if (mustfree) { xfree(name); } } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index a5a4edbbbf..dbfc64e2f1 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -19,6 +19,20 @@ #define EXMODE_NORMAL 1 #define EXMODE_VIM 2 +/// The scope of a working-directory command like `:cd`. +/// +/// Scopes are enumerated from lowest to highest. When adding a scope make sure +/// to update all functions using scopes as well, such as the implementation of +/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes +/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead. +typedef enum { + kCdScopeWindow, ///< Affects one window. + kCdScopeTab, ///< Affects one tab page. + kCdScopeGlobal, ///< Affects the entire instance of Neovim. +} CdScope; +#define MIN_CD_SCOPE kCdScopeWindow +#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..82d4c2b2d5 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); @@ -403,14 +403,15 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should && (p[3] == ':' || (ascii_isdigit(p[3]) && p[4] == ':')))))) { - if (*p == NUL || p == mesg) - STRCAT(val, mesg); /* 'E123' missing or at beginning */ - else { - /* '"filename" E123: message text' */ - if (mesg[0] != '"' || p-2 < &mesg[1] || - p[-2] != '"' || p[-1] != ' ') - /* "E123:" is part of the file name. */ + if (*p == NUL || p == mesg) { + STRCAT(val, mesg); // 'E123' missing or at beginning + } else { + // '"filename" E123: message text' + if (mesg[0] != '"' || p-2 < &mesg[1] + || p[-2] != '"' || p[-1] != ' ') { + // "E123:" is part of the file name. continue; + } STRCAT(val, p); p[-2] = NUL; @@ -569,17 +570,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 +617,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 +1375,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, @@ -1556,22 +1566,21 @@ void ex_endtry(exarg_T *eap) void *rettv = NULL; struct condstack *cstack = eap->cstack; - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) + if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = (char_u *)N_("E602: :endtry without :try"); - else { - /* - * Don't do something after an error, interrupt or throw in the try - * block, catch clause, or finally clause preceding this ":endtry" or - * when an error or interrupt occurred after a ":continue", ":break", - * ":return", or ":finish" in a try block or catch clause preceding this - * ":endtry" or when the try block never got active (because of an - * inactive surrounding conditional or after an error or interrupt or - * throw) or when there is a surrounding conditional and it has been - * made inactive by a ":continue", ":break", ":return", or ":finish" in - * the finally clause. The latter case need not be tested since then - * anything pending has already been discarded. */ - skip = did_emsg || got_int || did_throw || - !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + } else { + // Don't do something after an error, interrupt or throw in the try + // block, catch clause, or finally clause preceding this ":endtry" or + // when an error or interrupt occurred after a ":continue", ":break", + // ":return", or ":finish" in a try block or catch clause preceding this + // ":endtry" or when the try block never got active (because of an + // inactive surrounding conditional or after an error or interrupt or + // throw) or when there is a surrounding conditional and it has been + // made inactive by a ":continue", ":break", ":return", or ":finish" in + // the finally clause. The latter case need not be tested since then + // anything pending has already been discarded. + skip = (did_emsg || got_int || did_throw + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 39bff9b2ad..a4e5a4dcd7 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -289,7 +289,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (ccline.cmdbuff != NULL) { // Put line in history buffer (":" and "=" only when it was typed). - if (ccline.cmdlen && s->firstc != NUL + if (s->histype != HIST_INVALID + && ccline.cmdlen + && s->firstc != NUL && (s->some_key_typed || s->histype == HIST_SEARCH)) { add_to_history(s->histype, ccline.cmdbuff, true, s->histype == HIST_SEARCH ? s->firstc : NUL); @@ -622,8 +624,8 @@ static int command_line_execute(VimState *state, int key) // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' - || (ccline.cmdfirstc == '=' && - KeyTyped))) { + || (ccline.cmdfirstc == '=' + && KeyTyped))) { vungetc(s->c); s->c = Ctrl_BSL; } else if (s->c == 'e') { @@ -1130,7 +1132,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); @@ -1268,7 +1270,7 @@ static int command_line_handle_key(CommandLineState *s) case K_KPAGEUP: case K_PAGEDOWN: case K_KPAGEDOWN: - if (hislen == 0 || s->firstc == NUL) { + if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) { // no history return command_line_not_changed(s); } @@ -2424,20 +2426,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; @@ -3984,6 +3983,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, char_u *s, *e; int flags = flagsarg; int ret; + bool did_curdir = false; /* for ":set path=" and ":set tags=" halve backslashes for escaped * space */ @@ -3992,7 +3992,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, if (pat[i] == '\\' && pat[i + 1] == ' ') STRMOVE(pat + i, pat + i + 1); - flags |= EW_FILE | EW_EXEC; + flags |= EW_FILE | EW_EXEC | EW_SHELLCMD; bool mustfree = false; // Track memory allocation for *path. /* For an absolute name we don't use $PATH. */ @@ -4012,12 +4012,24 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, /* * Go over all directories in $PATH. Expand matches in that directory and - * collect them in "ga". + * collect them in "ga". When "." is not in $PATH also expaned for the + * current directory, to find "subdir/cmd". */ ga_init(&ga, (int)sizeof(char *), 10); - for (s = path; *s != NUL; s = e) { - if (*s == ' ') - ++s; /* Skip space used for absolute path name. */ + for (s = path; ; s = e) { + if (*s == NUL) { + if (did_curdir) { + break; + } + // Find directories in the current directory, path is empty. + did_curdir = true; + } else if (*s == '.') { + did_curdir = true; + } + + if (*s == ' ') { + s++; // Skip space used for absolute path name. + } e = vim_strchr(s, ':'); if (e == NULL) @@ -4275,20 +4287,33 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) * Command line history stuff * *********************************/ -/* - * Translate a history character to the associated type number. - */ -static int hist_char2type(int c) +/// Translate a history character to the associated type number +static HistoryType hist_char2type(const int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - if (c == ':') - return HIST_CMD; - if (c == '=') - return HIST_EXPR; - if (c == '@') - return HIST_INPUT; - if (c == '>') - return HIST_DEBUG; - return HIST_SEARCH; /* must be '?' or '/' */ + switch (c) { + case ':': { + return HIST_CMD; + } + case '=': { + return HIST_EXPR; + } + case '@': { + return HIST_INPUT; + } + case '>': { + return HIST_DEBUG; + } + case '/': + case '?': { + return HIST_SEARCH; + } + default: { + return HIST_INVALID; + } + } + // Silence -Wreturn-type + return 0; } /* @@ -4457,28 +4482,38 @@ in_history ( return false; } -/* - * Convert history name (from table above) to its HIST_ equivalent. - * When "name" is empty, return "cmd" history. - * Returns -1 for unknown history name. - */ -int get_histtype(char_u *name) +/// Convert history name to its HIST_ equivalent +/// +/// Names are taken from the table above. When `name` is empty returns currently +/// active history or HIST_DEFAULT, depending on `return_default` argument. +/// +/// @param[in] name Converted name. +/// @param[in] len Name length. +/// @param[in] return_default Determines whether HIST_DEFAULT should be +/// returned or value based on `ccline.cmdfirstc`. +/// +/// @return Any value from HistoryType enum, including HIST_INVALID. May not +/// return HIST_DEFAULT unless return_default is true. +HistoryType get_histtype(const char_u *const name, const size_t len, + const bool return_default) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int i; - int len = (int)STRLEN(name); - - /* No argument: use current history. */ - if (len == 0) - return hist_char2type(ccline.cmdfirstc); + // No argument: use current history. + if (len == 0) { + return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc); + } - for (i = 0; history_names[i] != NULL; ++i) - if (STRNICMP(name, history_names[i], len) == 0) + for (HistoryType i = 0; history_names[i] != NULL; i++) { + if (STRNICMP(name, history_names[i], len) == 0) { return i; + } + } - if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL) + if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) { return hist_char2type(name[0]); + } - return -1; + return HIST_INVALID; } static int last_maptick = -1; /* last seen maptick */ @@ -4499,8 +4534,10 @@ add_to_history ( histentry_T *hisptr; int len; - if (hislen == 0) /* no history */ + if (hislen == 0 || histype == HIST_INVALID) { // no history return; + } + assert(histype != HIST_DEFAULT); if (cmdmod.keeppatterns && histype == HIST_SEARCH) return; @@ -4850,23 +4887,20 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - i = *end; - *end = NUL; - histype1 = get_histtype(arg); - if (histype1 == -1) { - if (STRNICMP(arg, "all", STRLEN(arg)) == 0) { + histype1 = get_histtype(arg, end - arg, false); + if (histype1 == HIST_INVALID) { + if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; histype2 = HIST_COUNT-1; } else { - *end = i; EMSG(_(e_trailing)); return; } } else histype2 = histype1; - *end = i; - } else + } else { end = arg; + } if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { EMSG(_(e_trailing)); return; @@ -4974,7 +5008,6 @@ static int ex_window(void) win_T *wp; int i; linenr_T lnum; - int histtype; garray_T winsizes; char_u typestr[2]; int save_restart_edit = restart_edit; @@ -5023,7 +5056,7 @@ static int ex_window(void) /* Showing the prompt may have set need_wait_return, reset it. */ need_wait_return = FALSE; - histtype = hist_char2type(cmdwin_type); + const int histtype = hist_char2type(cmdwin_type); if (histtype == HIST_CMD || histtype == HIST_DEBUG) { if (p_wc == TAB) { add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT); @@ -5038,7 +5071,7 @@ static int ex_window(void) /* Fill the buffer with the history. */ init_history(); - if (hislen > 0) { + if (hislen > 0 && histtype != HIST_INVALID) { i = hisidx[histtype]; if (i >= 0) { lnum = 0; diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 21da8b9d42..24eebdc303 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -27,11 +27,13 @@ /// Present history tables typedef enum { - HIST_CMD, ///< Colon commands. - HIST_SEARCH, ///< Search commands. - HIST_EXPR, ///< Expressions (e.g. from entering = register). - HIST_INPUT, ///< input() lines. - HIST_DEBUG, ///< Debug commands. + HIST_DEFAULT = -2, ///< Default (current) history. + HIST_INVALID = -1, ///< Unknown history. + HIST_CMD = 0, ///< Colon commands. + HIST_SEARCH, ///< Search commands. + HIST_EXPR, ///< Expressions (e.g. from entering = register). + HIST_INPUT, ///< input() lines. + HIST_DEBUG, ///< Debug commands. } HistoryType; /// Number of history tables diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c index 47a132c0d0..61e17128ea 100644 --- a/src/nvim/farsi.c +++ b/src/nvim/farsi.c @@ -100,8 +100,9 @@ static char_u toF_Xor_X_(int c) case F_HE : tempc = _HE; - if (p_ri && - (curwin->w_cursor.col + 1 < (colnr_T)STRLEN(get_cursor_line_ptr()))) { + if (p_ri + && (curwin->w_cursor.col + 1 + < (colnr_T)STRLEN(get_cursor_line_ptr()))) { inc_cursor(); if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { tempc = _HE_; @@ -526,8 +527,8 @@ static void chg_l_toXor_X(void) { char_u tempc; - if ((curwin->w_cursor.col != 0) && - (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) { + if ((curwin->w_cursor.col != 0) + && (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) { return; } @@ -680,17 +681,17 @@ int fkmap(int c) } } - if ((c < 0x100) && - (isalpha(c) || - (c == '&') || - (c == '^') || - (c == ';') || - (c == '\'') || - (c == ',') || - (c == '[') || - (c == ']') || - (c == '{') || - (c == '}'))) { + if ((c < 0x100) + && (isalpha(c) + || (c == '&') + || (c == '^') + || (c == ';') + || (c == '\'') + || (c == ',') + || (c == '[') + || (c == ']') + || (c == '{') + || (c == '}'))) { chg_r_to_Xor_X_(); } diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 2929790ebf..beefc4238e 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1400,8 +1400,14 @@ find_file_in_path_option ( && (ff_file_to_find[2] == NUL || vim_ispathsep(ff_file_to_find[2]))))); if (vim_isAbsName(ff_file_to_find) - /* "..", "../path", "." and "./path": don't use the path_option */ + // "..", "../path", "." and "./path": don't use the path_option || rel_to_curdir +#if defined(WIN32) + // handle "\tmp" as absolute path + || vim_ispathsep(ff_file_to_find[0]) + // handle "c:name" as absolute path + || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':') +#endif ) { /* * Absolute path, no need to use "path_option". diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 383cd47dbe..db1469db97 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" @@ -606,13 +605,14 @@ readfile ( * Don't do this for a "nofile" or "nowrite" buffer type. */ if (!bt_dontwrite(curbuf)) { check_need_swap(newfile); - if (!read_stdin && (curbuf != old_curbuf - || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) - || (using_b_fname && - (old_b_fname != curbuf->b_fname)))) { + if (!read_stdin + && (curbuf != old_curbuf + || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) + || (using_b_fname && (old_b_fname != curbuf->b_fname)))) { EMSG(_(e_auchangedbuf)); - if (!read_buffer) + if (!read_buffer) { close(fd); + } return FAIL; } #ifdef UNIX @@ -2139,9 +2139,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'"); } @@ -2576,7 +2577,7 @@ buf_write ( errmsg = (char_u *)_("is a directory"); goto fail; } - if (mch_nodetype(fname) != NODE_WRITABLE) { + if (os_nodetype((char *)fname) != NODE_WRITABLE) { errnum = (char_u *)"E503: "; errmsg = (char_u *)_("is not a file or writable device"); goto fail; @@ -2588,11 +2589,11 @@ buf_write ( perm = -1; } } -#else /* !UNIX */ +#else /* win32 */ /* * Check for a writable device name. */ - c = mch_nodetype(fname); + c = os_nodetype((char *)fname); if (c == NODE_OTHER) { errnum = (char_u *)"E503: "; errmsg = (char_u *)_("is not a file or writable device"); @@ -2688,7 +2689,6 @@ buf_write ( } else if ((bkc & BKC_AUTO)) { /* "auto" */ int i; -# ifdef UNIX /* * Don't rename the file when: * - it's a hard link @@ -2699,9 +2699,7 @@ buf_write ( || !os_fileinfo_link((char *)fname, &file_info) || !os_fileinfo_id_equal(&file_info, &file_info_old)) { backup_copy = TRUE; - } else -# endif - { + } else { /* * Check if we can create a file and set the owner/group to * the ones from the original file. @@ -3435,9 +3433,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; } } @@ -4372,8 +4370,8 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) // (we need the full path in case :cd is used). if (fname == NULL || *fname == NUL) { retval = xmalloc(MAXPATHL + extlen + 3); // +3 for PATHSEP, "_" (Win), NUL - if (os_dirname((char_u *)retval, MAXPATHL) == FAIL || - (fnamelen = strlen(retval)) == 0) { + if (os_dirname((char_u *)retval, MAXPATHL) == FAIL + || (fnamelen = strlen(retval)) == 0) { xfree(retval); return NULL; } @@ -4740,7 +4738,6 @@ buf_check_timestamp ( { int retval = 0; char_u *path; - char_u *tbuf; char *mesg = NULL; char *mesg2 = ""; int helpmesg = FALSE; @@ -4810,19 +4807,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 +4871,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) { @@ -5098,22 +5097,167 @@ void write_lnum_adjust(linenr_T offset) } #if defined(BACKSLASH_IN_FILENAME) -/* - * Convert all backslashes in fname to forward slashes in-place. - */ +/// Convert all backslashes in fname to forward slashes in-place, +/// unless when it looks like a URL. void forward_slash(char_u *fname) { char_u *p; - for (p = fname; *p != NUL; ++p) - /* The Big5 encoding can have '\' in the trail byte. */ - if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) - ++p; - else if (*p == '\\') + if (path_with_url(fname)) { + return; + } + for (p = fname; *p != NUL; p++) { + // The Big5 encoding can have '\' in the trail byte. + if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) { + p++; + } else if (*p == '\\') { *p = '/'; + } + } } #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..ac3cf959c8 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) { @@ -2106,10 +2110,11 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, */ if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level && flp->lvl > 0) { - foldFind(gap, startlnum - 1, &fp); + (void)foldFind(gap, startlnum - 1, &fp); if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len - || fp->fd_top >= startlnum) + || fp->fd_top >= startlnum) { fp = NULL; + } } /* @@ -2163,13 +2168,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, } } if (lvl < level + i) { - foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); - if (fp2 != NULL) + (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); + if (fp2 != NULL) { bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top; - } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) - finish = TRUE; - else + } + } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) { + finish = true; + } else { break; + } } /* At the start of the first nested fold and at the end of the current @@ -2672,7 +2679,7 @@ static void foldlevelExpr(fline_T *flp) win = curwin; curwin = flp->wp; curbuf = flp->wp->w_buffer; - set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T) lnum); flp->start = 0; flp->had_end = flp->end; diff --git a/src/nvim/garray.c b/src/nvim/garray.c index e6cbd9332b..98cec69b54 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -188,12 +188,23 @@ void ga_concat(garray_T *gap, const char_u *restrict s) return; } - int len = (int)strlen((char *) s); + ga_concat_len(gap, (const char *restrict) s, strlen((char *) s)); +} + +/// Concatenate a string to a growarray which contains characters +/// +/// @param[out] gap Growarray to modify. +/// @param[in] s String to concatenate. +/// @param[in] len String length. +void ga_concat_len(garray_T *const gap, const char *restrict s, + const size_t len) + FUNC_ATTR_NONNULL_ALL +{ if (len) { - ga_grow(gap, len); + ga_grow(gap, (int) len); char *data = gap->ga_data; - memcpy(data + gap->ga_len, s, (size_t)len); - gap->ga_len += len; + memcpy(data + gap->ga_len, s, len); + gap->ga_len += (int) len; } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 437495faa4..dbf0322d78 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -144,7 +144,7 @@ static int KeyNoremap = 0; /* remapping flags */ static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ -static int last_recorded_len = 0; /* number of last recorded chars */ +static size_t last_recorded_len = 0; // number of last recorded chars static const uint8_t ui_toggle[] = { K_SPECIAL, KS_EXTRA, KE_PASTE, 0 }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -211,7 +211,7 @@ char_u *get_recorded(void) * (possibly mapped) characters that stopped the recording. */ len = STRLEN(p); - if ((int)len >= last_recorded_len) { + if (len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; } @@ -243,13 +243,15 @@ static void add_buff ( buffheader_T *buf, char_u *s, - long slen /* length of "s" or -1 */ + ssize_t slen // length of "s" or -1 ) { - if (slen < 0) - slen = (long)STRLEN(s); - if (slen == 0) /* don't add empty strings */ + if (slen < 0) { + slen = (ssize_t)STRLEN(s); + } + if (slen == 0) { // don't add empty strings return; + } if (buf->bh_first.b_next == NULL) { /* first add to list */ buf->bh_space = 0; @@ -263,18 +265,19 @@ add_buff ( STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); buf->bh_index = 0; - ssize_t len; - if (buf->bh_space >= (int)slen) { + size_t len; + if (buf->bh_space >= (size_t)slen) { len = STRLEN(buf->bh_curr->b_str); STRLCPY(buf->bh_curr->b_str + len, s, slen + 1); - buf->bh_space -= slen; + buf->bh_space -= (size_t)slen; } else { - if (slen < MINIMAL_SIZE) + if (slen < MINIMAL_SIZE) { len = MINIMAL_SIZE; - else - len = slen; + } else { + len = (size_t)slen; + } buffblock_T *p = xmalloc(sizeof(buffblock_T) + len); - buf->bh_space = (int)(len - slen); + buf->bh_space = len - (size_t)slen; STRLCPY(p->b_str, s, slen + 1); p->b_next = buf->bh_curr->b_next; @@ -317,11 +320,11 @@ static void add_char_buff(buffheader_T *buf, int c) if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { /* translate special key code into three byte sequence */ temp[0] = K_SPECIAL; - temp[1] = K_SECOND(c); - temp[2] = K_THIRD(c); + temp[1] = (char_u)K_SECOND(c); + temp[2] = (char_u)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = c; + temp[0] = (char_u)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -694,10 +697,11 @@ static int read_redo(int init, int old_redo) bp = bp->b_next; p = bp->b_str; } - buf[i] = c; - if (i == n - 1) { /* last byte of a character */ - if (n != 1) + buf[i] = (char_u)c; + if (i == n - 1) { // last byte of a character + if (n != 1) { c = (*mb_ptr2char)(buf); + } break; } c = *p; @@ -882,8 +886,8 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) setcursor(); return FAIL; } - s1 = xmalloc(newlen); - s2 = xmalloc(newlen); + s1 = xmalloc((size_t)newlen); + s2 = xmalloc((size_t)newlen); typebuf.tb_buflen = newlen; /* copy the old chars, before the insertion point */ @@ -937,7 +941,7 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) nrm = noremap; for (i = 0; i < addlen; ++i) typebuf.tb_noremap[typebuf.tb_off + i + offset] = - (--nrm >= 0) ? val : RM_YES; + (char_u)((--nrm >= 0) ? val : RM_YES); /* tb_maplen and tb_silent only remember the length of mapped and/or * silent mappings at the start of the buffer, assuming that a mapped @@ -965,8 +969,8 @@ void ins_char_typebuf(int c) char_u buf[MB_MAXBYTES + 1]; if (IS_SPECIAL(c)) { buf[0] = K_SPECIAL; - buf[1] = K_SECOND(c); - buf[2] = K_THIRD(c); + buf[1] = (char_u)K_SECOND(c); + buf[2] = (char_u)K_THIRD(c); buf[3] = NUL; } else { buf[(*mb_char2bytes)(c, buf)] = NUL; @@ -1083,25 +1087,25 @@ void del_typebuf(int len, int offset) * Write typed characters to script file. * If recording is on put the character in the recordbuffer. */ -static void gotchars(char_u *chars, int len) +static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; char_u buf[2]; - int todo = len; - /* remember how many chars were last recorded */ - if (Recording) + // remember how many chars were last recorded + if (Recording) { last_recorded_len += len; + } buf[1] = NUL; - while (todo--) { - /* Handle one byte at a time; no translation to be done. */ + while (len--) { + // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = c; + buf[0] = (char_u)c; add_buff(&recordbuff, buf, 1L); } } @@ -1465,10 +1469,10 @@ int vgetc(void) * Note: This will loop until enough bytes are received! */ if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) { - ++no_mapping; - buf[0] = c; - for (i = 1; i < n; ++i) { - buf[i] = vgetorpeek(TRUE); + no_mapping++; + buf[0] = (char_u)c; + for (i = 1; i < n; i++) { + buf[i] = (char_u)vgetorpeek(true); if (buf[i] == K_SPECIAL ) { /* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, @@ -1562,7 +1566,7 @@ int char_avail(void) { int retval; - ++no_mapping; + no_mapping++; retval = vpeekc(); --no_mapping; return retval != NUL; @@ -1711,7 +1715,7 @@ static int vgetorpeek(int advance) if (advance) { /* Also record this character, it might be needed to * get out of Insert mode. */ - *typebuf.tb_buf = c; + *typebuf.tb_buf = (char_u)c; gotchars(typebuf.tb_buf, 1); } cmd_silent = FALSE; @@ -1877,19 +1881,19 @@ static int vgetorpeek(int advance) match = typebuf_match_len(p_pt, &mlen); } if (match) { - /* write chars to script file(s) */ - if (mlen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - mlen - typebuf.tb_maplen); + // write chars to script file(s) + if (mlen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(mlen - typebuf.tb_maplen)); + } del_typebuf(mlen, 0); /* remove the chars */ set_option_value((char_u *)"paste", (long)!p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); /* clear ruler */ + msg_row = (int)Rows - 1; + msg_clr_eos(); // clear ruler } status_redraw_all(); redraw_statuslines(); @@ -1975,11 +1979,11 @@ static int vgetorpeek(int advance) char_u *save_m_keys; char_u *save_m_str; - /* write chars to script file(s) */ - if (keylen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - keylen - typebuf.tb_maplen); + // write chars to script file(s) + if (keylen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(keylen - typebuf.tb_maplen)); + } cmd_silent = (typebuf.tb_silent > 0); del_typebuf(keylen, 0); /* remove the mapped keys */ @@ -2417,7 +2421,7 @@ inchar ( else return -1; } else { - buf[0] = script_char; + buf[0] = (char_u)script_char; len = 1; } } @@ -2453,7 +2457,7 @@ inchar ( * Fill up to a third of the buffer, because each character may be * tripled below. */ - len = os_inchar(buf, maxlen / 3, wait_time, tb_change_cnt); + len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt); } if (typebuf_changed(tb_change_cnt)) @@ -2496,8 +2500,8 @@ fix_input_buffer ( && !script && (i < 2 || p[1] != KS_EXTRA))) { memmove(p + 3, p + 1, (size_t)i); - p[2] = K_THIRD(p[0]); - p[1] = K_SECOND(p[0]); + p[2] = (char_u)K_THIRD(p[0]); + p[1] = (char_u)K_SECOND(p[0]); p[0] = K_SPECIAL; p += 2; len += 2; @@ -2573,11 +2577,11 @@ do_map ( int new_hash; mapblock_T **abbr_table; mapblock_T **map_table; - int unique = FALSE; - int nowait = FALSE; - int silent = FALSE; - int special = FALSE; - int expr = FALSE; + bool unique = false; + bool nowait = false; + bool silent = false; + bool special = false; + bool expr = false; int noremap; char_u *orig_rhs; @@ -2609,7 +2613,7 @@ do_map ( */ if (STRNCMP(keys, "<nowait>", 8) == 0) { keys = skipwhite(keys + 8); - nowait = TRUE; + nowait = true; continue; } @@ -2618,7 +2622,7 @@ do_map ( */ if (STRNCMP(keys, "<silent>", 8) == 0) { keys = skipwhite(keys + 8); - silent = TRUE; + silent = true; continue; } @@ -2627,7 +2631,7 @@ do_map ( */ if (STRNCMP(keys, "<special>", 9) == 0) { keys = skipwhite(keys + 9); - special = TRUE; + special = true; continue; } @@ -2645,7 +2649,7 @@ do_map ( */ if (STRNCMP(keys, "<expr>", 6) == 0) { keys = skipwhite(keys + 6); - expr = TRUE; + expr = true; continue; } /* @@ -2653,7 +2657,7 @@ do_map ( */ if (STRNCMP(keys, "<unique>", 8) == 0) { keys = skipwhite(keys + 8); - unique = TRUE; + unique = true; continue; } break; @@ -2669,13 +2673,14 @@ do_map ( p = keys; do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); while (*p && (maptype == 1 || !ascii_iswhite(*p))) { - if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && - p[1] != NUL) - ++p; /* skip CTRL-V or backslash */ - ++p; + if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) { + p++; // skip CTRL-V or backslash + } + p++; } - if (*p != NUL) + if (*p != NUL) { *p++ = NUL; + } p = skipwhite(p); rhs = p; @@ -2688,22 +2693,24 @@ do_map ( goto theend; } - /* - * If mapping has been given as ^V<C_UP> say, then replace the term codes - * with the appropriate two bytes. If it is a shifted special key, unshift - * it too, giving another two bytes. - * replace_termcodes() may move the result to allocated memory, which - * needs to be freed later (*keys_buf and *arg_buf). - * replace_termcodes() also removes CTRL-Vs and sometimes backslashes. - */ - if (haskey) - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); + // If mapping has been given as ^V<C_UP> say, then replace the term codes + // with the appropriate two bytes. If it is a shifted special key, unshift + // it too, giving another two bytes. + // replace_termcodes() may move the result to allocated memory, which + // needs to be freed later (*keys_buf and *arg_buf). + // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. + if (haskey) { + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special, + CPO_TO_CPO_FLAGS); + } orig_rhs = rhs; if (hasarg) { - if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */ + if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing rhs = (char_u *)""; - else - rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); + } else { + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, + CPO_TO_CPO_FLAGS); + } } /* @@ -3270,7 +3277,8 @@ int map_to_exists(char_u *str, char_u *modechars, int abbr) char_u *buf; int retval; - rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); + rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, + CPO_TO_CPO_FLAGS); if (vim_strchr(modechars, 'n') != NULL) mode |= NORMAL; @@ -3465,7 +3473,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) mp = maphash[hash]; for (; mp; mp = mp->m_next) { if (mp->m_mode & expand_mapmodes) { - p = translate_mapping(mp->m_keys, TRUE); + p = translate_mapping(mp->m_keys, true, CPO_TO_CPO_FLAGS); if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) ++count; @@ -3483,7 +3491,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) break; /* for (round) */ if (round == 1) { - *file = (char_u **)xmalloc(count * sizeof(char_u *)); + *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *)); } } /* for (round) */ @@ -3647,8 +3655,8 @@ int check_abbr(int c, char_u *ptr, int col, int mincol) /* special key code, split up */ if (IS_SPECIAL(c) || c == K_SPECIAL) { tb[j++] = K_SPECIAL; - tb[j++] = K_SECOND(c); - tb[j++] = K_THIRD(c); + tb[j++] = (char_u)K_SECOND(c); + tb[j++] = (char_u)K_THIRD(c); } else { if (c < ABBR_OFF && (c < ' ' || c > '~')) tb[j++] = Ctrl_V; /* special char needs CTRL-V */ @@ -3657,8 +3665,9 @@ int check_abbr(int c, char_u *ptr, int col, int mincol) if (c >= ABBR_OFF) c -= ABBR_OFF; j += (*mb_char2bytes)(c, tb + j); - } else - tb[j++] = c; + } else { + tb[j++] = (char_u)c; + } } tb[j] = NUL; /* insert the last typed char */ @@ -4190,14 +4199,15 @@ void add_map(char_u *map, int mode) // Returns NULL when there is a problem. static char_u * translate_mapping ( char_u *str, - int expmap // TRUE when expanding mappings on command-line + int expmap, // True when expanding mappings on command-line + int cpo_flags // Value of various flags present in &cpo ) { garray_T ga; ga_init(&ga, 1, 40); - int cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL); - int cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL); + bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); + bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI); for (; *str; ++str) { int c = *str; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 69e65c3208..dafb75ca87 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 */ @@ -299,10 +293,11 @@ EXTERN int msg_no_more INIT(= FALSE); /* don't use more prompt, truncate EXTERN char_u *sourcing_name INIT( = NULL); /* name of error message source */ EXTERN linenr_T sourcing_lnum INIT(= 0); /* line number of the source file */ -EXTERN int ex_nesting_level INIT(= 0); /* nesting level */ -EXTERN int debug_break_level INIT(= -1); /* break below this level */ -EXTERN int debug_did_msg INIT(= FALSE); /* did "debug mode" message */ -EXTERN int debug_tick INIT(= 0); /* breakpoint change count */ +EXTERN int ex_nesting_level INIT(= 0); // nesting level +EXTERN int debug_break_level INIT(= -1); // break below this level +EXTERN int debug_did_msg INIT(= false); // did "debug mode" message +EXTERN int debug_tick INIT(= 0); // breakpoint change count +EXTERN int debug_backtrace_level INIT(= 0); // breakpoint backtrace level /* Values for "do_profiling". */ #define PROF_NONE 0 /* profiling not started */ @@ -509,6 +504,7 @@ EXTERN int cterm_normal_fg_bold INIT(= 0); EXTERN int cterm_normal_bg_color INIT(= 0); EXTERN RgbValue normal_fg INIT(= -1); EXTERN RgbValue normal_bg INIT(= -1); +EXTERN RgbValue normal_sp INIT(= -1); EXTERN int autocmd_busy INIT(= FALSE); /* Is apply_autocmds() busy? */ EXTERN int autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */ @@ -918,8 +914,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..916d27a964 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" @@ -2191,18 +2190,19 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen); } - /* Check if need to use Courier for ASCII code range, and if so pick up - * the encoding to use */ - prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present && - (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) - == 'y'); + // Check if need to use Courier for ASCII code range, and if so pick up + // the encoding to use + prt_use_courier = ( + mbfont_opts[OPT_MBFONT_USECOURIER].present + && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y')); if (prt_use_courier) { - /* Use national ASCII variant unless ASCII wanted */ - if (mbfont_opts[OPT_MBFONT_ASCII].present && - (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) + // Use national ASCII variant unless ASCII wanted + if (mbfont_opts[OPT_MBFONT_ASCII].present + && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) { prt_ascii_encoding = "ascii"; - else + } else { prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc; + } } prt_ps_font = &prt_ps_mb_font; @@ -2780,11 +2780,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(); @@ -3028,10 +3030,10 @@ int mch_print_text_out(char_u *p, size_t len) prt_text_run += char_width; prt_pos_x += char_width; - /* The downside of fp - use relative error on right margin check */ + // The downside of fp - use relative error on right margin check next_pos = prt_pos_x + prt_char_width; - need_break = (next_pos > prt_right_margin) && - ((next_pos - prt_right_margin) > (prt_right_margin*1e-5)); + need_break = ((next_pos > prt_right_margin) + && ((next_pos - prt_right_margin) > (prt_right_margin * 1e-5))); if (need_break) prt_flush_buffer(); diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 7169a1d963..2f9ec0b3ff 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -27,7 +27,6 @@ #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -1063,8 +1062,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m", *qfpos == '-', cmdline) > 0) { if (postponed_split != 0) { - win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); + (void)win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags); RESET_BINDING(curwin); postponed_split = 0; } @@ -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/indent_c.c b/src/nvim/indent_c.c index 17fadc4bfd..efe8e73a3c 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -2282,15 +2282,14 @@ int get_c_indent(void) * location for b_ind_open_extra. */ - if (start_brace == BRACE_IN_COL0) { /* '{' is in column 0 */ + if (start_brace == BRACE_IN_COL0) { // '{' is in column 0 amount = curbuf->b_ind_open_left_imag; - lookfor_cpp_namespace = TRUE; - } else if (start_brace == BRACE_AT_START && - lookfor_cpp_namespace) { /* '{' is at start */ - - lookfor_cpp_namespace = TRUE; + lookfor_cpp_namespace = true; + } else if (start_brace == BRACE_AT_START + && lookfor_cpp_namespace) { // '{' is at start + lookfor_cpp_namespace = true; } else { - if (start_brace == BRACE_AT_END) { /* '{' is at end of line */ + if (start_brace == BRACE_AT_END) { // '{' is at end of line amount += curbuf->b_ind_open_imag; l = skipwhite(get_cursor_line_ptr()); diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 6c75d8bdf4..99e94fc60f 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -482,26 +482,28 @@ char_u *get_special_key_name(int c, int modifiers) return string; } -/* - * Try translating a <> name at (*srcp)[] to dst[]. - * Return the number of characters added to dst[], zero for no match. - * If there is a match, srcp is advanced to after the <> name. - * dst[] must be big enough to hold the result (up to six characters)! - */ -unsigned int -trans_special ( - char_u **srcp, - char_u *dst, - int keycode /* prefer key code, e.g. K_DEL instead of DEL */ -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Source from which <> are translated. Is advanced to +/// after the <> name if there is a match. +/// @param[in] src_len Length of the srcp. +/// @param[out] dst Location where translation result will be kept. Must have +/// at least six bytes. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// +/// @return Number of characters added to dst, zero for no match. +unsigned int trans_special(const char_u **srcp, const size_t src_len, + char_u *const dst, const bool keycode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int modifiers = 0; int key; unsigned int dlen = 0; - key = find_special_key(srcp, &modifiers, keycode, FALSE); - if (key == 0) + key = find_special_key(srcp, src_len, &modifiers, keycode, false); + if (key == 0) { return 0; + } /* Put the appropriate modifier in a string */ if (modifiers != 0) { @@ -514,69 +516,78 @@ trans_special ( dst[dlen++] = K_SPECIAL; dst[dlen++] = (char_u)KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); - } else if (has_mbyte && !keycode) + } else if (has_mbyte && !keycode) { dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen); - else if (keycode) { + } else if (keycode) { char_u *after = add_char2buf(key, dst + dlen); assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); dlen = (unsigned int)(after - dst); - } - else + } else { dst[dlen++] = (char_u)key; + } return dlen; } -// Try translating a <> name at (*srcp)[], return the key and modifiers. -// srcp is advanced to after the <> name. -// returns 0 if there is no match. -int find_special_key( - char_u **srcp, - int *modp, - int keycode, // prefer key code, e.g. K_DEL instead of DEL - int keep_x_key // don't translate xHome to Home key -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. +/// @param[in] src_len srcp length. +/// @param[out] modp Location where information about modifiers is saved. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// @param[in] keep_x_key Don’t translate xHome to Home key. +/// +/// @return Key and modifiers or 0 if there is no match. +int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, + const bool keycode, const bool keep_x_key) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *last_dash; - char_u *end_of_name; - char_u *src; - char_u *bp; + const char_u *last_dash; + const char_u *end_of_name; + const char_u *src; + const char_u *bp; + const char_u *const end = *srcp + src_len - 1; int modifiers; int bit; int key; unsigned long n; int l; + if (src_len == 0) { + return 0; + } + src = *srcp; - if (src[0] != '<') + if (src[0] != '<') { return 0; + } // Find end of modifier list last_dash = src; - for (bp = src + 1; *bp == '-' || vim_isIDc(*bp); bp++) { + for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { if (*bp == '-') { last_dash = bp; - if (bp[1] != NUL) { + if (bp + 1 <= end) { if (has_mbyte) { - l = mb_ptr2len(bp + 1); + l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1); } else { l = 1; } - if (bp[l + 1] == '>') { - bp += l; // anything accepted, like <C-?> + if (end - bp > l && bp[l + 1] == '>') { + bp += l; // anything accepted, like <C-?> } } } - if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) { - bp += 3; // skip t_xx, xx may be '-' or '>' - } else if (STRNICMP(bp, "char-", 5) == 0) { + if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { + bp += 3; // skip t_xx, xx may be '-' or '>' + } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); bp += l + 5; break; } } - if (*bp == '>') { /* found matching '>' */ + if (bp <= end && *bp == '>') { // found matching '>' end_of_name = bp + 1; /* Which modifiers are given? */ @@ -696,7 +707,7 @@ int find_special_key_in_table(int c) * termcap name. * Return the key code, or 0 if not found. */ -int get_special_key_code(char_u *name) +int get_special_key_code(const char_u *name) { char_u *table_name; int i, j; @@ -730,50 +741,58 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) return 0; /* Shouldn't get here */ } -// Replace any terminal code strings in from[] with the equivalent internal -// vim representation. This is used for the "from" and "to" part of a -// mapping, and the "to" part of a menu command. -// Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains -// '<'. -// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER. -// -// The replacement is done in result[] and finally copied into allocated -// memory. If this all works well *bufp is set to the allocated memory and a -// pointer to it is returned. If something fails *bufp is set to NULL and from -// is returned. -// -// CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V -// is included, otherwise it is removed (for ":map xx ^V", maps xx to -// nothing). When 'cpoptions' does not contain 'B', a backslash can be used -// instead of a CTRL-V. -char_u * replace_termcodes ( - char_u *from, - char_u **bufp, - int from_part, - int do_lt, // also translate <lt> - int special // always accept <key> notation -) +/// Replace any terminal code strings with the equivalent internal +/// representation +/// +/// This is used for the "from" and "to" part of a mapping, and the "to" part of +/// a menu command. Any strings like "<C-UP>" are also replaced, unless +/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL +/// KS_SPECIAL KE_FILLER. +/// +/// @param[in] from What characters to replace. +/// @param[in] from_len Length of the "from" argument. +/// @param[out] bufp Location where results were saved in case of success +/// (allocated). Will be set to NULL in case of failure. +/// @param[in] do_lt If true, also translate <lt>. +/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is +/// removed (to make ":map xx ^V" map xx to nothing). +/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash +/// can be used in place of <C-v>. All other <C-v> +/// characters are removed. +/// @param[in] special If true, always accept <key> notation. +/// @param[in] cpo_flags Relevant flags derived from p_cpo, see +/// #CPO_TO_CPO_FLAGS. +/// +/// @return Pointer to an allocated memory in case of success, "from" in case of +/// failure. In case of success returned pointer is also saved to +/// "bufp". +char_u *replace_termcodes(const char_u *from, const size_t from_len, + char_u **bufp, const bool from_part, const bool do_lt, + const bool special, int cpo_flags) + FUNC_ATTR_NONNULL_ALL { ssize_t i; size_t slen; char_u key; size_t dlen = 0; - char_u *src; + const char_u *src; + const char_u *const end = from + from_len - 1; int do_backslash; // backslash is a special character int do_special; // recognize <> key codes char_u *result; // buffer for resulting string - do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); - do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special; + do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); + do_special = !(cpo_flags&FLAG_CPO_SPECI) || special; // Allocate space for the translation. Worst case a single character is // replaced by 6 bytes (shifted special key), plus a NUL at the end. - result = xmalloc(STRLEN(from) * 6 + 1); + result = xmalloc(from_len * 6 + 1); src = from; // Check for #n at start only: function key n - if (from_part && src[0] == '#' && ascii_isdigit(src[1])) { // function key + if (from_part && from_len > 1 && src[0] == '#' + && ascii_isdigit(src[1])) { // function key result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; if (src[1] == '0') { @@ -785,13 +804,14 @@ char_u * replace_termcodes ( } // Copy each byte from *from to result[dlen] - while (*src != NUL) { + while (src <= end) { // If 'cpoptions' does not contain '<', check for special key codes, // like "<C-S-LeftMouse>" - if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0)) { + if (do_special && (do_lt || ((end - src) >= 3 + && STRNCMP(src, "<lt>", 4) != 0))) { // Replace <SID> by K_SNR <script-nr> _. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) - if (STRNICMP(src, "<SID>", 5) == 0) { + if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (current_SID <= 0) { EMSG(_(e_usingsid)); } else { @@ -806,7 +826,7 @@ char_u * replace_termcodes ( } } - slen = trans_special(&src, result + dlen, TRUE); + slen = trans_special(&src, (size_t) (end - src) + 1, result + dlen, true); if (slen) { dlen += slen; continue; @@ -819,10 +839,10 @@ char_u * replace_termcodes ( // Replace <Leader> by the value of "mapleader". // Replace <LocalLeader> by the value of "maplocalleader". // If "mapleader" or "maplocalleader" isn't set use a backslash. - if (STRNICMP(src, "<Leader>", 8) == 0) { + if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) { len = 8; p = get_var_value((char_u *)"g:mapleader"); - } else if (STRNICMP(src, "<LocalLeader>", 13) == 0) { + } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) { len = 13; p = get_var_value((char_u *)"g:maplocalleader"); } else { @@ -851,8 +871,8 @@ char_u * replace_termcodes ( // If 'cpoptions' does not contain 'B', also accept a backslash. key = *src; if (key == Ctrl_V || (do_backslash && key == '\\')) { - ++src; // skip CTRL-V or backslash - if (*src == NUL) { + src++; // skip CTRL-V or backslash + if (src > end) { if (from_part) { result[dlen++] = key; } @@ -861,7 +881,7 @@ char_u * replace_termcodes ( } // skip multibyte char correctly - for (i = (*mb_ptr2len)(src); i > 0; --i) { + for (i = (*mb_ptr2len_len)(src, (int) (end - src) + 1); i > 0; i--) { // If the character is K_SPECIAL, replace it with K_SPECIAL // KS_SPECIAL KE_FILLER. // If compiled with the GUI replace CSI with K_CSI. diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 8f9980c6b4..bb8ba84a6a 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -1,6 +1,8 @@ #ifndef NVIM_KEYMAP_H #define NVIM_KEYMAP_H +#include "nvim/strings.h" + /* * Keycode definitions for special keys. * @@ -461,6 +463,14 @@ enum key_extra { // This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 +#define FLAG_CPO_BSLASH 0x01 +#define FLAG_CPO_SPECI 0x02 +#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ + ? 0 \ + : FLAG_CPO_BSLASH)| \ + (vim_strchr(p_cpo, CPO_SPECI) == NULL \ + ? 0 \ + : FLAG_CPO_SPECI)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "keymap.h.generated.h" 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 d3cdfe3edf..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(); /* @@ -317,14 +317,16 @@ int main(int argc, char **argv) } // open terminals when opening files that start with term:// - do_cmdline_cmd("autocmd BufReadCmd term://* " +#define PROTO "term://" + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " ":call termopen( " // Capture the command string "matchstr(expand(\"<amatch>\"), " - "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " // capture the working directory "{'cwd': get(matchlist(expand(\"<amatch>\"), " - "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})"); + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"); +#undef PROTO /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -333,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`. @@ -657,6 +659,9 @@ static void init_locale(void) setlocale(LC_NUMERIC, "C"); # endif +# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale + bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR); +# else // old vim style: $runtime/lang { char_u *p; @@ -665,11 +670,12 @@ static void init_locale(void) p = (char_u *)vim_getenv("VIMRUNTIME"); if (p != NULL && *p != NUL) { vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); - bindtextdomain(VIMPACKAGE, (char *)NameBuff); + bindtextdomain(PROJECT_NAME, (char *)NameBuff); } xfree(p); - textdomain(VIMPACKAGE); } +# endif + textdomain(PROJECT_NAME); TIME_MSG("locale set"); } #endif @@ -747,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; @@ -1134,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 fdd83f9dac..3495203c43 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -571,8 +571,8 @@ char_u * mb_init(void) #ifdef HAVE_WORKING_LIBINTL /* GNU gettext 0.10.37 supports this feature: set the codeset used for * translated messages independently from the current locale. */ - (void)bind_textdomain_codeset(VIMPACKAGE, - enc_utf8 ? "utf-8" : (char *)p_enc); + (void)bind_textdomain_codeset(PROJECT_NAME, + enc_utf8 ? "utf-8" : (char *)p_enc); #endif @@ -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..4e35dd481f 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; } @@ -789,9 +786,8 @@ void ml_recover(void) if (fname == NULL) /* When there is no file name */ fname = (char_u *)""; len = (int)STRLEN(fname); - if (len >= 4 && - STRNICMP(fname + len - 4, ".s", 2) - == 0 + if (len >= 4 + && STRNICMP(fname + len - 4, ".s", 2) == 0 && vim_strchr((char_u *)"UVWuvw", fname[len - 2]) != NULL && ASCII_ISALPHA(fname[len - 1])) { directly = TRUE; @@ -3196,7 +3192,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/menu.c b/src/nvim/menu.c index 91a72abfc5..3c2394d579 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -215,10 +215,12 @@ ex_menu ( if (STRICMP(map_to, "<nop>") == 0) { /* "<Nop>" means nothing */ map_to = (char_u *)""; map_buf = NULL; - } else if (modes & MENU_TIP_MODE) - map_buf = NULL; /* Menu tips are plain text. */ - else - map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special); + } else if (modes & MENU_TIP_MODE) { + map_buf = NULL; // Menu tips are plain text. + } else { + map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true, + special, CPO_TO_CPO_FLAGS); + } menuarg.modes = modes; menuarg.noremap[0] = noremap; menuarg.silent[0] = silent; diff --git a/src/nvim/message.c b/src/nvim/message.c index 1dd71baaa4..47f246fc76 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., @@ -472,22 +467,23 @@ int emsg(char_u *s) { int attr; char_u *p; - int ignore = FALSE; + int ignore = false; int severe; - /* Skip this if not giving error messages at the moment. */ - if (emsg_not_now()) - return TRUE; + // Skip this if not giving error messages at the moment. + if (emsg_not_now()) { + return true; + } - called_emsg = TRUE; - ex_exitval = 1; + called_emsg = true; + if (emsg_silent == 0) { + ex_exitval = 1; + } - /* - * If "emsg_severe" is TRUE: When an error exception is to be thrown, - * prefer this message over previous messages for the same command. - */ + // If "emsg_severe" is TRUE: When an error exception is to be thrown, + // prefer this message over previous messages for the same command. severe = emsg_severe; - emsg_severe = FALSE; + emsg_severe = false; if (!emsg_off || vim_strchr(p_debug, 't') != NULL) { /* @@ -503,8 +499,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 +559,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); } @@ -1538,51 +1508,44 @@ void msg_puts_attr(char_u *s, int attr) msg_puts_attr_len(s, -1, attr); } -/* - * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). - * When "maxlen" is -1 there is no maximum length. - * When "maxlen" is >= 0 the message is not put in the history. - */ +/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). +/// When "maxlen" is -1 there is no maximum length. +/// When "maxlen" is >= 0 the message is not put in the history. static void msg_puts_attr_len(char_u *str, int maxlen, int attr) { - /* - * If redirection is on, also write to the redirection file. - */ + // If redirection is on, also write to the redirection file. redir_write(str, maxlen); - /* - * Don't print anything when using ":silent cmd". - */ - if (msg_silent != 0) + // Don't print anything when using ":silent cmd". + if (msg_silent != 0) { return; + } - /* if MSG_HIST flag set, add message to history */ + // if MSG_HIST flag set, add message to history if ((attr & MSG_HIST) && maxlen < 0) { add_msg_hist(str, -1, attr); attr &= ~MSG_HIST; } - /* - * When writing something to the screen after it has scrolled, requires a - * wait-return prompt later. Needed when scrolling, resetting - * need_wait_return after some prompt, and then outputting something - * without scrolling - */ - if (msg_scrolled != 0 && !msg_scrolled_ign) - need_wait_return = TRUE; - msg_didany = TRUE; /* remember that something was outputted */ + // When writing something to the screen after it has scrolled, requires a + // wait-return prompt later. Needed when scrolling, resetting + // need_wait_return after some prompt, and then outputting something + // without scrolling + if (msg_scrolled != 0 && !msg_scrolled_ign) { + need_wait_return = true; + } + msg_didany = true; // remember that something was outputted - /* - * If there is no valid screen, use fprintf so we can see error messages. - * If termcap is not active, we may be writing in an alternate console - * window, cursor positioning may not work correctly (window size may be - * different, e.g. for Win32 console) or we just don't know where the - * cursor is. - */ - if (msg_use_printf()) - msg_puts_printf(str, maxlen); - else - msg_puts_display(str, maxlen, attr, FALSE); + // If there is no valid screen, use fprintf so we can see error messages. + // If termcap is not active, we may be writing in an alternate console + // window, cursor positioning may not work correctly (window size may be + // different, e.g. for Win32 console) or we just don't know where the + // cursor is. + if (msg_use_printf()) { + msg_puts_printf((char *)str, maxlen); + } else { + msg_puts_display(str, maxlen, attr, false); + } } /* @@ -1601,39 +1564,31 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse) int wrap; int did_last_char; - did_wait_return = FALSE; + did_wait_return = false; while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) { - /* - * We are at the end of the screen line when: - * - When outputting a newline. - * - When outputting a character in the last column. - */ - if (!recurse && msg_row >= Rows - 1 && (*s == '\n' || ( - cmdmsg_rl - ? ( - msg_col <= 1 - || (*s == TAB && msg_col <= 7) - || (has_mbyte && - (*mb_ptr2cells)(s) > 1 && - msg_col <= 2) - ) - : - (msg_col + t_col >= Columns - 1 - || (*s == TAB && msg_col + - t_col >= ((Columns - 1) & ~7)) - || (has_mbyte && - (*mb_ptr2cells)(s) > 1 - && msg_col + t_col >= - Columns - 2) - )))) { - /* - * The screen is scrolled up when at the last row (some terminals - * scroll automatically, some don't. To avoid problems we scroll - * ourselves). - */ - if (t_col > 0) - /* output postponed text */ + // We are at the end of the screen line when: + // - When outputting a newline. + // - When outputting a character in the last column. + if (!recurse && msg_row >= Rows - 1 + && (*s == '\n' || (cmdmsg_rl + ? (msg_col <= 1 + || (*s == TAB && msg_col <= 7) + || (has_mbyte + && (*mb_ptr2cells)(s) > 1 + && msg_col <= 2)) + : (msg_col + t_col >= Columns - 1 + || (*s == TAB + && msg_col + t_col >= ((Columns - 1) & ~7)) + || (has_mbyte + && (*mb_ptr2cells)(s) > 1 + && msg_col + t_col >= Columns - 2))))) { + // The screen is scrolled up when at the last row (some terminals + // scroll automatically, some don't. To avoid problems we scroll + // ourselves). + if (t_col > 0) { + // output postponed text t_puts(&t_col, t_s, s, attr); + } /* When no more prompt and no more room, truncate here */ if (msg_no_more && lines_left == 0) @@ -1740,18 +1695,15 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse) cw = 1; l = 1; } - /* When drawing from right to left or when a double-wide character - * doesn't fit, draw a single character here. Otherwise collect - * characters and draw them all at once later. */ - if ( - cmdmsg_rl - || - (cw > 1 && msg_col + t_col >= Columns - 1) - ) { - if (l > 1) + // When drawing from right to left or when a double-wide character + // doesn't fit, draw a single character here. Otherwise collect + // characters and draw them all at once later. + if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) { + if (l > 1) { s = screen_puts_mbyte(s, l, attr) - 1; - else + } else { msg_screen_putchar(*s, attr); + } } else { /* postpone this character until later */ if (t_col == 0) @@ -1787,25 +1739,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 */ @@ -1968,46 +1919,46 @@ int msg_use_printf(void) return !embedded_mode && !ui_active(); } -/* - * Print a message when there is no valid screen. - */ -static void msg_puts_printf(char_u *str, int maxlen) +/// Print a message when there is no valid screen. +static void msg_puts_printf(char *str, int maxlen) { - char_u *s = str; - char_u buf[4]; - char_u *p; + char *s = str; + char buf[4]; + char *p; while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { if (!(silent_mode && p_verbose == 0)) { - /* NL --> CR NL translation (for Unix, not for "--version") */ - /* NL --> CR translation (for Mac) */ + // NL --> CR NL translation (for Unix, not for "--version") p = &buf[0]; - if (*s == '\n' && !info_message) + if (*s == '\n' && !info_message) { *p++ = '\r'; + } *p++ = *s; *p = '\0'; - if (info_message) /* informative message, not an error */ - mch_msg((char *)buf); - else - mch_errmsg((char *)buf); + if (info_message) { + mch_msg(buf); + } else { + mch_errmsg(buf); + } } - /* primitive way to compute the current column */ + // primitive way to compute the current column if (cmdmsg_rl) { - if (*s == '\r' || *s == '\n') + if (*s == '\r' || *s == '\n') { msg_col = Columns - 1; - else - --msg_col; + } else { + msg_col--; + } } else { - if (*s == '\r' || *s == '\n') + if (*s == '\r' || *s == '\n') { msg_col = 0; - else - ++msg_col; + } else { + msg_col++; + } } - ++s; + s++; } - msg_didout = TRUE; /* assume that line is not empty */ - + msg_didout = true; // assume that line is not empty } /* @@ -2572,7 +2523,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 +3037,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 +3048,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 +3087,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; @@ -3413,8 +3365,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // leave negative numbers for sprintf to handle, to // avoid handling tricky cases like (short int)-32768 } else if (alternate_form) { - if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' || - fmt_spec == 'b' || fmt_spec == 'B')) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' + || fmt_spec == 'b' || fmt_spec == 'B')) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } 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..48791384a6 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,22 +197,19 @@ 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); else lead_len = 0; if (dir == FORWARD) { - /* - * Skip preprocessor directives, unless they are - * recognised as comments. - */ - if ( - lead_len == 0 && - ptr[0] == '#') { - while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) + // Skip preprocessor directives, unless they are + // recognised as comments. + if (lead_len == 0 && ptr[0] == '#') { + while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) { ptr = ml_get(--curwin->w_cursor.lnum); + } newindent = get_indent(); } if (flags & OPENLINE_DO_COM) @@ -303,28 +293,26 @@ open_line ( && cin_is_cinword(ptr)) did_si = TRUE; } - } else { /* dir == BACKWARD */ - /* - * Skip preprocessor directives, unless they are - * recognised as comments. - */ - if ( - lead_len == 0 && - ptr[0] == '#') { - int was_backslashed = FALSE; - - while ((ptr[0] == '#' || was_backslashed) && - curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') - was_backslashed = TRUE; - else - was_backslashed = FALSE; + } else { // dir == BACKWARD + // Skip preprocessor directives, unless they are + // recognised as comments. + if (lead_len == 0 && ptr[0] == '#') { + bool was_backslashed = false; + + while ((ptr[0] == '#' || was_backslashed) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { + if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') { + was_backslashed = true; + } else { + was_backslashed = false; + } ptr = ml_get(++curwin->w_cursor.lnum); } - if (was_backslashed) - newindent = 0; /* Got to end of file */ - else + if (was_backslashed) { + newindent = 0; // Got to end of file + } else { newindent = get_indent(); + } } p = skipwhite(ptr); if (*p == '}') /* if line starts with '}': do indent */ @@ -401,7 +389,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 +486,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 +587,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 +599,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 = ' '; @@ -675,16 +662,12 @@ open_line ( did_si = can_si = FALSE; } else if (comment_end != NULL) { - /* - * We have finished a comment, so we don't use the leader. - * If this was a C-comment and 'ai' or 'si' is set do a normal - * indent to align with the line containing the start of the - * comment. - */ - if (comment_end[0] == '*' && comment_end[1] == '/' && - (curbuf->b_p_ai - || do_si - )) { + // We have finished a comment, so we don't use the leader. + // If this was a C-comment and 'ai' or 'si' is set do a normal + // indent to align with the line containing the start of the + // comment. + if (comment_end[0] == '*' && comment_end[1] == '/' + && (curbuf->b_p_ai || do_si)) { old_cursor = curwin->w_cursor; curwin->w_cursor.col = (colnr_T)(comment_end - saved_line); if ((pos = findmatch(NULL, NUL)) != NULL) { @@ -813,9 +796,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 +1266,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 +1284,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 +1294,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 +1329,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 +1352,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; - - n = (*mb_char2bytes)(c, buf); + size_t n = (size_t)(*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 +1453,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 +1502,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 +1556,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 +1564,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 +1578,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 +1617,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 +1642,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 +1675,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); @@ -1827,6 +1777,9 @@ void changed(void) if (curbuf->b_may_swap && !bt_dontwrite(curbuf) ) { + int save_need_wait_return = need_wait_return; + + need_wait_return = false; ml_open_file(curbuf); /* The ml_open_file() can cause an ATTENTION message. @@ -1838,6 +1791,8 @@ void changed(void) os_delay(2000L, true); wait_return(TRUE); msg_scroll = save_msg_scroll; + } else { + need_wait_return = save_need_wait_return; } } changed_int(); @@ -2250,7 +2205,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 +2315,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 +2684,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 +2722,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 +2731,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..2f499e477c 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/move.c b/src/nvim/move.c index ba79c0411a..b129c5cb7a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1010,12 +1010,9 @@ scrollup ( int byfold /* true: count a closed fold as one line */ ) { - if ( - (byfold && hasAnyFolding(curwin)) - || - curwin->w_p_diff - ) { - /* count each sequence of folded lines as one logical line */ + if ((byfold && hasAnyFolding(curwin)) + || curwin->w_p_diff) { + // count each sequence of folded lines as one logical line linenr_T lnum = curwin->w_topline; while (line_count--) { if (curwin->w_topfill > 0) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5ef81721d4..0049ae6b95 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -419,8 +419,8 @@ void msgpack_rpc_validate(uint64_t *response_id, return; } - if ((type == kMessageTypeRequest && req->via.array.size != 4) || - (type == kMessageTypeNotification && req->via.array.size != 3)) { + if ((type == kMessageTypeRequest && req->via.array.size != 4) + || (type == kMessageTypeNotification && req->via.array.size != 3)) { api_set_error(err, Validation, _("Request array size should be 4 (request) " "or 3 (notification)")); return; diff --git a/src/nvim/msgpack_rpc/remote_ui.c b/src/nvim/msgpack_rpc/remote_ui.c index f0d92b52a0..6ffcffe2e1 100644 --- a/src/nvim/msgpack_rpc/remote_ui.c +++ b/src/nvim/msgpack_rpc/remote_ui.c @@ -96,6 +96,7 @@ static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, ui->visual_bell = remote_ui_visual_bell; ui->update_fg = remote_ui_update_fg; ui->update_bg = remote_ui_update_bg; + ui->update_sp = remote_ui_update_sp; ui->flush = remote_ui_flush; ui->suspend = remote_ui_suspend; ui->set_title = remote_ui_set_title; @@ -285,6 +286,10 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) PUT(hl, "background", INTEGER_OBJ(attrs.background)); } + if (attrs.special != -1) { + PUT(hl, "special", INTEGER_OBJ(attrs.special)); + } + ADD(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); } @@ -323,6 +328,13 @@ static void remote_ui_update_bg(UI *ui, int bg) push_call(ui, "update_bg", args); } +static void remote_ui_update_sp(UI *ui, int sp) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(sp)); + push_call(ui, "update_sp", args); +} + static void remote_ui_flush(UI *ui) { UIData *data = ui->data; 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 e064d34e09..382c4943ff 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -569,36 +569,33 @@ static bool normal_need_aditional_char(NormalState *s) static bool normal_need_redraw_mode_message(NormalState *s) { return ( - ( // 'showmode' is set and messages can be printed - p_smd && msg_silent == 0 - // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual - // mode - && (restart_edit != 0 || (VIsual_active - && s->old_pos.lnum == curwin->w_cursor.lnum - && s->old_pos.col == curwin->w_cursor.col)) - // command-line must be cleared or redrawn - && (clear_cmdline || redraw_cmdline) - // some message was printed or scrolled - && (msg_didout || (msg_didany && msg_scroll)) - // it is fine to remove the current message - && !msg_nowait - // the command was the result of direct user input and not a mapping - && KeyTyped - ) - || - // must restart insert mode, not in visual mode and error message is - // being shown - (restart_edit != 0 && !VIsual_active && (msg_scroll && emsg_on_display)) - ) - // no register was used - && s->oa.regname == 0 - && !(s->ca.retval & CA_COMMAND_BUSY) - && stuff_empty() - && typebuf_typed() - && emsg_silent == 0 - && !did_wait_return - && s->oa.op_type == OP_NOP; + ((p_smd && msg_silent == 0 + // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual + // mode + && (restart_edit != 0 || (VIsual_active + && s->old_pos.lnum == curwin->w_cursor.lnum + && s->old_pos.col == curwin->w_cursor.col)) + // command-line must be cleared or redrawn + && (clear_cmdline || redraw_cmdline) + // some message was printed or scrolled + && (msg_didout || (msg_didany && msg_scroll)) + // it is fine to remove the current message + && !msg_nowait + // the command was the result of direct user input and not a mapping + && KeyTyped) + // must restart insert mode, not in visual mode and error message is + // being shown + || (restart_edit != 0 && !VIsual_active && msg_scroll + && emsg_on_display)) + // no register was used + && s->oa.regname == 0 + && !(s->ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && typebuf_typed() + && emsg_silent == 0 + && !did_wait_return + && s->oa.op_type == OP_NOP); } static void normal_redraw_mode_message(NormalState *s) @@ -1436,19 +1433,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } curwin->w_p_lbr = false; oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') - oap->motion_type = MLINE; - else if (oap->motion_force == 'v') { - /* If the motion was linewise, "inclusive" will not have been set. - * Use "exclusive" to be consistent. Makes "dvj" work nice. */ - if (oap->motion_type == MLINE) + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { oap->inclusive = false; - /* If the motion already was characterwise, toggle "inclusive" */ - else if (oap->motion_type == MCHAR) + } else if (oap->motion_type == kMTCharWise) { + // If the motion already was characterwise, toggle "inclusive" oap->inclusive = !oap->inclusive; - oap->motion_type = MCHAR; + } + oap->motion_type = kMTCharWise; } else if (oap->motion_force == Ctrl_V) { - /* Change line- or characterwise motion into Visual block mode. */ + // Change line- or characterwise motion into Visual block mode. VIsual_active = true; VIsual = oap->start; VIsual_mode = Ctrl_V; @@ -1586,13 +1584,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * automatically. */ curwin->w_valid &= ~VALID_VIRTCOL; } else { - /* Include folded lines completely. */ - if (!VIsual_active && oap->motion_type == MLINE) { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) + NULL)) { curwin->w_cursor.col = 0; - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } } oap->end = oap->start; oap->start = curwin->w_cursor; @@ -1663,17 +1663,16 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } } - /* - * oap->inclusive defaults to true. - * If oap->end is on a NUL (empty line) oap->inclusive becomes - * false. This makes "d}P" and "v}dP" work the same. - */ - if (oap->motion_force == NUL || oap->motion_type == MLINE) + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { oap->inclusive = true; + } if (VIsual_mode == 'V') { - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; } else if (VIsual_mode == 'v') { - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; if (*ml_get_pos(&(oap->end)) == NUL && (include_line_break || !virtual_op) ) { @@ -1731,7 +1730,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * oap->empty is set when start and end are the same. The inclusive * flag affects this too, unless yanking and the end is on a NUL. */ - oap->empty = (oap->motion_type != MLINE + oap->empty = (oap->motion_type != kMTLineWise && (!oap->inclusive || (oap->op_type == OP_YANK && gchar_pos(&oap->end) == NUL)) @@ -1756,23 +1755,23 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* * If the end of an operator is in column one while oap->motion_type - * is MCHAR and oap->inclusive is false, we put op_end after the last + * is kMTCharWise and oap->inclusive is false, we put op_end after the last * character in the previous line. If op_start is on or before the * first non-blank in the line, the operator becomes linewise * (strange, but that's the way vi does it). */ - if (oap->motion_type == MCHAR + if (oap->motion_type == kMTCharWise && oap->inclusive == false && !(cap->retval & CA_NO_ADJ_OP_END) && oap->end.col == 0 && (!oap->is_VIsual || *p_sel == 'o') && oap->line_count > 1) { oap->end_adjusted = true; // remember that we did this - --oap->line_count; - --oap->end.lnum; - if (inindent(0)) - oap->motion_type = MLINE; - else { + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); if (oap->end.col) { --oap->end.col; @@ -1811,8 +1810,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) CancelRedo(); } else { (void)op_delete(oap); - if (oap->motion_type == MLINE && has_format_option(FO_AUTO)) - u_save_cursor(); /* cursor line wasn't saved yet */ + if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO)) { + u_save_cursor(); // cursor line wasn't saved yet + } auto_format(false, true); } break; @@ -2011,7 +2011,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* * if 'sol' not set, go back to old column for some commands */ - if (!p_sol && oap->motion_type == MLINE && !oap->end_adjusted + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { curwin->w_p_lbr = false; @@ -2086,13 +2086,14 @@ static void op_function(oparg_T *oap) /* Set '[ and '] marks to text to be operated on. */ curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; - if (oap->motion_type != MLINE && !oap->inclusive) - /* Exclude the end position. */ + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. decl(&curbuf->b_op_end); + } - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { argv[0] = (char_u *)"block"; - } else if (oap->motion_type == MLINE) { + } else if (oap->motion_type == kMTLineWise) { argv[0] = (char_u *)"line"; } else { argv[0] = (char_u *)"char"; @@ -2344,9 +2345,12 @@ do_mouse ( if (mouse_row == 0 && firstwin->w_winrow > 0) { if (is_drag) { if (in_tab_line) { - tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose - ? 9999 - : tab_page_click_defs[mouse_col].tabnr - 1); + if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) { + tabpage_move(9999); + } else { + int tabnr = tab_page_click_defs[mouse_col].tabnr; + tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr); + } } return false; } @@ -2527,7 +2531,7 @@ do_mouse ( */ if (!is_drag && oap != NULL && oap->op_type != OP_NOP) { got_click = false; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } /* When releasing the button let jump_to_mouse() know. */ @@ -2595,11 +2599,10 @@ do_mouse ( end_visual.col = leftcol; else end_visual.col = rightcol; - if (curwin->w_cursor.lnum < - (start_visual.lnum + end_visual.lnum) / 2) - end_visual.lnum = end_visual.lnum; - else + if (curwin->w_cursor.lnum >= + (start_visual.lnum + end_visual.lnum) / 2) { end_visual.lnum = start_visual.lnum; + } /* move VIsual to the right column */ start_visual = curwin->w_cursor; /* save the cursor pos */ @@ -2767,21 +2770,23 @@ do_mouse ( end_visual = curwin->w_cursor; while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) inc(&end_visual); - if (oap != NULL) - oap->motion_type = MCHAR; + if (oap != NULL) { + oap->motion_type = kMTCharWise; + } if (oap != NULL && VIsual_mode == 'v' && !vim_iswordc(gchar_pos(&end_visual)) && equalpos(curwin->w_cursor, VIsual) && (pos = findmatch(oap, NUL)) != NULL) { curwin->w_cursor = *pos; - if (oap->motion_type == MLINE) + if (oap->motion_type == kMTLineWise) { VIsual_mode = 'V'; - else if (*p_sel == 'e') { - if (lt(curwin->w_cursor, VIsual)) - ++VIsual.col; - else - ++curwin->w_cursor.col; + } else if (*p_sel == 'e') { + if (lt(curwin->w_cursor, VIsual)) { + VIsual.col++; + } else { + curwin->w_cursor.col++; + } } } } @@ -3241,9 +3246,9 @@ void clear_showcmd(void) top = curwin->w_cursor.lnum; bot = VIsual.lnum; } - /* Include closed folds as a whole. */ - hasFolding(top, &top, NULL); - hasFolding(bot, NULL, &bot); + // Include closed folds as a whole. + (void)hasFolding(top, &top, NULL); + (void)hasFolding(bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -3779,7 +3784,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) int width1; /* text width for first screen line */ int width2; /* test width for wrapped screen line */ - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); col_off1 = curwin_col_off(); @@ -3924,6 +3929,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; @@ -4050,16 +4057,15 @@ static void nv_zet(cmdarg_T *cap) } dozet: - if ( - /* "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc" - * and "zC" only in Visual mode. "zj" and "zk" are motion - * commands. */ - cap->nchar != 'f' && cap->nchar != 'F' - && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar)) - && cap->nchar != 'j' && cap->nchar != 'k' - && - checkclearop(cap->oap)) + // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc" + // and "zC" only in Visual mode. "zj" and "zk" are motion + // commands. */ + if (cap->nchar != 'f' && cap->nchar != 'F' + && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar)) + && cap->nchar != 'j' && cap->nchar != 'k' + && checkclearop(cap->oap)) { return; + } /* * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb": @@ -4096,6 +4102,7 @@ dozet: case 't': scroll_cursor_top(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "z." and "zz": put cursor in middle of screen */ @@ -4104,6 +4111,7 @@ dozet: case 'z': scroll_cursor_halfway(true); redraw_later(VALID); + set_fraction(curwin); break; /* "z^", "z-" and "zb": put cursor at bottom of screen */ @@ -4124,6 +4132,7 @@ dozet: case 'b': scroll_cursor_bot(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "zH" - scroll screen right half-page */ @@ -4457,8 +4466,8 @@ static void nv_colon(cmdarg_T *cap) nv_operator(cap); else { if (cap->oap->op_type != OP_NOP) { - /* Using ":" as a movement is characterwise exclusive. */ - cap->oap->motion_type = MCHAR; + // Using ":" as a movement is characterwise exclusive. + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; } else if (cap->count0) { /* translate "count:" into ":.,.+(count - 1)" */ @@ -4857,7 +4866,7 @@ static void nv_scroll(cmdarg_T *cap) linenr_T lnum; int half; - cap->oap->motion_type = MLINE; + cap->oap->motion_type = kMTLineWise; setpcmark(); if (cap->cmdchar == 'L') { @@ -4937,7 +4946,7 @@ static void nv_right(cmdarg_T *cap) return; } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; PAST_LINE = (VIsual_active && *p_sel != 'o'); @@ -5024,7 +5033,7 @@ static void nv_left(cmdarg_T *cap) return; } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; for (n = cap->count1; n > 0; --n) { if (oneleft() == false) { @@ -5086,11 +5095,12 @@ static void nv_up(cmdarg_T *cap) cap->arg = BACKWARD; nv_page(cap); } else { - cap->oap->motion_type = MLINE; - if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) + cap->oap->motion_type = kMTLineWise; + if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if (cap->arg) + } else if (cap->arg) { beginline(BL_WHITE | BL_FIX); + } } } @@ -5104,23 +5114,24 @@ static void nv_down(cmdarg_T *cap) /* <S-Down> is page down */ cap->arg = FORWARD; nv_page(cap); - } else - /* In a quickfix window a <CR> jumps to the error under the cursor. */ - if (bt_quickfix(curbuf) && cap->cmdchar == CAR) - if (curwin->w_llist_ref == NULL) - do_cmdline_cmd(".cc"); /* quickfix window */ - else - do_cmdline_cmd(".ll"); /* location list window */ - else { - /* In the cmdline window a <CR> executes the command. */ - if (cmdwin_type != 0 && cap->cmdchar == CAR) + } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) { + // In a quickfix window a <CR> jumps to the error under the cursor. + if (curwin->w_llist_ref == NULL) { + do_cmdline_cmd(".cc"); // quickfix window + } else { + do_cmdline_cmd(".ll"); // location list window + } + } else { + // In the cmdline window a <CR> executes the command. + if (cmdwin_type != 0 && cap->cmdchar == CAR) { cmdwin_result = CAR; - else { - cap->oap->motion_type = MLINE; - if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) + } else { + cap->oap->motion_type = kMTLineWise; + if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if (cap->arg) + } else if (cap->arg) { beginline(BL_WHITE | BL_FIX); + } } } } @@ -5146,9 +5157,10 @@ static void nv_gotofile(cmdarg_T *cap) ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { - /* do autowrite if necessary */ - if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) - autowrite(curbuf, false); + // do autowrite if necessary + if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) { + (void)autowrite(curbuf, false); + } setpcmark(); (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST, P_HID(curbuf) ? ECMD_HIDE : 0, curwin); @@ -5180,7 +5192,7 @@ static void nv_end(cmdarg_T *cap) */ static void nv_dollar(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = true; /* In virtual mode when off the edge of a line and an operator * is pending (whew!) keep the cursor where it is. @@ -5255,18 +5267,19 @@ static int normal_search( { int i; - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; i = do_search(cap->oap, dir, pat, cap->count1, - opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); - if (i == 0) + opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); + if (i == 0) { clearop(cap->oap); - else { - if (i == 2) - cap->oap->motion_type = MLINE; + } else { + if (i == 2) { + cap->oap->motion_type = kMTLineWise; + } curwin->w_cursor.coladd = 0; if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) foldOpenCursor(); @@ -5293,10 +5306,10 @@ static void nv_csearch(cmdarg_T *cap) else t_cmd = false; - cap->oap->motion_type = MCHAR; - if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) + cap->oap->motion_type = kMTCharWise; + if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) { clearopbeep(cap->oap); - else { + } else { curwin->w_set_curswant = true; /* Include a Tab for "tx" and for "dfx". */ if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD @@ -5328,7 +5341,7 @@ static void nv_brackets(cmdarg_T *cap) int findc; int c; - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; old_pos = curwin->w_cursor; curwin->w_cursor.coladd = 0; /* TODO: don't do this for an error. */ @@ -5618,11 +5631,11 @@ static void nv_percent(cmdarg_T *cap) linenr_T lnum = curwin->w_cursor.lnum; cap->oap->inclusive = true; - if (cap->count0) { /* {cnt}% : goto {cnt} percentage in file */ - if (cap->count0 > 100) + if (cap->count0) { // {cnt}% : goto {cnt} percentage in file + if (cap->count0 > 100) { clearopbeep(cap->oap); - else { - cap->oap->motion_type = MLINE; + } else { + cap->oap->motion_type = kMTLineWise; setpcmark(); /* Round up, so CTRL-G will give same value. Watch out for a * large line count, the line number must not go negative! */ @@ -5636,8 +5649,8 @@ static void nv_percent(cmdarg_T *cap) curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; beginline(BL_SOL | BL_FIX); } - } else { /* "%" : go to matching paren */ - cap->oap->motion_type = MCHAR; + } else { // "%" : go to matching paren + cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; if ((pos = findmatch(cap->oap, NUL)) == NULL) clearopbeep(cap->oap); @@ -5662,7 +5675,7 @@ static void nv_percent(cmdarg_T *cap) */ static void nv_brace(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; /* The motion used to be inclusive for "(", but that is not what Vi does. */ cap->oap->inclusive = false; @@ -5696,7 +5709,7 @@ static void nv_mark(cmdarg_T *cap) */ static void nv_findpar(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; @@ -5800,14 +5813,11 @@ static void nv_replace(cmdarg_T *cap) return; } - /* - * Replacing with a TAB is done by edit() when it is complicated because - * 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB. - * Other characters are done below to avoid problems with things like - * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC). - */ - if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && - (curbuf->b_p_et || p_sta)) { + // Replacing with a TAB is done by edit() when it is complicated because + // 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB. + // Other characters are done below to avoid problems with things like + // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC). + if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta)) { stuffnumReadbuff(cap->count1); stuffcharReadbuff('R'); stuffcharReadbuff('\t'); @@ -6074,10 +6084,11 @@ static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos) else check_cursor(); } - cap->oap->motion_type = flag ? MLINE : MCHAR; - if (cap->cmdchar == '`') + cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise; + if (cap->cmdchar == '`') { cap->oap->use_reg_one = true; - cap->oap->inclusive = false; /* ignored if not MCHAR */ + } + cap->oap->inclusive = false; // ignored if not kMTCharWise curwin->w_set_curswant = true; } @@ -6554,7 +6565,7 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap || hasFolding(curwin->w_cursor.lnum, NULL, NULL) ) { - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; i = cursor_down(cap->count1, oap->op_type == OP_NOP); } else i = nv_screengo(oap, FORWARD, cap->count1); @@ -6569,7 +6580,7 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap || hasFolding(curwin->w_cursor.lnum, NULL, NULL) ) { - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; i = cursor_up(cap->count1, oap->op_type == OP_NOP); } else i = nv_screengo(oap, BACKWARD, cap->count1); @@ -6596,7 +6607,7 @@ static void nv_g_cmd(cmdarg_T *cap) case 'm': case K_HOME: case K_KHOME: - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = false; if (curwin->w_p_wrap && curwin->w_width != 0 @@ -6629,7 +6640,7 @@ static void nv_g_cmd(cmdarg_T *cap) case '_': /* "g_": to the last non-blank character in the line or <count> lines * downward. */ - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = true; curwin->w_curswant = MAXCOL; if (cursor_down(cap->count1 - 1, @@ -6657,7 +6668,7 @@ static void nv_g_cmd(cmdarg_T *cap) { int col_off = curwin_col_off(); - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = true; if (curwin->w_p_wrap && curwin->w_width != 0 @@ -6719,23 +6730,19 @@ static void nv_g_cmd(cmdarg_T *cap) */ case 'e': case 'E': - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; oap->inclusive = true; if (bckend_word(cap->count1, cap->nchar == 'E', false) == false) clearopbeep(oap); break; - /* - * "g CTRL-G": display info about cursor position - */ + // "g CTRL-G": display info about cursor position case Ctrl_G: - cursor_pos_info(); + cursor_pos_info(NULL); break; - /* - * "gi": start Insert at the last position. - */ + // "gi": start Insert at the last position. case 'i': if (curbuf->b_last_insert.mark.lnum != 0) { curwin->w_cursor = curbuf->b_last_insert.mark; @@ -7051,18 +7058,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); @@ -7080,17 +7086,19 @@ static void set_op_var(int optype) */ static void nv_lineop(cmdarg_T *cap) { - cap->oap->motion_type = MLINE; - if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) + cap->oap->motion_type = kMTLineWise; + if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if ( (cap->oap->op_type == OP_DELETE /* only with linewise motions */ + } else if ((cap->oap->op_type == OP_DELETE + // only with linewise motions && cap->oap->motion_force != 'v' && cap->oap->motion_force != Ctrl_V) || cap->oap->op_type == OP_LSHIFT - || cap->oap->op_type == OP_RSHIFT) + || cap->oap->op_type == OP_RSHIFT) { beginline(BL_SOL | BL_FIX); - else if (cap->oap->op_type != OP_YANK) /* 'Y' does not move cursor */ + } else if (cap->oap->op_type != OP_YANK) { // 'Y' does not move cursor beginline(BL_WHITE | BL_FIX); + } } /* @@ -7114,7 +7122,7 @@ static void nv_home(cmdarg_T *cap) */ static void nv_pipe(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; beginline(0); if (cap->count0 > 0) { @@ -7133,7 +7141,7 @@ static void nv_pipe(cmdarg_T *cap) */ static void nv_bck_word(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; curwin->w_set_curswant = true; if (bck_word(cap->count1, cap->arg, false) == false) @@ -7182,7 +7190,7 @@ static void nv_wordcmd(cmdarg_T *cap) } } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; if (word_end) n = end_word(cap->count1, cap->arg, flag, false); @@ -7233,7 +7241,7 @@ static void adjust_cursor(oparg_T *oap) */ static void nv_beginline(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; beginline(cap->arg); if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) @@ -7312,7 +7320,7 @@ static void nv_goto(cmdarg_T *cap) lnum = curbuf->b_ml.ml_line_count; else lnum = 1L; - cap->oap->motion_type = MLINE; + cap->oap->motion_type = kMTLineWise; setpcmark(); /* When a count is given, use it instead of the default lnum */ @@ -7640,19 +7648,25 @@ static void nv_halfpage(cmdarg_T *cap) */ static void nv_join(cmdarg_T *cap) { - if (VIsual_active) /* join the visual lines */ + if (VIsual_active) { // join the visual lines nv_operator(cap); - else if (!checkclearop(cap->oap)) { - if (cap->count0 <= 1) - cap->count0 = 2; /* default for join is two lines! */ + } else if (!checkclearop(cap->oap)) { + if (cap->count0 <= 1) { + cap->count0 = 2; // default for join is two lines! + } if (curwin->w_cursor.lnum + cap->count0 - 1 > - curbuf->b_ml.ml_line_count) - clearopbeep(cap->oap); /* beyond last line */ - else { - prep_redo(cap->oap->regname, cap->count0, - NUL, cap->cmdchar, NUL, NUL, cap->nchar); - do_join(cap->count0, cap->nchar == NUL, true, true, true); + curbuf->b_ml.ml_line_count) { + // can't join when on the last line + if (cap->count0 <= 2) { + clearopbeep(cap->oap); + return; + } + cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1; } + + prep_redo(cap->oap->regname, cap->count0, + NUL, cap->cmdchar, NUL, NUL, cap->nchar); + do_join(cap->count0, cap->nchar == NUL, true, true, true); } } @@ -7798,7 +7812,7 @@ static void get_op_vcol( return; } - oap->motion_type = MBLOCK; + oap->motion_type = kMTBlockWise; // prevent from moving onto a trail byte if (has_mbyte) { diff --git a/src/nvim/normal.h b/src/nvim/normal.h index 95619c7ef6..51170105ed 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -10,18 +10,29 @@ #define FIND_STRING 2 /* find any string (WORD) */ #define FIND_EVAL 4 /* include "->", "[]" and "." */ +/// Motion types, used for operators and for yank/delete registers. +/// +/// The three valid numerical values must not be changed, as they +/// are used in external communication and serialization. +typedef enum { + kMTCharWise = 0, ///< character-wise movement/register + kMTLineWise = 1, ///< line-wise movement/register + kMTBlockWise = 2, ///< block-wise movement/register + kMTUnknown = -1 ///< Unknown or invalid motion type +} MotionType; + /* * Arguments for operators. */ typedef struct oparg_S { int op_type; // current pending operator type int regname; // register to use for the operator - int motion_type; // type of the current cursor motion + MotionType motion_type; // type of the current cursor motion int motion_force; // force motion type: 'v', 'V' or CTRL-V bool use_reg_one; // true if delete uses reg 1 even when not // linewise bool inclusive; // true if char motion is inclusive (only - // valid when motion_type is MCHAR) + // valid when motion_type is kMTCharWise) bool end_adjusted; // backuped b_op_end one char (only used by // do_format()) pos_T start; // start of the operator diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b1adc85e1d..eda963ff77 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -19,6 +19,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" +#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" @@ -190,7 +191,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) (linenr_T)(oap->end.lnum + 1)) == FAIL) return; - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { block_col = curwin->w_cursor.col; } @@ -198,7 +199,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) first_char = *get_cursor_line_ptr(); if (first_char == NUL) { // empty line curwin->w_cursor.col = 0; - } else if (oap->motion_type == MBLOCK) { + } else if (oap->motion_type == kMTBlockWise) { shift_block(oap, amount); } else if (first_char != '#' || !preprocs_left()) { // Move the line right if it doesn't start with '#', 'smartindent' @@ -212,7 +213,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) /* The cursor line is not in a closed fold */ foldOpenCursor(); - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { curwin->w_cursor.lnum = oap->start.lnum; curwin->w_cursor.col = block_col; } else if (curs_top) { /* put cursor on first line, for ">>" */ @@ -810,17 +811,17 @@ yankreg_T *copy_register(int name) return copy; } -/* - * return TRUE if the current yank register has type MLINE - */ -int yank_register_mline(int regname) +/// check if the current yank register has kMTLineWise register type +bool yank_register_mline(int regname) { - if (regname != 0 && !valid_yank_reg(regname, false)) - return FALSE; - if (regname == '_') /* black hole is always empty */ - return FALSE; + if (regname != 0 && !valid_yank_reg(regname, false)) { + return false; + } + if (regname == '_') { // black hole is always empty + return false; + } yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - return reg->y_type == MLINE; + return reg->y_type == kMTLineWise; } /* @@ -835,12 +836,13 @@ int do_record(int c) yankreg_T *old_y_previous; int retval; - if (Recording == FALSE) { /* start recording */ - /* registers 0-9, a-z and " are allowed */ - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) + if (Recording == false) { + // start recording + // registers 0-9, a-z and " are allowed + if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { retval = FAIL; - else { - Recording = TRUE; + } else { + Recording = c; showmode(); regname = c; retval = OK; @@ -917,7 +919,7 @@ static int stuff_yank(int regname, char_u *p) reg->y_array = (char_u **)xmalloc(sizeof(char_u *)); reg->y_array[0] = p; reg->y_size = 1; - reg->y_type = MCHAR; /* used to be MLINE, why? */ + reg->y_type = kMTCharWise; } reg->timestamp = os_time(); return OK; @@ -1009,8 +1011,8 @@ do_execreg ( for (i = reg->y_size - 1; i >= 0; i--) { char_u *escaped; - /* insert NL between lines and after last line if type is MLINE */ - if (reg->y_type == MLINE || i < reg->y_size - 1 + // insert NL between lines and after last line if type is kMTLineWise + if (reg->y_type == kMTLineWise || i < reg->y_size - 1 || addcr) { if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL) return FAIL; @@ -1135,12 +1137,11 @@ insert_reg ( else { for (i = 0; i < reg->y_size; i++) { stuffescaped(reg->y_array[i], literally); - /* - * Insert a newline between lines and after last line if - * y_type is MLINE. - */ - if (reg->y_type == MLINE || i < reg->y_size - 1) + // Insert a newline between lines and after last line if + // y_type is kMTLineWise. + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { stuffcharReadbuff('\n'); + } } } } @@ -1259,21 +1260,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; @@ -1284,13 +1282,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 kMTLineWise. + // Don't do this when "remcr" is true. + if ((reg->y_type == kMTLineWise || i < reg->y_size - 1) && !remcr) { cmdline_paste_str((char_u *)"\r", literally); } @@ -1334,10 +1328,10 @@ int op_delete(oparg_T *oap) /* * Imitate the strange Vi behaviour: If the delete spans more than one - * line and motion_type == MCHAR and the result is a blank line, make the + * line and motion_type == kMTCharWise and the result is a blank line, make the * delete linewise. Don't do this for the change command or Visual mode. */ - if (oap->motion_type == MCHAR + if (oap->motion_type == kMTCharWise && !oap->is_VIsual && oap->line_count > 1 && oap->motion_force == NUL @@ -1346,15 +1340,16 @@ int op_delete(oparg_T *oap) if (*ptr != NUL) ptr += oap->inclusive; ptr = skipwhite(ptr); - if (*ptr == NUL && inindent(0)) - oap->motion_type = MLINE; + if (*ptr == NUL && inindent(0)) { + oap->motion_type = kMTLineWise; + } } /* * Check for trying to delete (e.g. "D") in an empty line. * Note: For the change operator it is ok. */ - if (oap->motion_type != MLINE + if (oap->motion_type != kMTLineWise && oap->line_count == 1 && oap->op_type == OP_DELETE && *ml_get(oap->start.lnum) == NUL) { @@ -1390,7 +1385,7 @@ int op_delete(oparg_T *oap) * Put deleted text into register 1 and shift number registers if the * delete contains a line break, or when a regname has been specified. */ - if (oap->regname != 0 || oap->motion_type == MLINE + if (oap->regname != 0 || oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { free_register(&y_regs[9]); /* free register "9 */ for (n = 9; n > 1; n--) @@ -1403,14 +1398,15 @@ int op_delete(oparg_T *oap) /* Yank into small delete register when no named register specified * and the delete is within one line. */ - if (oap->regname == 0 && oap->motion_type != MLINE + if (oap->regname == 0 && oap->motion_type != kMTLineWise && oap->line_count == 1) { reg = get_yank_register('-', YREG_YANK); op_yank_reg(oap, false, reg, false); } - if(oap->regname == 0) { + if (oap->regname == 0) { set_clipboard(0, reg); + yank_do_autocmd(oap, reg); } } @@ -1418,7 +1414,7 @@ int op_delete(oparg_T *oap) /* * block mode delete */ - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { if (u_save((linenr_T)(oap->start.lnum - 1), (linenr_T)(oap->end.lnum + 1)) == FAIL) { return FAIL; @@ -1456,9 +1452,9 @@ int op_delete(oparg_T *oap) check_cursor_col(); changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col, - oap->end.lnum + 1, 0L); - oap->line_count = 0; /* no lines deleted */ - } else if (oap->motion_type == MLINE) { + oap->end.lnum + 1, 0L); + oap->line_count = 0; // no lines deleted + } else if (oap->motion_type == kMTLineWise) { if (oap->op_type == OP_CHANGE) { /* Delete the lines except the first one. Temporarily move the * cursor to the next line. Save the current line number, if the @@ -1586,7 +1582,7 @@ int op_delete(oparg_T *oap) msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { curbuf->b_op_end.lnum = oap->end.lnum; curbuf->b_op_end.col = oap->start.col; } else @@ -1647,7 +1643,7 @@ int op_replace(oparg_T *oap, int c) /* * block mode replace */ - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { bd.is_MAX = (curwin->w_curswant == MAXCOL); for (; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { curwin->w_cursor.col = 0; /* make sure cursor position is valid */ @@ -1736,10 +1732,8 @@ int op_replace(oparg_T *oap, int c) } } } else { - /* - * MCHAR and MLINE motion replace. - */ - if (oap->motion_type == MLINE) { + // Characterwise or linewise motion replace. + if (oap->motion_type == kMTLineWise) { oap->start.col = 0; curwin->w_cursor.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); @@ -1829,8 +1823,8 @@ void op_tilde(oparg_T *oap) return; pos = oap->start; - if (oap->motion_type == MBLOCK) { // Visual block mode - for (; pos.lnum <= oap->end.lnum; ++pos.lnum) { + if (oap->motion_type == kMTBlockWise) { // Visual block mode + for (; pos.lnum <= oap->end.lnum; pos.lnum++) { int one_change; block_prep(oap, &bd, pos.lnum, FALSE); @@ -1841,8 +1835,8 @@ void op_tilde(oparg_T *oap) } if (did_change) changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); - } else { /* not block mode */ - if (oap->motion_type == MLINE) { + } else { // not block mode + if (oap->motion_type == kMTLineWise) { oap->start.col = 0; pos.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); @@ -1992,7 +1986,7 @@ void op_insert(oparg_T *oap, long count1) curwin->w_cursor.lnum = oap->start.lnum; update_screen(INVERTED); - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { // When 'virtualedit' is used, need to insert the extra spaces before // doing block_prep(). When only "block" is used, virtual edit is // already disabled, but still need it when calling @@ -2018,7 +2012,7 @@ void op_insert(oparg_T *oap, long count1) } if (oap->op_type == OP_APPEND) { - if (oap->motion_type == MBLOCK + if (oap->motion_type == kMTBlockWise && curwin->w_cursor.coladd == 0 ) { /* Move the cursor to the character right of the block. */ @@ -2052,8 +2046,8 @@ void op_insert(oparg_T *oap, long count1) // When a tab was inserted, and the characters in front of the tab // have been converted to a tab as well, the column of the cursor // might have actually been reduced, so need to adjust here. */ - if (t1.lnum == curbuf->b_op_start_orig.lnum && - lt(curbuf->b_op_start_orig, t1)) { + if (t1.lnum == curbuf->b_op_start_orig.lnum + && lt(curbuf->b_op_start_orig, t1)) { oap->start = curbuf->b_op_start_orig; } @@ -2063,7 +2057,7 @@ void op_insert(oparg_T *oap, long count1) if (curwin->w_cursor.lnum != oap->start.lnum || got_int) return; - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { struct block_def bd2; /* The user may have moved the cursor before inserting something, try @@ -2150,7 +2144,7 @@ int op_change(oparg_T *oap) struct block_def bd; l = oap->start.col; - if (oap->motion_type == MLINE) { + if (oap->motion_type == kMTLineWise) { l = 0; if (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin @@ -2172,7 +2166,7 @@ int op_change(oparg_T *oap) // check for still on same line (<CR> in inserted text meaningless) // skip blank lines too - if (oap->motion_type == MBLOCK) { + if (oap->motion_type == kMTBlockWise) { // Add spaces before getting the current line length. if (virtual_op && (curwin->w_cursor.coladd > 0 || gchar_cursor() == NUL)) { @@ -2184,8 +2178,9 @@ int op_change(oparg_T *oap) bd.textcol = curwin->w_cursor.col; } - if (oap->motion_type == MLINE) + if (oap->motion_type == kMTLineWise) { fix_indent(); + } retval = edit(NUL, FALSE, (linenr_T)1); @@ -2194,7 +2189,7 @@ int op_change(oparg_T *oap) * block. * Don't repeat the insert when Insert mode ended with CTRL-C. */ - if (oap->motion_type == MBLOCK + if (oap->motion_type == kMTBlockWise && oap->start.lnum != oap->end.lnum && !got_int) { // Auto-indenting may have changed the indent. If the cursor was past // the indent, exclude that indent change from the inserted text. @@ -2309,6 +2304,8 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); set_clipboard(oap->regname, reg); + yank_do_autocmd(oap, reg); + return true; } @@ -2320,7 +2317,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) char_u **new_ptr; linenr_T lnum; /* current line number */ long j; - int yanktype = oap->motion_type; + MotionType yank_type = oap->motion_type; long yanklines = oap->line_count; linenr_T yankendlnum = oap->end.lnum; char_u *p; @@ -2338,19 +2335,19 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) * If the cursor was in column 1 before and after the movement, and the * operator is not inclusive, the yank is always linewise. */ - if (oap->motion_type == MCHAR + if (oap->motion_type == kMTCharWise && oap->start.col == 0 && !oap->inclusive && (!oap->is_VIsual || *p_sel == 'o') && oap->end.col == 0 && yanklines > 1) { - yanktype = MLINE; - --yankendlnum; - --yanklines; + yank_type = kMTLineWise; + yankendlnum--; + yanklines--; } reg->y_size = yanklines; - reg->y_type = yanktype; /* set the yank register type */ + reg->y_type = yank_type; // set the yank register type reg->y_width = 0; reg->y_array = xcalloc(yanklines, sizeof(char_u *)); reg->additional_data = NULL; @@ -2359,7 +2356,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) y_idx = 0; lnum = oap->start.lnum; - if (yanktype == MBLOCK) { + if (yank_type == kMTBlockWise) { // Visual block mode reg->y_width = oap->end_vcol - oap->start_vcol; @@ -2369,16 +2366,16 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) for (; lnum <= yankendlnum; lnum++, y_idx++) { switch (reg->y_type) { - case MBLOCK: - block_prep(oap, &bd, lnum, FALSE); + case kMTBlockWise: + block_prep(oap, &bd, lnum, false); yank_copy_line(reg, &bd, y_idx); break; - case MLINE: + case kMTLineWise: reg->y_array[y_idx] = vim_strsave(ml_get(lnum)); break; - case MCHAR: + case kMTCharWise: { colnr_T startcol = 0, endcol = MAXCOL; int is_oneChar = FALSE; @@ -2439,7 +2436,9 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) yank_copy_line(reg, &bd, y_idx); break; } - /* NOTREACHED */ + // NOTREACHED + case kMTUnknown: + assert(false); } } @@ -2450,12 +2449,15 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(curr->y_array); curr->y_array = new_ptr; - if (yanktype == MLINE) /* MLINE overrides MCHAR and MBLOCK */ - curr->y_type = MLINE; + if (yank_type == kMTLineWise) { + // kMTLineWise overrides kMTCharWise and kMTBlockWise + curr->y_type = kMTLineWise; + } - /* Concatenate the last line of the old block with the first line of - * the new block, unless being Vi compatible. */ - if (curr->y_type == MCHAR && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) { + // Concatenate the last line of the old block with the first line of + // the new block, unless being Vi compatible. + if (curr->y_type == kMTCharWise + && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) { pnew = xmalloc(STRLEN(curr->y_array[curr->y_size - 1]) + STRLEN(reg->y_array[0]) + 1); STRCPY(pnew, curr->y_array[--j]); @@ -2475,7 +2477,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) redraw_later(SOME_VALID); // cursor moved to start } if (message) { // Display message about yank? - if (yanktype == MCHAR && yanklines == 1) { + if (yank_type == kMTCharWise && yanklines == 1) { yanklines = 0; } // Some versions of Vi use ">=" here, some don't... @@ -2483,12 +2485,12 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) // redisplay now, so message is not deleted update_topline_redraw(); if (yanklines == 1) { - if (yanktype == MBLOCK) { + if (yank_type == kMTBlockWise) { MSG(_("block of 1 line yanked")); } else { MSG(_("1 line yanked")); } - } else if (yanktype == MBLOCK) { + } else if (yank_type == kMTBlockWise) { smsg(_("block of %" PRId64 " lines yanked"), (int64_t)yanklines); } else { @@ -2502,7 +2504,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) */ curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; - if (yanktype == MLINE) { + if (yank_type == kMTLineWise) { curbuf->b_op_start.col = 0; curbuf->b_op_end.col = MAXCOL; } @@ -2524,6 +2526,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx) *pnew = NUL; } +/// Execute autocommands for TextYankPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + + if (recursive || !has_event(EVENT_TEXTYANKPOST)) { + // No autocommand was defined + // or we yanked from this autocommand. + return; + } + + recursive = true; + + // set v:event to a dictionary with information about the yank + dict_T *dict = get_vim_var_dict(VV_EVENT); + + // the yanked text + list_T *list = list_alloc(); + for (linenr_T i = 0; i < reg->y_size; i++) { + list_append_string(list, reg->y_array[i], -1); + } + list->lv_lock = VAR_FIXED; + dict_add_list(dict, "regcontents", list); + + // the register type + char buf[NUMBUFLEN+2]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + + // name of requested register or the empty string for an unnamed operation. + buf[0] = (char)oap->regname; + buf[1] = NUL; + dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + + // kind of operation (yank/delete/change) + buf[0] = get_op_char(oap->op_type); + buf[1] = NUL; + dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + + dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); + textlock--; + dict_clear(dict); + + recursive = false; +} + /* * Put contents of register "regname" into the text. @@ -2540,8 +2594,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) int totlen = 0; /* init for gcc */ linenr_T lnum; colnr_T col; - long i; /* index in y_array[] */ - int y_type; + long i; // index in y_array[] + MotionType y_type; long y_size; int oldlen; long y_width = 0; @@ -2602,7 +2656,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } if (insert_string != NULL) { - y_type = MCHAR; + y_type = kMTCharWise; if (regname == '=') { /* For the = register we need to split the string at NL * characters. @@ -2621,7 +2675,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) ++ptr; /* A trailing '\n' makes the register linewise. */ if (*ptr == NUL) { - y_type = MLINE; + y_type = kMTLineWise; break; } } @@ -2662,7 +2716,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) return; } - if (y_type == MLINE) { + if (y_type == kMTLineWise) { if (flags & PUT_LINE_SPLIT) { // "p" or "P" in Visual mode: split the lines to put the text in // between. @@ -2696,8 +2750,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curbuf->b_op_end = curwin->w_cursor; /* default for '] mark */ } - if (flags & PUT_LINE) /* :put command or "p" in Visual line mode. */ - y_type = MLINE; + if (flags & PUT_LINE) { // :put command or "p" in Visual line mode. + y_type = kMTLineWise; + } if (y_size == 0 || y_array == NULL) { EMSG2(_("E353: Nothing in register %s"), @@ -2705,13 +2760,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) goto end; } - if (y_type == MBLOCK) { + if (y_type == kMTBlockWise) { lnum = curwin->w_cursor.lnum + y_size + 1; if (lnum > curbuf->b_ml.ml_line_count) lnum = curbuf->b_ml.ml_line_count + 1; if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL) goto end; - } else if (y_type == MLINE) { + } else if (y_type == kMTLineWise) { lnum = curwin->w_cursor.lnum; /* Correct line number for closed fold. Don't move the cursor yet, * u_save() uses it. */ @@ -2735,7 +2790,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) yanklen = (int)STRLEN(y_array[0]); - if (ve_flags == VE_ALL && y_type == MCHAR) { + if (ve_flags == VE_ALL && y_type == kMTCharWise) { if (gchar_cursor() == TAB) { /* Don't need to insert spaces when "p" on the last position of a * tab or "P" on the first position. */ @@ -2755,7 +2810,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* * Block mode */ - if (y_type == MBLOCK) { + if (y_type == kMTBlockWise) { char c = gchar_cursor(); colnr_T endcol2 = 0; @@ -2903,12 +2958,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } else curwin->w_cursor.lnum = lnum; } else { - /* - * Character or Line mode - */ - if (y_type == MCHAR) { - /* if type is MCHAR, FORWARD is the same as BACKWARD on the next - * char */ + // Character or Line mode + if (y_type == kMTCharWise) { + // if type is kMTCharWise, FORWARD is the same as BACKWARD on the next + // char if (dir == FORWARD && gchar_cursor() != NUL) { if (has_mbyte) { int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr()); @@ -2939,7 +2992,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* * simple case: insert into current line */ - if (y_type == MCHAR && y_size == 1) { + if (y_type == kMTCharWise && y_size == 1) { do { totlen = count * yanklen; if (totlen > 0) { @@ -2974,18 +3027,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) ++curwin->w_cursor.col; changed_bytes(lnum, col); } else { - /* - * Insert at least one line. When y_type is MCHAR, break the first - * line in two. - */ - for (cnt = 1; cnt <= count; ++cnt) { + // Insert at least one line. When y_type is kMTCharWise, break the first + // line in two. + for (cnt = 1; cnt <= count; cnt++) { i = 0; - if (y_type == MCHAR) { - /* - * Split the current line in two at the insert position. - * First insert y_array[size - 1] in front of second line. - * Then append y_array[0] to first line. - */ + if (y_type == kMTCharWise) { + // Split the current line in two at the insert position. + // First insert y_array[size - 1] in front of second line. + // Then append y_array[0] to first line. lnum = new_cursor.lnum; ptr = ml_get(lnum) + col; totlen = (int)STRLEN(y_array[y_size - 1]); @@ -3009,10 +3058,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } for (; i < y_size; i++) { - if ((y_type != MCHAR || i < y_size - 1) - && ml_append(lnum, y_array[i], (colnr_T)0, FALSE) - == FAIL) + if ((y_type != kMTCharWise || i < y_size - 1) + && ml_append(lnum, y_array[i], (colnr_T)0, false) + == FAIL) { goto error; + } lnum++; ++nr_lines; if (flags & PUT_FIXINDENT) { @@ -3041,22 +3091,23 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } error: - /* Adjust marks. */ - if (y_type == MLINE) { + // Adjust marks. + if (y_type == kMTLineWise) { curbuf->b_op_start.col = 0; if (dir == FORWARD) curbuf->b_op_start.lnum++; } - mark_adjust(curbuf->b_op_start.lnum + (y_type == MCHAR), - (linenr_T)MAXLNUM, nr_lines, 0L); + mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise), + (linenr_T)MAXLNUM, nr_lines, 0L); - /* note changed text for displaying and folding */ - if (y_type == MCHAR) + // note changed text for displaying and folding + if (y_type == kMTCharWise) { changed_lines(curwin->w_cursor.lnum, col, - curwin->w_cursor.lnum + 1, nr_lines); - else + curwin->w_cursor.lnum + 1, nr_lines); + } else { changed_lines(curbuf->b_op_start.lnum, 0, - curbuf->b_op_start.lnum, nr_lines); + curbuf->b_op_start.lnum, nr_lines); + } /* put '] mark at last inserted character */ curbuf->b_op_end.lnum = lnum; @@ -3072,19 +3123,20 @@ error: curwin->w_cursor.lnum = lnum; beginline(BL_WHITE | BL_FIX); } else if (flags & PUT_CURSEND) { - /* put cursor after inserted text */ - if (y_type == MLINE) { - if (lnum >= curbuf->b_ml.ml_line_count) + // put cursor after inserted text + if (y_type == kMTLineWise) { + if (lnum >= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_cursor.lnum = lnum + 1; + } curwin->w_cursor.col = 0; } else { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = col; } - } else if (y_type == MLINE) { - /* put cursor on first non-blank in first inserted line */ + } else if (y_type == kMTLineWise) { + // put cursor on first non-blank in first inserted line curwin->w_cursor.col = 0; if (dir == FORWARD) ++curwin->w_cursor.lnum; @@ -3137,11 +3189,9 @@ void adjust_cursor_eol(void) */ int preprocs_left(void) { - return - (curbuf->b_p_si && !curbuf->b_p_cin) || - (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) - && curbuf->b_ind_hash_comment == 0) - ; + return ((curbuf->b_p_si && !curbuf->b_p_cin) + || (curbuf->b_p_cin && in_cinkeys('#', ' ', true) + && curbuf->b_ind_hash_comment == 0)); } /* Return the character name of the register with the given number */ @@ -3223,9 +3273,10 @@ void ex_display(exarg_T *eap) p += clen - 1; } } - if (n > 1 && yb->y_type == MLINE) + if (n > 1 && yb->y_type == kMTLineWise) { MSG_PUTS_ATTR("^J", attr); - ui_flush(); /* show one line at a time */ + } + ui_flush(); // show one line at a time } os_breakcheck(); } @@ -3939,7 +3990,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 */ @@ -4215,18 +4266,18 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) } pos = oap->start; - for (; pos.lnum <= oap->end.lnum; ++pos.lnum) { - if (oap->motion_type == MBLOCK) { + for (; pos.lnum <= oap->end.lnum; pos.lnum++) { + if (oap->motion_type == kMTBlockWise) { // Visual block mode block_prep(oap, &bd, pos.lnum, false); pos.col = bd.textcol; length = bd.textlen; - } else if (oap->motion_type == MLINE) { + } else if (oap->motion_type == kMTLineWise) { curwin->w_cursor.col = 0; pos.col = 0; length = (colnr_T)STRLEN(ml_get(pos.lnum)); } else { - // oap->motion_type == MCHAR + // oap->motion_type == kMTCharWise if (!oap->inclusive) { dec(&(oap->end)); } @@ -4346,8 +4397,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if (dobin && dohex && !((col > 0 - && (ptr[col] == 'X' || - ptr[col] == 'x') + && (ptr[col] == 'X' || ptr[col] == 'x') && ptr[col - 1] == '0' && ascii_isxdigit(ptr[col + 1])))) { // In case of binary/hexadecimal pattern overlap match, rescan @@ -4361,17 +4411,15 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) if ((dohex && col > 0 - && (ptr[col] == 'X' - || ptr[col] == 'x') - && ptr[col - 1] == '0' - && ascii_isxdigit(ptr[col + 1])) || - (dobin - && col > 0 - && (ptr[col] == 'B' - || ptr[col] == 'b') + && (ptr[col] == 'X' || ptr[col] == 'x') && ptr[col - 1] == '0' - && ascii_isbdigit(ptr[col + 1]))) { - // Found hexadecimal or binary number, move to its start. + && ascii_isxdigit(ptr[col + 1])) + || (dobin + && col > 0 + && (ptr[col] == 'B' || ptr[col] == 'b') + && ptr[col - 1] == '0' + && ascii_isbdigit(ptr[col + 1]))) { + // Found hexadecimal or binary number, move to its start. col--; } else { // Search forward and then backward to find the start of number. @@ -4392,8 +4440,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } if (visual) { - while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) && - !(doalp && ASCII_ISALPHA(ptr[col]))) { + while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { col++; length--; } @@ -4555,8 +4603,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) *ptr++ = '0'; length--; } - if (pre == 'b' || pre == 'B' || - pre == 'x' || pre == 'X') { + if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { *ptr++ = pre; length--; } @@ -4629,38 +4676,71 @@ theend: /* * Return the type of a register. * Used for getregtype() - * Returns MAUTO for error. + * Returns kMTUnknown for error. */ -char_u get_reg_type(int regname, long *reglen) +MotionType get_reg_type(int regname, colnr_T *reg_width) { switch (regname) { - case '%': /* file name */ - case '#': /* alternate file name */ - case '=': /* expression */ - case ':': /* last command line */ - case '/': /* last search-pattern */ - case '.': /* last inserted text */ - case Ctrl_F: /* Filename under cursor */ - case Ctrl_P: /* Path under cursor, expand via "path" */ - case Ctrl_W: /* word under cursor */ - case Ctrl_A: /* WORD (mnemonic All) under cursor */ - case '_': /* black hole: always empty */ - return MCHAR; + case '%': // file name + case '#': // alternate file name + case '=': // expression + case ':': // last command line + case '/': // last search-pattern + case '.': // last inserted text + case Ctrl_F: // Filename under cursor + case Ctrl_P: // Path under cursor, expand via "path" + case Ctrl_W: // word under cursor + case Ctrl_A: // WORD (mnemonic All) under cursor + case '_': // black hole: always empty + return kMTCharWise; } - if (regname != NUL && !valid_yank_reg(regname, false)) - return MAUTO; + if (regname != NUL && !valid_yank_reg(regname, false)) { + return kMTUnknown; + } yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array != NULL) { - if (reglen != NULL && reg->y_type == MBLOCK) - *reglen = reg->y_width; + if (reg_width != NULL && reg->y_type == kMTBlockWise) { + *reg_width = reg->y_width; + } return reg->y_type; } - return MAUTO; + return kMTUnknown; } +/// Format the register type as a string. +/// +/// @param reg_type The register type. +/// @param reg_width The width, only used if "reg_type" is kMTBlockWise. +/// @param[out] buf Buffer to store formatted string. The allocated size should +/// be at least NUMBUFLEN+2 to always fit the value. +/// @param buf_len The allocated size of the buffer. +void format_reg_type(MotionType reg_type, colnr_T reg_width, + char *buf, size_t buf_len) + FUNC_ATTR_NONNULL_ALL +{ + assert(buf_len > 1); + switch (reg_type) { + case kMTLineWise: + buf[0] = 'V'; + buf[1] = NUL; + break; + case kMTCharWise: + buf[0] = 'v'; + buf[1] = NUL; + break; + case kMTBlockWise: + snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); + break; + case kMTUnknown: + buf[0] = NUL; + break; + } +} + + /// When `flags` has `kGRegList` return a list with text `s`. /// Otherwise just return `s`. /// @@ -4739,10 +4819,11 @@ void *get_reg_contents(int regname, int flags) len += STRLEN(reg->y_array[i]); /* * Insert a newline between lines and after last line if - * y_type is MLINE. + * y_type is kMTLineWise. */ - if (reg->y_type == MLINE || i < reg->y_size - 1) - ++len; + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { + len++; + } } retval = xmalloc(len + 1); @@ -4757,10 +4838,11 @@ void *get_reg_contents(int regname, int flags) /* * Insert a NL between lines and after the last line if y_type is - * MLINE. + * kMTLineWise. */ - if (reg->y_type == MLINE || i < reg->y_size - 1) + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { retval[len++] = '\n'; + } } retval[len] = NUL; @@ -4801,11 +4883,12 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous void write_reg_contents(int name, const char_u *str, ssize_t len, int must_append) { - write_reg_contents_ex(name, str, len, must_append, MAUTO, 0L); + write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } void write_reg_contents_lst(int name, char_u **strings, int maxlen, - bool must_append, int yank_type, long block_len) + bool must_append, MotionType yank_type, + long block_len) { if (name == '/' || name == '=') { char_u *s = strings[0]; @@ -4851,13 +4934,13 @@ void write_reg_contents_lst(int name, char_u **strings, int maxlen, /// contents of the register. Note that regardless of /// `must_append`, this function will append when `name` /// is an uppercase letter. -/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO +/// @param yank_type The motion type (kMTUnknown to auto detect) /// @param block_len width of visual block void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_append, - int yank_type, + MotionType yank_type, long block_len) { if (len < 0) { @@ -4931,24 +5014,24 @@ void write_reg_contents_ex(int name, /// When the register is not empty, the string is appended. /// /// @param y_ptr pointer to yank register -/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO +/// @param yank_type The motion type (kMTUnknown to auto detect) /// @param str string or list of strings to put in register /// @param len length of the string (Ignored when str_list=true.) /// @param blocklen width of visual block, or -1 for "I don't know." /// @param str_list True if str is `char_u **`. -static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, - size_t len, colnr_T blocklen, bool str_list) +static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, + const char_u *str, size_t len, colnr_T blocklen, + bool str_list) FUNC_ATTR_NONNULL_ALL { if (y_ptr->y_array == NULL) { // NULL means empty register y_ptr->y_size = 0; } - int type = yank_type; // MCHAR, MLINE or MBLOCK - if (yank_type == MAUTO) { - type = ((str_list || - (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR))) - ? MLINE : MCHAR); + if (yank_type == kMTUnknown) { + yank_type = ((str_list + || (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR))) + ? kMTLineWise : kMTCharWise); } size_t newlines = 0; @@ -4962,11 +5045,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, } } else { newlines = memcnt(str, '\n', len); - if (type == MCHAR || len == 0 || str[len - 1] != '\n') { + if (yank_type == kMTCharWise || len == 0 || str[len - 1] != '\n') { extraline = 1; ++newlines; // count extra newline at the end } - if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) { + if (y_ptr->y_size > 0 && y_ptr->y_type == kMTCharWise) { append = true; --newlines; // uncount newline when appending first line } @@ -5019,11 +5102,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, memchrsub(pp[lnum], NUL, '\n', s_len); } } - y_ptr->y_type = type; + y_ptr->y_type = yank_type; y_ptr->y_size = lnum; set_yreg_additional_data(y_ptr, NULL); y_ptr->timestamp = os_time(); - if (type == MBLOCK) { + if (yank_type == kMTBlockWise) { y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen); } else { y_ptr->y_width = 0; @@ -5082,18 +5165,18 @@ static long line_count_info(char_u *line, long *wc, long *cc, long limit, int eo return i; } -/* - * Give some info about the position of the cursor (for "g CTRL-G"). - * In Visual mode, give some info about the selected region. (In this case, - * the *_count_cursor variables store running totals for the selection.) - */ -void cursor_pos_info(void) +/// Give some info about the position of the cursor (for "g CTRL-G"). +/// In Visual mode, give some info about the selected region. (In this case, +/// the *_count_cursor variables store running totals for the selection.) +/// When "dict" is not NULL store the info there instead of showing it. +void cursor_pos_info(dict_T *dict) { char_u *p; char_u buf1[50]; char_u buf2[40]; linenr_T lnum; long byte_count = 0; + long bom_count = 0; long byte_count_cursor = 0; long char_count = 0; long char_count_cursor = 0; @@ -5108,11 +5191,12 @@ void cursor_pos_info(void) const int l_VIsual_active = VIsual_active; const int l_VIsual_mode = VIsual_mode; - /* - * Compute the length of the file in characters. - */ + // Compute the length of the file in characters. if (curbuf->b_ml.ml_flags & ML_EMPTY) { - MSG(_(no_lines_msg)); + if (dict == NULL) { + MSG(_(no_lines_msg)); + return; + } } else { if (get_fileformat(curbuf) == EOL_DOS) eol_size = 2; @@ -5136,7 +5220,7 @@ void cursor_pos_info(void) /* Make 'sbr' empty for a moment to get the correct size. */ p_sbr = empty_option; oparg.is_VIsual = true; - oparg.motion_type = MBLOCK; + oparg.motion_type = kMTBlockWise; oparg.op_type = OP_NOP; getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol); @@ -5217,78 +5301,105 @@ void cursor_pos_info(void) &char_count, (long)MAXCOL, eol_size); } - /* Correction for when last line doesn't have an EOL. */ - if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) + // Correction for when last line doesn't have an EOL. + if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) { byte_count -= eol_size; + } + + if (dict == NULL) { + if (l_VIsual_active) { + if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { + getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col); + vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "), + (int64_t)(oparg.end_vcol - oparg.start_vcol + 1)); + } else { + buf1[0] = NUL; + } + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%" PRId64 " of %" PRId64 " Lines;" + " %" PRId64 " of %" PRId64 " Words;" + " %" PRId64 " of %" PRId64 " Bytes"), + buf1, (int64_t)line_count_selected, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } else { + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%" PRId64 " of %" PRId64 " Lines;" + " %" PRId64 " of %" PRId64 " Words;" + " %" PRId64 " of %" PRId64 " Chars;" + " %" PRId64 " of %" PRId64 " Bytes"), + buf1, (int64_t)line_count_selected, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)char_count_cursor, (int64_t)char_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } + } else { + p = get_cursor_line_ptr(); + validate_virtcol(); + col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, + (int)curwin->w_virtcol + 1); + col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p)); + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" + " Word %" PRId64 " of %" PRId64 ";" + " Byte %" PRId64 " of %" PRId64 ""), + (char *)buf1, (char *)buf2, + (int64_t)curwin->w_cursor.lnum, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } else { + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" + " Word %" PRId64 " of %" PRId64 ";" + " Char %" PRId64 " of %" PRId64 ";" + " Byte %" PRId64 " of %" PRId64 ""), + (char *)buf1, (char *)buf2, + (int64_t)curwin->w_cursor.lnum, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)char_count_cursor, (int64_t)char_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } + } + } + + // Don't shorten this message, the user asked for it. + bom_count = bomb_size(); + if (bom_count > 0) { + vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), + _("(+%" PRId64 " for BOM)"), (int64_t)byte_count); + } + if (dict == NULL) { + p = p_shm; + p_shm = (char_u *)""; + msg(IObuff); + p_shm = p; + } + } + + if (dict != NULL) { + dict_add_nr_str(dict, "words", word_count, NULL); + dict_add_nr_str(dict, "chars", char_count, NULL); + dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); if (l_VIsual_active) { - if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { - getvcols(curwin, &min_pos, &max_pos, &min_pos.col, - &max_pos.col); - vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "), - (int64_t)(oparg.end_vcol - oparg.start_vcol + 1)); - } else - buf1[0] = NUL; - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64 - " of %" PRId64 " Words; %" PRId64 " of %" PRId64 " Bytes"), - buf1, (int64_t)line_count_selected, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64 - " of %" PRId64 " Words; %" PRId64 " of %" PRId64 - " Chars; %" PRId64 " of %" PRId64 " Bytes"), - buf1, (int64_t)line_count_selected, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)char_count_cursor, (int64_t)char_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); + dict_add_nr_str(dict, "visual_bytes", byte_count_cursor, NULL); + dict_add_nr_str(dict, "visual_chars", char_count_cursor, NULL); + dict_add_nr_str(dict, "visual_words", word_count_cursor, NULL); } else { - p = get_cursor_line_ptr(); - validate_virtcol(); - col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, - (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p)); - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64 - " of %" PRId64 "; Byte %" PRId64 " of %" PRId64 ""), - (char *)buf1, (char *)buf2, - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _( - "Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64 - " of %" PRId64 "; Char %" PRId64 " of %" PRId64 - "; Byte %" PRId64 " of %" PRId64 ""), - (char *)buf1, (char *)buf2, - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)char_count_cursor, (int64_t)char_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - } - - byte_count = bomb_size(); - if (byte_count > 0) - sprintf((char *)IObuff + STRLEN(IObuff), _("(+%" PRId64 " for BOM)"), - (int64_t)byte_count); - /* Don't shorten this message, the user asked for it. */ - p = p_shm; - p_shm = (char_u *)""; - msg(IObuff); - p_shm = p; + dict_add_nr_str(dict, "cursor_bytes", byte_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_chars", char_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_words", word_count_cursor, NULL); + } } } @@ -5391,16 +5502,16 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } switch (regtype[0]) { case 0: - reg->y_type = MAUTO; + reg->y_type = kMTUnknown; break; case 'v': case 'c': - reg->y_type = MCHAR; + reg->y_type = kMTCharWise; break; case 'V': case 'l': - reg->y_type = MLINE; + reg->y_type = kMTLineWise; break; case 'b': case Ctrl_V: - reg->y_type = MBLOCK; + reg->y_type = kMTBlockWise; break; default: goto err; @@ -5408,7 +5519,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } else { lines = res; // provider did not specify regtype, calculate it below - reg->y_type = MAUTO; + reg->y_type = kMTUnknown; } reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *)); @@ -5429,20 +5540,20 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { // a known-to-be charwise yank might have a final linebreak // but otherwise there is no line after the final newline - if (reg->y_type != MCHAR) { + if (reg->y_type != kMTCharWise) { xfree(reg->y_array[reg->y_size-1]); reg->y_size--; - if (reg->y_type == MAUTO) { - reg->y_type = MLINE; + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTLineWise; } } } else { - if (reg->y_type == MAUTO) { - reg->y_type = MCHAR; + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTCharWise; } } - if (reg->y_type == MBLOCK) { + if (reg->y_type == kMTBlockWise) { int maxlen = 0; for (int i = 0; i < reg->y_size; i++) { int rowlen = STRLEN(reg->y_array[i]); @@ -5491,17 +5602,19 @@ static void set_clipboard(int name, yankreg_T *reg) char_u regtype; switch (reg->y_type) { - case MLINE: + case kMTLineWise: regtype = 'V'; list_append_string(lines, (char_u*)"", 0); break; - case MCHAR: + case kMTCharWise: regtype = 'v'; break; - case MBLOCK: + case kMTBlockWise: regtype = 'b'; list_append_string(lines, (char_u*)"", 0); break; + case kMTUnknown: + assert(false); } list_append_string(args, ®type, 1); @@ -5542,7 +5655,7 @@ static inline bool reg_empty(const yankreg_T *const reg) return (reg->y_array == NULL || reg->y_size == 0 || (reg->y_size == 1 - && reg->y_type == MCHAR + && reg->y_type == kMTCharWise && *(reg->y_array[0]) == NUL)); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index f33e87572f..8c8a586957 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -78,10 +78,10 @@ enum GRegFlags { /// Definition of one register typedef struct yankreg { - char_u **y_array; ///< Pointer to an array of line pointers. - linenr_T y_size; ///< Number of lines in y_array. - char_u y_type; ///< Register type: MLINE, MCHAR or MBLOCK. - colnr_T y_width; ///< Register width (only valid for y_type == MBLOCK). + char_u **y_array; ///< Pointer to an array of line pointers. + linenr_T y_size; ///< Number of lines in y_array. + MotionType y_type; ///< Register type + colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise). Timestamp timestamp; ///< Time when register was last modified. dict_T *additional_data; ///< Additional data from ShaDa file. } yankreg_T; diff --git a/src/nvim/option.c b/src/nvim/option.c index af7b272467..2f22c245dd 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -32,6 +32,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/edit.h" #include "nvim/option.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -1177,28 +1178,27 @@ do_set ( errmsg = e_invarg; goto skip; } - arg[len] = NUL; /* put NUL after name */ - if (arg[1] == 't' && arg[2] == '_') /* could be term code */ - opt_idx = findoption(arg + 1); - arg[len++] = '>'; /* restore '>' */ - if (opt_idx == -1) + if (arg[1] == 't' && arg[2] == '_') { // could be term code + opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + } + len++; + if (opt_idx == -1) { key = find_key_option(arg + 1); + } } else { len = 0; - /* - * The two characters after "t_" may not be alphanumeric. - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // The two characters after "t_" may not be alphanumeric. + if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { len = 4; - else - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') - ++len; - nextchar = arg[len]; - arg[len] = NUL; /* put NUL after name */ - opt_idx = findoption(arg); - arg[len] = nextchar; /* restore nextchar */ - if (opt_idx == -1) + } else { + while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { + len++; + } + } + opt_idx = findoption_len(arg, (size_t) len); + if (opt_idx == -1) { key = find_key_option(arg); + } } /* remember character after option name */ @@ -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, @@ -2958,13 +2959,17 @@ did_set_string_option ( } /* 'completeopt' */ else if (varp == &p_cot) { - if (check_opt_strings(p_cot, p_cot_values, TRUE) != OK) + if (check_opt_strings(p_cot, p_cot_values, true) != OK) { errmsg = e_invarg; + } else { + completeopt_was_set(); + } } /* 'pastetoggle': translate key codes like in a mapping */ else if (varp == &p_pt) { if (*p_pt) { - (void)replace_termcodes(p_pt, &p, TRUE, TRUE, FALSE); + (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false, + CPO_TO_CPO_FLAGS); if (p != NULL) { if (new_value_alloced) free_string_option(p_pt); @@ -2984,6 +2989,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 +3823,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 +3870,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 +4260,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); @@ -4284,14 +4307,16 @@ static void check_redraw(uint32_t flags) redraw_all_later(NOT_VALID); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for named option +/// +/// @param[in] arg Option to find index for. +/// @param[in] len Length of the option. +/// +/// @return Index of the option or -1 if option was not found. +int findoption_len(const char_u *const arg, const size_t len) { - char *s, *p; - static short quick_tab[27] = {0, 0}; /* quick access table */ + char *s, *p; + static int quick_tab[27] = { 0, 0 }; // quick access table int is_term_opt; /* @@ -4315,25 +4340,31 @@ static int findoption(char_u *arg) /* * Check for name starting with an illegal character. */ - if (arg[0] < 'a' || arg[0] > 'z') + if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; + } int opt_idx; - is_term_opt = (arg[0] == 't' && arg[1] == '_'); - if (is_term_opt) + is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + if (is_term_opt) { opt_idx = quick_tab[26]; - else + } else { opt_idx = quick_tab[CharOrdLow(arg[0])]; + } + // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRCMP(arg, s) == 0) /* match full name */ + if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } } if (s == NULL && !is_term_opt) { opt_idx = quick_tab[CharOrdLow(arg[0])]; + // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRCMP(arg, s) == 0) /* match short name */ + if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } s = NULL; } } @@ -4401,6 +4432,15 @@ bool set_tty_option(char *name, char *value) } /* + * Find index for option 'arg'. + * Return -1 if not found. + */ +static int findoption(char_u *arg) +{ + return findoption_len(arg, STRLEN(arg)); +} + +/* * Get the value for an option. * * Returns: @@ -4656,27 +4696,32 @@ char_u *get_highlight_default(void) /* * Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. */ -static int find_key_option(char_u *arg) +int find_key_option_len(const char_u *arg, size_t len) { int key; int modifiers; - /* - * Don't use get_special_key_code() for t_xx, we don't want it to call - * add_termcap_entry(). - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // Don't use get_special_key_code() for t_xx, we don't want it to call + // add_termcap_entry(). + if (len >= 4 && arg[0] == 't' && arg[1] == '_') { key = TERMCAP2KEY(arg[2], arg[3]); - else { - --arg; /* put arg at the '<' */ + } else { + arg--; // put arg at the '<' modifiers = 0; - key = find_special_key(&arg, &modifiers, TRUE, TRUE); - if (modifiers) /* can't handle modifiers here */ + key = find_special_key(&arg, len + 1, &modifiers, true, true); + if (modifiers) { // can't handle modifiers here key = 0; + } } return key; } +static int find_key_option(const char_u *arg) +{ + return find_key_option_len(arg, STRLEN(arg)); +} + + /* * if 'all' == 0: show changed options * if 'all' == 1: show all normal options @@ -4737,9 +4782,10 @@ showoptions ( option_value2string(p, opt_flags); len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1; } - if ((len <= INC - GAP && run == 1) || - (len > INC - GAP && run == 2)) + if ((len <= INC - GAP && run == 1) + || (len > INC - GAP && run == 2)) { items[item_count++] = p; + } } } @@ -4928,18 +4974,15 @@ int makeset(FILE *fd, int opt_flags, int local_only) } else { /* P_STRING */ int do_endif = FALSE; - /* Don't set 'syntax' and 'filetype' again if the value is - * already right, avoids reloading the syntax file. */ - if ( - p->indir == PV_SYN - || - p->indir == PV_FT - ) { + // Don't set 'syntax' and 'filetype' again if the value is + // already right, avoids reloading the syntax file. + if (p->indir == PV_SYN || p->indir == PV_FT) { if (fprintf(fd, "if &%s != '%s'", p->fullname, - *(char_u **)(varp)) < 0 - || put_eol(fd) < 0) + *(char_u **)(varp)) < 0 + || put_eol(fd) < 0) { return FAIL; - do_endif = TRUE; + } + do_endif = true; } if (put_setstring(fd, cmd, p->fullname, (char_u **)varp, (p->flags & P_EXPAND) != 0) == FAIL) @@ -5112,6 +5155,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 +5212,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 +5250,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 +5631,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); @@ -6167,16 +6219,14 @@ int has_format_option(int x) return vim_strchr(curbuf->b_p_fo, x) != NULL; } -/* - * Return TRUE if "x" is present in 'shortmess' option, or - * 'shortmess' contains 'a' and "x" is present in SHM_A. - */ -int shortmess(int x) +/// @returns true if "x" is present in 'shortmess' option, or +/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS. +bool shortmess(int x) { - return p_shm != NULL && - ( vim_strchr(p_shm, x) != NULL - || (vim_strchr(p_shm, 'a') != NULL - && vim_strchr((char_u *)SHM_A, x) != NULL)); + return (p_shm != NULL + && (vim_strchr(p_shm, x) != NULL + || (vim_strchr(p_shm, 'a') != NULL + && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL))); } /* diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 10706a0753..904e97f8ca 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 @@ -151,26 +152,42 @@ #define COCU_ALL "nvic" /* flags for 'concealcursor' */ -/* characters for p_shm option: */ -#define SHM_RO 'r' /* readonly */ -#define SHM_MOD 'm' /* modified */ -#define SHM_FILE 'f' /* (file 1 of 2) */ -#define SHM_LAST 'i' /* last line incomplete */ -#define SHM_TEXT 'x' /* tx instead of textmode */ -#define SHM_LINES 'l' /* "L" instead of "lines" */ -#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */ -#define SHM_WRI 'w' /* "[w]" instead of "written" */ -#define SHM_A "rmfixlnw" /* represented by 'a' flag */ -#define SHM_WRITE 'W' /* don't use "written" at all */ -#define SHM_TRUNC 't' /* trunctate file messages */ -#define SHM_TRUNCALL 'T' /* trunctate all messages */ -#define SHM_OVER 'o' /* overwrite file messages */ -#define SHM_OVERALL 'O' /* overwrite more messages */ -#define SHM_SEARCH 's' /* no search hit bottom messages */ -#define SHM_ATTENTION 'A' /* no ATTENTION messages */ -#define SHM_INTRO 'I' /* intro messages */ -#define SHM_COMPLETIONMENU 'c' // completion menu messages -#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */ +/// characters for p_shm option: +enum { + SHM_RO = 'r', ///< Readonly. + SHM_MOD = 'm', ///< Modified. + SHM_FILE = 'f', ///< (file 1 of 2) + SHM_LAST = 'i', ///< Last line incomplete. + SHM_TEXT = 'x', ///< Tx instead of textmode. + SHM_LINES = 'l', ///< "L" instead of "lines". + SHM_NEW = 'n', ///< "[New]" instead of "[New file]". + SHM_WRI = 'w', ///< "[w]" instead of "written". + SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS. + SHM_WRITE = 'W', ///< Don't use "written" at all. + SHM_TRUNC = 't', ///< Trunctate file messages. + SHM_TRUNCALL = 'T', ///< Trunctate all messages. + SHM_OVER = 'o', ///< Overwrite file messages. + SHM_OVERALL = 'O', ///< Overwrite more messages. + SHM_SEARCH = 's', ///< No search hit bottom messages. + SHM_ATTENTION = 'A', ///< No ATTENTION messages. + 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[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + 0, \ +}) +/// All possible flags for 'shm'. +#define SHM_ALL ((char_u[]) { \ + 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_FILEINFO, \ + 0, \ +}) /* characters for p_go: */ #define GO_ASEL 'a' /* autoselect */ @@ -580,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' @@ -594,6 +619,7 @@ EXTERN long p_titlelen; ///< 'titlelen' EXTERN char_u *p_titleold; ///< 'titleold' EXTERN char_u *p_titlestring; ///< 'titlestring' EXTERN char_u *p_tsr; ///< 'thesaurus' +EXTERN bool p_tgc; ///< 'termguicolors' EXTERN int p_ttimeout; ///< 'ttimeout' EXTERN long p_ttm; ///< 'ttimeoutlen' EXTERN char_u *p_udir; ///< 'undodir' @@ -720,6 +746,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..218e34f595 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, @@ -2376,6 +2383,14 @@ return { defaults={if_true={vi=""}} }, { + full_name='termguicolors', abbreviation='tgc', + type='bool', scope={'global'}, + vi_def=false, + redraw={'everything'}, + varname='p_tgc', + defaults={if_true={vi=false}} + }, + { full_name='terse', type='bool', scope={'global'}, vi_def=true, diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 41ce8ddbc2..edc430410c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -147,7 +147,7 @@ static char_u *homedir = NULL; void init_homedir(void) { - /* In case we are called a second time (when 'encoding' changes). */ + // In case we are called a second time (when 'encoding' changes). xfree(homedir); homedir = NULL; @@ -176,16 +176,16 @@ void init_homedir(void) if (var != NULL) { #ifdef UNIX - /* - * Change to the directory and get the actual path. This resolves - * links. Don't do it when we can't return. - */ + // Change to the directory and get the actual path. This resolves + // links. Don't do it when we can't return. if (os_dirname(NameBuff, MAXPATHL) == OK && os_chdir((char *)NameBuff) == 0) { - if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) + if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) { var = IObuff; - if (os_chdir((char *)NameBuff) != 0) + } + if (os_chdir((char *)NameBuff) != 0) { EMSG(_(e_prev_dir)); + } } #endif homedir = vim_strsave(var); @@ -239,29 +239,29 @@ void expand_env(char_u *src, char_u *dst, int dstlen) /// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded. /// Skips over "\ ", "\~" and "\$" (not for Win32 though). /// If anything fails no expansion is done and dst equals src. -/// startstr recognize the start of a new name, for '~' expansion. +/// prefix recognize the start of a new name, for '~' expansion. /// @param srcp Input string e.g. "$HOME/vim.hlp" /// @param dst Where to put the result /// @param dstlen Maximum length of the result /// @param esc Should we escape spaces in expanded variables? /// @param one Should we expand more than one '~'? -/// @param startstr Common prefix for paths, can be NULL -void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, - char_u *startstr) +/// @param prefix Common prefix for paths, can be NULL +void expand_env_esc(char_u *restrict srcp, + char_u *restrict dst, + int dstlen, + bool esc, + bool one, + char_u *prefix) { - char_u *src; char_u *tail; - int c; char_u *var; bool copy_char; bool mustfree; // var was allocated, need to free it later bool at_start = true; // at start of a name - int startstr_len = 0; - if (startstr != NULL) - startstr_len = (int)STRLEN(startstr); + int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix); - src = skipwhite(srcp); + char_u *src = skipwhite(srcp); dstlen--; // leave one char space for "\," while (*src && dstlen > 0) { // Skip over `=expr`. @@ -281,6 +281,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, dstlen -= (int)len; continue; } + copy_char = true; if ((*src == '$') || (*src == '~' && at_start)) { mustfree = false; @@ -290,14 +291,15 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, if (*src != '~') { // environment var tail = src + 1; var = dst; - c = dstlen - 1; + int c = dstlen - 1; #ifdef UNIX // Unix has ${var-name} type environment vars if (*tail == '{' && !vim_isIDc('{')) { - tail++; /* ignore '{' */ - while (c-- > 0 && *tail && *tail != '}') + tail++; // ignore '{' + while (c-- > 0 && *tail != NUL && *tail != '}') { *var++ = *tail++; + } } else // NOLINT #endif { @@ -321,7 +323,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, #if defined(UNIX) } #endif - } else if ( src[1] == NUL /* home directory */ + } else if (src[1] == NUL // home directory || vim_ispathsep(src[1]) || vim_strchr((char_u *)" ,\t\n", src[1]) != NULL) { var = homedir; @@ -331,12 +333,13 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, // Copy ~user to dst[], so we can put a NUL after it. tail = src; var = dst; - c = dstlen - 1; - while ( c-- > 0 - && *tail - && vim_isfilec(*tail) - && !vim_ispathsep(*tail)) + int c = dstlen - 1; + while (c-- > 0 + && *tail + && vim_isfilec(*tail) + && !vim_ispathsep(*tail)) { *var++ = *tail++; + } *var = NUL; // Use os_get_user_directory() to get the user directory. // If this function fails, the shell is used to @@ -344,8 +347,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, // does not support ~user (old versions of /bin/sh). var = (char_u *)os_get_user_directory((char *)dst + 1); mustfree = true; - if (var == NULL) - { + if (var == NULL) { expand_T xpc; ExpandInit(&xpc); @@ -381,8 +383,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) { char_u *p = vim_strsave_escaped(var, (char_u *)" \t"); - if (mustfree) + if (mustfree) { xfree(var); + } var = p; mustfree = true; } @@ -391,7 +394,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen)) { STRCPY(dst, var); dstlen -= (int)STRLEN(var); - c = (int)STRLEN(var); + int c = (int)STRLEN(var); // if var[] ends in a path separator and tail[] starts // with it, skip a character if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c) @@ -404,8 +407,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, src = tail; copy_char = false; } - if (mustfree) + if (mustfree) { xfree(var); + } } if (copy_char) { // copy at least one char @@ -422,9 +426,10 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, *dst++ = *src++; --dstlen; - if (startstr != NULL && src - startstr_len >= srcp - && STRNCMP(src - startstr_len, startstr, startstr_len) == 0) + if (prefix != NULL && src - prefix_len >= srcp + && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) { at_start = true; + } } } *dst = NUL; @@ -451,17 +456,37 @@ static char *vim_version_dir(const char *vimdir) return NULL; } -/// If the string between "p" and "pend" ends in "name/", return "pend" minus -/// the length of "name/". Otherwise return "pend". -static char *remove_tail(char *p, char *pend, char *name) +/// If `dirname + "/"` precedes `pend` in the path, return the pointer to +/// `dirname + "/" + pend`. Otherwise return `pend`. +/// +/// Examples (path = /usr/local/share/nvim/runtime/doc/help.txt): +/// +/// pend = help.txt +/// dirname = doc +/// -> doc/help.txt +/// +/// pend = doc/help.txt +/// dirname = runtime +/// -> runtime/doc/help.txt +/// +/// pend = runtime/doc/help.txt +/// dirname = vim74 +/// -> runtime/doc/help.txt +/// +/// @param path Path to a file +/// @param pend A suffix of the path +/// @param dirname The immediate path fragment before the pend +/// @return The new pend including dirname or just pend +static char *remove_tail(char *path, char *pend, char *dirname) { - size_t len = STRLEN(name) + 1; - char *newend = pend - len; + size_t len = STRLEN(dirname); + char *new_tail = pend - len - 1; - if (newend >= p - && fnamencmp((char_u *)newend, (char_u *)name, len - 1) == 0 - && (newend == p || after_pathsep(p, newend))) - return newend; + if (new_tail >= path + && fnamencmp((char_u *)new_tail, (char_u *)dirname, len) == 0 + && (new_tail == path || after_pathsep(path, new_tail))) { + return new_tail; + } return pend; } @@ -745,9 +770,10 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) /// @param src Input file name char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET { - size_t len = 3; /* space for "~/" and trailing NUL */ - if (src != NULL) /* just in case */ + size_t len = 3; // space for "~/" and trailing NUL + if (src != NULL) { // just in case len += STRLEN(src); + } char_u *dst = xmalloc(len); home_replace(buf, src, dst, (int)len, true); return dst; @@ -759,15 +785,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET void vim_setenv(const char *name, const char *val) { os_setenv(name, val, 1); - /* - * When setting $VIMRUNTIME adjust the directory to find message - * translations to $VIMRUNTIME/lang. - */ +#ifndef LOCALE_INSTALL_DIR + // When setting $VIMRUNTIME adjust the directory to find message + // translations to $VIMRUNTIME/lang. if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) { char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang"); - bindtextdomain(VIMPACKAGE, buf); + bindtextdomain(PROJECT_NAME, buf); xfree(buf); } +#endif } @@ -783,8 +809,7 @@ char_u *get_env_name(expand_T *xp, int idx) STRLCPY(name, envname, ENVNAMELEN); xfree(envname); return name; - } else { - return NULL; } + return NULL; } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2e671653ed..49a74cf0d1 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -2,6 +2,7 @@ #include <stdbool.h> #include <assert.h> +#include <fcntl.h> #include "nvim/os/os.h" #include "nvim/os/os_defs.h" @@ -59,6 +60,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. @@ -77,10 +95,76 @@ bool os_isdir(const char_u *name) return true; } +/// Check what `name` is: +/// @return NODE_NORMAL: file or directory (or doesn't exist) +/// NODE_WRITABLE: writable device, socket, fifo, etc. +/// NODE_OTHER: non-writable things +int os_nodetype(const char *name) +{ +#ifdef WIN32 + // Edge case from Vim os_win32.c: + // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read + // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. + if (STRNCMP(name, "\\\\.\\", 4) == 0) { + return NODE_WRITABLE; + } +#endif + + uv_stat_t statbuf; + if (os_stat(name, &statbuf) == 0) { + return NODE_NORMAL; + } + +#ifndef WIN32 + // libuv does not handle BLK and DIR in uv_handle_type. + // Related: https://github.com/joyent/libuv/pull/1421 + if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { + return NODE_NORMAL; + } + if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable + return NODE_OTHER; + } +#endif + + // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015): + // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { + // wn = enc_to_utf16(name, NULL); + // hFile = CreatFile(wn, ...) + // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we + // give it. uv_fs_open calls fs__capture_path which does a similar dance and + // saves us the hassle. + + int nodetype = NODE_WRITABLE; + int fd = os_open(name, O_RDONLY, 0); + switch(uv_guess_handle(fd)) { + case UV_TTY: // FILE_TYPE_CHAR + nodetype = NODE_WRITABLE; + break; + case UV_FILE: // FILE_TYPE_DISK + nodetype = NODE_NORMAL; + break; + case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c + case UV_UDP: // unix only + case UV_TCP: // unix only + case UV_UNKNOWN_HANDLE: + default: +#ifdef WIN32 + nodetype = NODE_NORMAL; +#else + nodetype = NODE_WRITABLE; // Everything else is writable? +#endif + break; + } + + close(fd); + return nodetype; +} + /// Checks if the given path represents an executable file. /// -/// @param[in] name The name of the executable. +/// @param[in] name Name of the executable. /// @param[out] abspath Path of the executable, if found and not `NULL`. +/// @param[in] use_path If 'false', only check if "name" is executable /// /// @return `true` if `name` is executable and /// - can be found in $PATH, @@ -88,14 +172,18 @@ bool os_isdir(const char_u *name) /// - is absolute. /// /// @return `false` otherwise. -bool os_can_exe(const char_u *name, char_u **abspath) +bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - // If it's an absolute or relative path don't need to use $PATH. - if (path_is_absolute_path(name) || - (name[0] == '.' && (name[1] == '/' || - (name[1] == '.' && name[2] == '/')))) { - if (is_executable(name)) { + // when use_path is false or if it's an absolute or relative path don't + // need to use $PATH. + if (!use_path || path_is_absolute_path(name) + || (name[0] == '.' + && (name[1] == '/' + || (name[1] == '.' && name[2] == '/')))) { + // There must be a path separator, files in the current directory + // can't be executed + if (gettail_dir(name) != name && is_executable(name)) { if (abspath != NULL) { *abspath = save_absolute_path(name); } @@ -166,7 +254,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/fs_defs.h b/src/nvim/os/fs_defs.h index 52b2841514..0bd9c37750 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -26,4 +26,10 @@ typedef struct { /// negative libuv error codes are returned by a number of os functions. #define os_strerror uv_strerror +// Values returned by os_nodetype() +#define NODE_NORMAL 0 // file or directory, check with os_isdir() +#define NODE_WRITABLE 1 // something we can write to (character + // device, fifo, socket, ..) +#define NODE_OTHER 2 // non-writable thing (e.g., block device) + #endif // NVIM_OS_FS_DEFS_H diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index f317fd6b5a..7687b14f02 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -175,8 +175,9 @@ size_t input_enqueue(String keys) char *ptr = keys.data, *end = ptr + keys.size; while (rbuffer_space(input_buffer) >= 6 && ptr < end) { - uint8_t buf[6] = {0}; - unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true); + uint8_t buf[6] = { 0 }; + unsigned int new_size = trans_special((const uint8_t **)&ptr, keys.size, + buf, true); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); @@ -402,9 +403,9 @@ static int push_event_key(uint8_t *buf, int maxlen) // Check if there's pending input static bool input_ready(void) { - return typebuf_was_filled || // API call filled typeahead - rbuffer_size(input_buffer) || // Input buffer filled - pending_events(); // Events must be processed + return (typebuf_was_filled // API call filled typeahead + || rbuffer_size(input_buffer) // Input buffer filled + || pending_events()); // Events must be processed } // Exit because of an input read error. diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c index 5e483c0c3d..871ece7a0e 100644 --- a/src/nvim/os/mem.c +++ b/src/nvim/os/mem.c @@ -8,5 +8,5 @@ uint64_t os_get_total_mem_kib(void) { // Convert bytes to KiB. - return uv_get_total_memory() >> 10; + return uv_get_total_memory() / 1024; } 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/win_defs.h b/src/nvim/os/win_defs.h index 242d355f77..6a29f86e79 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -1,6 +1,9 @@ #ifndef NVIM_OS_WIN_DEFS_H #define NVIM_OS_WIN_DEFS_H +// winsock2.h must be first to avoid incompatibilities +// with winsock.h (included by windows.h) +#include <winsock2.h> #include <windows.h> #include <sys/stat.h> #include <io.h> @@ -43,6 +46,8 @@ # endif #endif +#define BACKSLASH_IN_FILENAME + #ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index cb9a58cc77..2ed0c2c856 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" @@ -137,26 +136,6 @@ void mch_free_acl(vim_acl_T aclent) } #endif -/* - * Check what "name" is: - * NODE_NORMAL: file or directory (or doesn't exist) - * NODE_WRITABLE: writable device, socket, fifo, etc. - * NODE_OTHER: non-writable things - */ -int mch_nodetype(char_u *name) -{ - struct stat st; - - if (stat((char *)name, &st)) - return NODE_NORMAL; - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) - return NODE_NORMAL; - if (S_ISBLK(st.st_mode)) /* block device isn't writable */ - return NODE_OTHER; - /* Everything else is writable? */ - return NODE_WRITABLE; -} - void mch_exit(int r) { exiting = true; @@ -597,9 +576,11 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) continue; - /* Skip files that are not executable if we check for that. */ - if (!dir && (flags & EW_EXEC) && !os_can_exe((*file)[i], NULL)) + // Skip files that are not executable if we check for that. + if (!dir && (flags & EW_EXEC) + && !os_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD))) { continue; + } p = xmalloc(STRLEN((*file)[i]) + 1 + dir); STRCPY(p, (*file)[i]); diff --git a/src/nvim/os_unix.h b/src/nvim/os_unix.h index 5a3eb84ba4..d627b16ec0 100644 --- a/src/nvim/os_unix.h +++ b/src/nvim/os_unix.h @@ -4,12 +4,6 @@ #include "nvim/types.h" // for vim_acl_T #include "nvim/os/shell.h" -/* Values returned by mch_nodetype() */ -#define NODE_NORMAL 0 /* file or directory, check with os_isdir()*/ -#define NODE_WRITABLE 1 /* something we can write to (character - device, fifo, socket, ..) */ -#define NODE_OTHER 2 /* non-writable thing (e.g., block device) */ - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os_unix.h.generated.h" #endif diff --git a/src/nvim/path.c b/src/nvim/path.c index 5cd93ab811..41fd69f238 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) @@ -999,12 +981,12 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) * "/path/file", "/path/dir/", "/path//dir", "/file" * ^ ^ ^ ^ */ -static char_u *gettail_dir(char_u *fname) +char_u *gettail_dir(const char_u *fname) { - char_u *dir_end = fname; - char_u *next_dir_end = fname; + const char_u *dir_end = fname; + const char_u *next_dir_end = fname; bool look_for_sep = true; - char_u *p; + const char_u *p; for (p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { @@ -1019,7 +1001,7 @@ static char_u *gettail_dir(char_u *fname) } mb_ptr_adv(p); } - return dir_end; + return (char_u *)dir_end; } @@ -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; @@ -1303,6 +1284,29 @@ static int expand_backtick( return cnt; } +#ifdef BACKSLASH_IN_FILENAME +/// Replace all slashes by backslashes. +/// This used to be the other way around, but MS-DOS sometimes has problems +/// with slashes (e.g. in a command name). We can't have mixed slashes and +/// backslashes, because comparing file names will not work correctly. The +/// commands that use a file name should try to avoid the need to type a +/// backslash twice. +/// When 'shellslash' set do it the other way around. +/// When the path looks like a URL leave it unmodified. +void slash_adjust(char_u *p) +{ + if (path_with_url(p)) { + return; + } + while (*p) { + if (*p == psepcN) { + *p = psepc; + } + mb_ptr_adv(p); + } +} +#endif + // Add a file to a file list. Accepted flags: // EW_DIR add directories // EW_FILE add files @@ -1320,9 +1324,10 @@ void addfile( FileInfo file_info; // if the file/dir/link doesn't exist, may not add it - if (!(flags & EW_NOTFOUND) && - ((flags & EW_ALLLINKS) ? - !os_fileinfo_link((char *)f, &file_info) : !os_file_exists(f))) { + if (!(flags & EW_NOTFOUND) + && ((flags & EW_ALLLINKS) + ? !os_fileinfo_link((char *)f, &file_info) + : !os_file_exists(f))) { return; } @@ -1336,9 +1341,12 @@ void addfile( if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) return; - /* If the file isn't executable, may not add it. Do accept directories. */ - if (!isdir && (flags & EW_EXEC) && !os_can_exe(f, NULL)) + // If the file isn't executable, may not add it. Do accept directories. + // When invoked from expand_shellcmd() do not use $PATH. + if (!isdir && (flags & EW_EXEC) + && !os_can_exe(f, NULL, !(flags & EW_SHELLCMD))) { return; + } char_u *p = xmalloc(STRLEN(f) + 1 + isdir); @@ -1402,9 +1410,9 @@ void simplify_filename(char_u *filename) --p; /* strip preceding path separator */ STRMOVE(p, tail); } - } else if (p[0] == '.' && p[1] == '.' && - (vim_ispathsep(p[2]) || p[2] == NUL)) { - /* Skip to after ".." or "../" or "..///". */ + } else if (p[0] == '.' && p[1] == '.' + && (vim_ispathsep(p[2]) || p[2] == NUL)) { + // Skip to after ".." or "../" or "..///". tail = p + 2; while (vim_ispathsep(*tail)) mb_ptr_adv(tail); @@ -1517,13 +1525,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 +1548,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 +1560,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 +1569,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 +1578,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 +1637,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; @@ -1947,7 +1953,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set to /// NULL or points to "". -int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, +int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags) { int retval; @@ -1955,7 +1961,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, char_u *p; int non_suf_match; /* number without matching suffix */ - retval = gen_expand_wildcards(num_pat, pat, num_file, file, flags); + retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags); /* When keeping all matches, return here */ if ((flags & EW_KEEPALL) || retval == FAIL) @@ -1967,18 +1973,20 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, if (*p_wig) { char_u *ffname; - /* check all files in (*file)[] */ - for (i = 0; i < *num_file; ++i) { - ffname = (char_u *)FullName_save((char *)(*file)[i], FALSE); - if (ffname == NULL) /* out of memory */ + // check all filess in (*files)[] + for (i = 0; i < *num_files; i++) { + ffname = (char_u *)FullName_save((char *)(*files)[i], false); + if (ffname == NULL) { // out of memory break; - if (match_file_list(p_wig, (*file)[i], ffname)) { - /* remove this matching file from the list */ - xfree((*file)[i]); - for (j = i; j + 1 < *num_file; ++j) - (*file)[j] = (*file)[j + 1]; - --*num_file; - --i; + } + if (match_file_list(p_wig, (*files)[i], ffname)) { + // remove this matching files from the list + xfree((*files)[i]); + for (j = i; j + 1 < *num_files; j++) { + (*files)[j] = (*files)[j + 1]; + } + (*num_files)--; + i--; } xfree(ffname); } @@ -1987,26 +1995,28 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, /* * Move the names where 'suffixes' match to the end. */ - if (*num_file > 1) { + if (*num_files > 1) { non_suf_match = 0; - for (i = 0; i < *num_file; ++i) { - if (!match_suffix((*file)[i])) { - /* - * Move the name without matching suffix to the front - * of the list. - */ - p = (*file)[i]; - for (j = i; j > non_suf_match; --j) - (*file)[j] = (*file)[j - 1]; - (*file)[non_suf_match++] = p; + for (i = 0; i < *num_files; i++) { + if (!match_suffix((*files)[i])) { + // + // Move the name without matching suffix to the front + // of the list. + // + p = (*files)[i]; + for (j = i; j > non_suf_match; j--) { + (*files)[j] = (*files)[j - 1]; + } + (*files)[non_suf_match++] = p; } } } // Free empty array of matches - if (*num_file == 0) { - xfree(*file); - *file = NULL; + if (*num_files == 0) { + xfree(*files); + *files = NULL; + return FAIL; } return retval; @@ -2017,14 +2027,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); @@ -2036,9 +2044,9 @@ int match_suffix(char_u *fname) } } else { if (fnamelen >= setsuflen - && fnamencmp(suf_buf, fname + fnamelen - setsuflen, - (size_t)setsuflen) == 0) + && fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) { break; + } setsuflen = 0; } } @@ -2049,7 +2057,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 +2099,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 +2137,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..4e466d1b71 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -21,6 +21,10 @@ /* 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_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check + // if executable is in $PATH +#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/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index d1e08db65e..184c4894b9 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -72,8 +72,8 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) install_helper( FILES ${moFile} - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/lang/${name}/LC_MESSAGES - RENAME nvim.mo) + DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES + RENAME ${PROJECT_NAME}.mo) list(APPEND LANGUAGE_MO_FILES ${moFile}) endmacro() 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/pos.h b/src/nvim/pos.h index 7071df51e8..864f3fe866 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -2,7 +2,11 @@ #define NVIM_POS_H typedef long linenr_T; // line number type -typedef int colnr_T; // column number type + +/// Column number type +typedef int colnr_T; +/// Format used to print values which have colnr_T type +#define PRIdCOLNR "d" #define MAXLNUM 0x7fffffff // maximum (invalid) line number #define MAXCOL 0x7fffffff // maximum column number, 31 bits diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f3abf864fb..17cb8a86aa 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. @@ -2299,13 +2305,15 @@ static void qf_fill_buffer(qf_info_T *qi) if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { - if (qfp->qf_type == 1) /* :helpgrep */ - STRCPY(IObuff, path_tail(errbuf->b_fname)); - else - STRCPY(IObuff, errbuf->b_fname); + if (qfp->qf_type == 1) { // :helpgrep + STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff)); + } else { + STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff)); + } len = (int)STRLEN(IObuff); - } else + } else { len = 0; + } IObuff[len++] = '|'; if (qfp->qf_lnum > 0) { @@ -2431,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; @@ -2470,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 @@ -2546,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); @@ -2752,8 +2757,8 @@ void ex_cc(exarg_T *eap) // For cdo and ldo commands, jump to the nth valid error. // For cfdo and lfdo commands, jump to the nth valid file entry. - if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || - eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { size_t n; if (eap->addr_count > 0) { assert(eap->line1 >= 0); @@ -2796,9 +2801,9 @@ void ex_cnext(exarg_T *eap) } int errornr; - if (eap->addr_count > 0 && - (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo && - eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) { + if (eap->addr_count > 0 + && (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo + && eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) { errornr = (int)eap->line2; } else { errornr = 1; @@ -2897,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; @@ -2967,16 +2972,18 @@ void ex_vimgrep(exarg_T *eap) goto theend; } - if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd && - eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) - || qi->qf_curlist == qi->qf_listcount) - /* make place for a new list */ + if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd + && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) + || qi->qf_curlist == qi->qf_listcount) { + // make place for a new list qf_new_list(qi, *eap->cmdlinep); - else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) - /* Adding to existing list, find last entry. */ + } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { + // Adding to existing list, find last entry. for (prevp = qi->qf_lists[qi->qf_curlist].qf_start; - prevp->qf_next != prevp; prevp = prevp->qf_next) - ; + prevp->qf_next != prevp; + prevp = prevp->qf_next) { + } + } /* parse the list of arguments */ if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) @@ -3454,18 +3461,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); @@ -3492,21 +3493,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. */ @@ -3515,22 +3517,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/regexp.c b/src/nvim/regexp.c index 608aa38466..886a48e7c5 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3445,13 +3445,14 @@ static long bt_regexec_both(char_u *line, c = regline[col]; if (prog->regstart == NUL || prog->regstart == c - || (ireg_ic && (( - (enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) - || (c < 255 && prog->regstart < 255 && - vim_tolower(prog->regstart) == vim_tolower(c))))) + || (ireg_ic + && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) + || (c < 255 && prog->regstart < 255 + && vim_tolower(prog->regstart) == vim_tolower(c))))) { retval = regtry(prog, col); - else + } else { retval = 0; + } } else { int tm_count = 0; /* Messy cases: unanchored match. */ @@ -4121,15 +4122,15 @@ regmatch ( char_u *opnd; opnd = OPERAND(scan); - /* Inline the first byte, for speed. */ + // Inline the first byte, for speed. if (*opnd != *reginput - && (!ireg_ic || ( - !enc_utf8 && - vim_tolower(*opnd) != vim_tolower(*reginput)))) + && (!ireg_ic + || (!enc_utf8 + && vim_tolower(*opnd) != vim_tolower(*reginput)))) { status = RA_NOMATCH; - else if (*opnd == NUL) { - /* match empty string always works; happens when "~" is - * empty. */ + } else if (*opnd == NUL) { + // match empty string always works; happens when "~" is + // empty. } else { if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) { len = 1; /* matched a single byte above */ diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index e2849fb17a..7e53b2ccd1 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3464,13 +3464,12 @@ static char *pim_info(nfa_pim_T *pim) #endif -/* Used during execution: whether a match has been found. */ +// Used during execution: whether a match has been found. static int nfa_match; +static proftime_T *nfa_time_limit; +static int nfa_time_count; - -/* - * Copy postponed invisible match info from "from" to "to". - */ +// Copy postponed invisible match info from "from" to "to". static void copy_pim(nfa_pim_T *to, nfa_pim_T *from) { to->result = from->result; @@ -4566,7 +4565,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T if (log_fd != NULL) { fprintf(log_fd, "****************************\n"); fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n"); - fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE"); + fprintf(log_fd, "MATCH = %s\n", !result ? "FALSE" : "OK"); fprintf(log_fd, "****************************\n"); } else { EMSG(_( @@ -4778,8 +4777,8 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) int c2_len = PTR2LEN(s2); int c2 = PTR2CHAR(s2); - if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) || - c1_len != c2_len) { + if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) + || c1_len != c2_len) { match = false; break; } @@ -4813,22 +4812,21 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) #undef PTR2LEN } -/* - * Main matching routine. - * - * Run NFA to determine whether it matches reginput. - * - * When "nfa_endp" is not NULL it is a required end-of-match position. - * - * Return TRUE if there is a match, FALSE otherwise. - * When there is a match "submatch" contains the positions. - * Note: Caller must ensure that: start != NULL. - */ -static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *submatch, regsubs_T *m) +/// Main matching routine. +/// +/// Run NFA to determine whether it matches reginput. +/// +/// When "nfa_endp" is not NULL it is a required end-of-match position. +/// +/// Return TRUE if there is a match, FALSE otherwise. +/// When there is a match "submatch" contains the positions. +/// Note: Caller must ensure that: start != NULL. +static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, + regsubs_T *submatch, regsubs_T *m) { int result; int flag = 0; - int go_to_nextline = FALSE; + bool go_to_nextline = false; nfa_thread_T *t; nfa_list_T list[2]; int listidx; @@ -4845,18 +4843,22 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (debug == NULL) { EMSG2(_("(NFA) COULD NOT OPEN %s !"), NFA_REGEXP_DEBUG_LOG); - return FALSE; + return false; } #endif - /* Some patterns may take a long time to match, especially when using - * recursive_regmatch(). Allow interrupting them with CTRL-C. */ + // Some patterns may take a long time to match, especially when using + // recursive_regmatch(). Allow interrupting them with CTRL-C. fast_breakcheck(); - if (got_int) - return FALSE; + if (got_int) { + return false; + } + if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { + return false; + } - nfa_match = FALSE; + nfa_match = false; - /* Allocate memory for the lists of nodes. */ + // Allocate memory for the lists of nodes. size_t size = (nstate + 1) * sizeof(nfa_thread_T); list[0].t = xmalloc(size); list[0].len = nstate + 1; @@ -4924,7 +4926,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } if (curc == NUL) { clen = 0; - go_to_nextline = FALSE; + go_to_nextline = false; } /* swap lists */ @@ -5007,7 +5009,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) { break; } - nfa_match = TRUE; + nfa_match = true; copy_sub(&submatch->norm, &t->subs.norm); if (nfa_has_zsubexpr) copy_sub(&submatch->synt, &t->subs.synt); @@ -5072,10 +5074,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm fprintf(log_fd, "Match found:\n"); log_subsexpr(m); #endif - nfa_match = TRUE; - /* See comment above at "goto nextchar". */ - if (nextlist->n == 0) + nfa_match = true; + // See comment above at "goto nextchar". + if (nextlist->n == 0) { clen = 0; + } goto nextchar; case NFA_START_INVISIBLE: @@ -5092,8 +5095,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm failure_chance(t->state->out, 0), failure_chance(t->state->out1->out, 0)); #endif - /* Do it directly if there already is a PIM or when - * nfa_postprocess() detected it will work better. */ + // Do it directly if there already is a PIM or when + // nfa_postprocess() detected it will work better. if (t->pim.result != NFA_PIM_UNUSED || t->state->c == NFA_START_INVISIBLE_FIRST || t->state->c == NFA_START_INVISIBLE_NEG_FIRST @@ -5101,42 +5104,40 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST) { int in_use = m->norm.in_use; - /* Copy submatch info for the recursive call, opposite - * of what happens on success below. */ + // Copy submatch info for the recursive call, opposite + // of what happens on success below. copy_sub_off(&m->norm, &t->subs.norm); if (nfa_has_zsubexpr) copy_sub_off(&m->synt, &t->subs.synt); - /* - * First try matching the invisible match, then what - * follows. - */ - result = recursive_regmatch(t->state, NULL, prog, - submatch, m, &listids); + // First try matching the invisible match, then what + // follows. + result = recursive_regmatch(t->state, NULL, prog, submatch, m, + &listids); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; } - /* for \@! and \@<! it is a match when the result is - * FALSE */ + // for \@! and \@<! it is a match when the result is + // FALSE if (result != (t->state->c == NFA_START_INVISIBLE_NEG || t->state->c == NFA_START_INVISIBLE_NEG_FIRST || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &m->norm); if (nfa_has_zsubexpr) copy_sub_off(&t->subs.synt, &m->synt); - /* If the pattern has \ze and it matched in the - * sub pattern, use it. */ + // If the pattern has \ze and it matched in the + // sub pattern, use it. copy_ze_off(&t->subs.norm, &m->norm); - /* t->state->out1 is the corresponding - * END_INVISIBLE node; Add its out to the current - * list (zero-width match). */ + // t->state->out1 is the corresponding + // END_INVISIBLE node; Add its out to the current + // list (zero-width match). add_here = true; add_state = t->state->out1->out; } @@ -5144,12 +5145,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else { nfa_pim_T pim; - /* - * First try matching what follows. Only if a match - * is found verify the invisible match matches. Add a - * nfa_pim_T to the following states, it contains info - * about the invisible match. - */ + // First try matching what follows. Only if a match + // is found verify the invisible match matches. Add a + // nfa_pim_T to the following states, it contains info + // about the invisible match. pim.state = t->state; pim.result = NFA_PIM_TODO; pim.subs.norm.in_use = 0; @@ -5160,9 +5159,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else pim.end.ptr = reginput; - /* t->state->out1 is the corresponding END_INVISIBLE - * node; Add its out to the current list (zero-width - * match). */ + // t->state->out1 is the corresponding END_INVISIBLE + // node; Add its out to the current list (zero-width + // match). addstate_here(thislist, t->state->out1->out, &t->subs, &pim, &listidx); } @@ -5176,8 +5175,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm int skip_lid = 0; #endif - /* There is no point in trying to match the pattern if the - * output state is not going to be added to the list. */ + // There is no point in trying to match the pattern if the + // output state is not going to be added to the list. if (state_in_list(nextlist, t->state->out1->out, &t->subs)) { skip = t->state->out1->out; #ifdef REGEXP_DEBUG @@ -5206,15 +5205,16 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #endif break; } - /* Copy submatch info to the recursive call, opposite of what - * happens afterwards. */ + // Copy submatch info to the recursive call, opposite of what + // happens afterwards. copy_sub_off(&m->norm, &t->subs.norm); - if (nfa_has_zsubexpr) + if (nfa_has_zsubexpr) { copy_sub_off(&m->synt, &t->subs.synt); + } - /* First try matching the pattern. */ - result = recursive_regmatch(t->state, NULL, prog, - submatch, m, &listids); + // First try matching the pattern. + result = recursive_regmatch(t->state, NULL, prog, submatch, m, + &listids); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; @@ -5226,36 +5226,38 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm fprintf(log_fd, "NFA_START_PATTERN matches:\n"); log_subsexpr(m); #endif - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &m->norm); - if (nfa_has_zsubexpr) + if (nfa_has_zsubexpr) { copy_sub_off(&t->subs.synt, &m->synt); - /* Now we need to skip over the matched text and then - * continue with what follows. */ - if (REG_MULTI) - /* TODO: multi-line match */ + } + // Now we need to skip over the matched text and then + // continue with what follows. + if (REG_MULTI) { + // TODO(RE): multi-line match bytelen = m->norm.list.multi[0].end_col - (int)(reginput - regline); - else + } else { bytelen = (int)(m->norm.list.line[0].end - reginput); + } #ifdef REGEXP_DEBUG fprintf(log_fd, "NFA_START_PATTERN length: %d\n", bytelen); #endif if (bytelen == 0) { - /* empty match, output of corresponding - * NFA_END_PATTERN/NFA_SKIP to be used at current - * position */ + // empty match, output of corresponding + // NFA_END_PATTERN/NFA_SKIP to be used at current + // position add_here = true; add_state = t->state->out1->out->out; } else if (bytelen <= clen) { - /* match current character, output of corresponding - * NFA_END_PATTERN to be used at next position. */ + // match current character, output of corresponding + // NFA_END_PATTERN to be used at next position. add_state = t->state->out1->out->out; add_off = clen; } else { - /* skip over the matched characters, set character - * count in NFA_SKIP */ + // skip over the matched characters, set character + // count in NFA_SKIP add_state = t->state->out1->out; add_off = bytelen; add_count = bytelen - clen; @@ -5279,23 +5281,25 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; case NFA_BOW: - result = TRUE; + result = true; - if (curc == NUL) - result = FALSE; - else if (has_mbyte) { + if (curc == NUL) { + result = false; + } else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ + // Get class of current and previous char (if it exists). this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - result = FALSE; - else if (reg_prev_class() == this_class) - result = FALSE; + if (this_class <= 1) { + result = false; + } else if (reg_prev_class() == this_class) { + result = false; + } } else if (!vim_iswordc_buf(curc, reg_buf) || (reginput > regline - && vim_iswordc_buf(reginput[-1], reg_buf))) - result = FALSE; + && vim_iswordc_buf(reginput[-1], reg_buf))) { + result = false; + } if (result) { add_here = true; add_state = t->state->out; @@ -5303,22 +5307,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; case NFA_EOW: - result = TRUE; - if (reginput == regline) - result = FALSE; - else if (has_mbyte) { + result = true; + if (reginput == regline) { + result = false; + } else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ + // Get class of current and previous char (if it exists). this_class = mb_get_class_buf(reginput, reg_buf); prev_class = reg_prev_class(); if (this_class == prev_class - || prev_class == 0 || prev_class == 1) - result = FALSE; + || prev_class == 0 || prev_class == 1) { + result = false; + } } else if (!vim_iswordc_buf(reginput[-1], reg_buf) || (reginput[0] != NUL - && vim_iswordc_buf(curc, reg_buf))) - result = FALSE; + && vim_iswordc_buf(curc, reg_buf))) { + result = false; + } if (result) { add_here = true; add_state = t->state->out; @@ -5353,30 +5359,31 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm sta = t->state->out; len = 0; if (utf_iscomposing(sta->c)) { - /* Only match composing character(s), ignore base - * character. Used for ".{composing}" and "{composing}" - * (no preceding character). */ + // Only match composing character(s), ignore base + // character. Used for ".{composing}" and "{composing}" + // (no preceding character). len += mb_char2len(mc); } if (ireg_icombine && len == 0) { - /* If \Z was present, then ignore composing characters. - * When ignoring the base character this always matches. */ - if (sta->c != curc) + // If \Z was present, then ignore composing characters. + // When ignoring the base character this always matches. + if (sta->c != curc) { result = FAIL; - else + } else { result = OK; - while (sta->c != NFA_END_COMPOSING) + } + while (sta->c != NFA_END_COMPOSING) { sta = sta->out; - } - /* Check base character matches first, unless ignored. */ - else if (len > 0 || mc == sta->c) { + } + } else if (len > 0 || mc == sta->c) { + // Check base character matches first, unless ignored. if (len == 0) { len += mb_char2len(mc); sta = sta->out; } - /* We don't care about the order of composing characters. - * Get them into cchars[] first. */ + // We don't care about the order of composing characters. + // Get them into cchars[] first. while (len < clen) { mc = mb_ptr2char(reginput + len); cchars[ccount++] = mc; @@ -5385,9 +5392,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } - /* Check that each composing char in the pattern matches a - * composing char in the text. We do not check if all - * composing chars are matched. */ + // Check that each composing char in the pattern matches a + // composing char in the text. We do not check if all + // composing chars are matched. result = OK; while (sta->c != NFA_END_COMPOSING) { for (j = 0; j < ccount; ++j) @@ -5402,7 +5409,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else result = FAIL; - end = t->state->out1; /* NFA_END_COMPOSING */ + end = t->state->out1; // NFA_END_COMPOSING ADD_STATE_IF_MATCH(end); break; } @@ -5410,13 +5417,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_NEWL: if (curc == NUL && !reg_line_lbr && REG_MULTI && reglnum <= reg_maxline) { - go_to_nextline = TRUE; - /* Pass -1 for the offset, which means taking the position - * at the start of the next line. */ + go_to_nextline = true; + // Pass -1 for the offset, which means taking the position + // at the start of the next line. add_state = t->state->out; add_off = -1; } else if (curc == '\n' && reg_line_lbr) { - /* match \n as if it is an ordinary character */ + // match \n as if it is an ordinary character add_state = t->state->out; add_off = 1; } @@ -5425,16 +5432,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_START_COLL: case NFA_START_NEG_COLL: { - /* What follows is a list of characters, until NFA_END_COLL. - * One of them must match or none of them must match. */ + // What follows is a list of characters, until NFA_END_COLL. + // One of them must match or none of them must match. nfa_state_T *state; int result_if_matched; int c1, c2; - /* Never match EOL. If it's part of the collection it is added - * as a separate state with an OR. */ - if (curc == NUL) + // Never match EOL. If it's part of the collection it is added + // as a separate state with an OR. + if (curc == NUL) { break; + } state = t->state->out; result_if_matched = (t->state->c == NFA_START_COLL); @@ -5445,7 +5453,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } if (state->c == NFA_RANGE_MIN) { c1 = state->val; - state = state->out; /* advance to NFA_RANGE_MAX */ + state = state->out; // advance to NFA_RANGE_MAX c2 = state->val; #ifdef REGEXP_DEBUG fprintf(log_fd, "NFA_RANGE_MIN curc=%d c1=%d c2=%d\n", @@ -5478,8 +5486,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm state = state->out; } if (result) { - /* next state is in out of the NFA_END_COLL, out1 of - * START points to the END state */ + // next state is in out of the NFA_END_COLL, out1 of + // START points to the END state add_state = t->state->out1->out; add_off = clen; } @@ -5487,7 +5495,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } case NFA_ANY: - /* Any char except '\0', (end of input) does not match. */ + // Any char except '\0', (end of input) does not match. if (curc > 0) { add_state = t->state->out; add_off = clen; @@ -5506,157 +5514,155 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm add_state = t->state->out; break; - /* - * Character classes like \a for alpha, \d for digit etc. - */ - case NFA_IDENT: /* \i */ + // Character classes like \a for alpha, \d for digit etc. + case NFA_IDENT: // \i result = vim_isIDc(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SIDENT: /* \I */ + case NFA_SIDENT: // \I result = !ascii_isdigit(curc) && vim_isIDc(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_KWORD: /* \k */ + case NFA_KWORD: // \k result = vim_iswordp_buf(reginput, reg_buf); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SKWORD: /* \K */ + case NFA_SKWORD: // \K result = !ascii_isdigit(curc) && vim_iswordp_buf(reginput, reg_buf); ADD_STATE_IF_MATCH(t->state); break; - case NFA_FNAME: /* \f */ + case NFA_FNAME: // \f result = vim_isfilec(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SFNAME: /* \F */ + case NFA_SFNAME: // \F result = !ascii_isdigit(curc) && vim_isfilec(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_PRINT: /* \p */ + case NFA_PRINT: // \p result = vim_isprintc(PTR2CHAR(reginput)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SPRINT: /* \P */ + case NFA_SPRINT: // \P result = !ascii_isdigit(curc) && vim_isprintc(PTR2CHAR(reginput)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_WHITE: /* \s */ + case NFA_WHITE: // \s result = ascii_iswhite(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NWHITE: /* \S */ + case NFA_NWHITE: // \S result = curc != NUL && !ascii_iswhite(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_DIGIT: /* \d */ + case NFA_DIGIT: // \d result = ri_digit(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NDIGIT: /* \D */ + case NFA_NDIGIT: // \D result = curc != NUL && !ri_digit(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_HEX: /* \x */ + case NFA_HEX: // \x result = ri_hex(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NHEX: /* \X */ + case NFA_NHEX: // \X result = curc != NUL && !ri_hex(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_OCTAL: /* \o */ + case NFA_OCTAL: // \o result = ri_octal(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NOCTAL: /* \O */ + case NFA_NOCTAL: // \O result = curc != NUL && !ri_octal(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_WORD: /* \w */ + case NFA_WORD: // \w result = ri_word(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NWORD: /* \W */ + case NFA_NWORD: // \W result = curc != NUL && !ri_word(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_HEAD: /* \h */ + case NFA_HEAD: // \h result = ri_head(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NHEAD: /* \H */ + case NFA_NHEAD: // \H result = curc != NUL && !ri_head(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_ALPHA: /* \a */ + case NFA_ALPHA: // \a result = ri_alpha(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NALPHA: /* \A */ + case NFA_NALPHA: // \A result = curc != NUL && !ri_alpha(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_LOWER: /* \l */ + case NFA_LOWER: // \l result = ri_lower(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NLOWER: /* \L */ + case NFA_NLOWER: // \L result = curc != NUL && !ri_lower(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_UPPER: /* \u */ + case NFA_UPPER: // \u result = ri_upper(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NUPPER: /* \U */ + case NFA_NUPPER: // \U result = curc != NUL && !ri_upper(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_LOWER_IC: /* [a-z] */ + case NFA_LOWER_IC: // [a-z] result = ri_lower(curc) || (ireg_ic && ri_upper(curc)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NLOWER_IC: /* [^a-z] */ + case NFA_NLOWER_IC: // [^a-z] result = curc != NUL && !(ri_lower(curc) || (ireg_ic && ri_upper(curc))); ADD_STATE_IF_MATCH(t->state); break; - case NFA_UPPER_IC: /* [A-Z] */ + case NFA_UPPER_IC: // [A-Z] result = ri_upper(curc) || (ireg_ic && ri_lower(curc)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NUPPER_IC: /* ^[A-Z] */ + case NFA_NUPPER_IC: // [^A-Z] result = curc != NUL && !(ri_upper(curc) || (ireg_ic && ri_lower(curc))); ADD_STATE_IF_MATCH(t->state); @@ -5680,7 +5686,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_ZREF7: case NFA_ZREF8: case NFA_ZREF9: - /* \1 .. \9 \z1 .. \z9 */ + // \1 .. \9 \z1 .. \z9 { int subidx; int bytelen; @@ -5695,18 +5701,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (result) { if (bytelen == 0) { - /* empty match always works, output of NFA_SKIP to be - * used next */ + // empty match always works, output of NFA_SKIP to be + // used next add_here = true; add_state = t->state->out->out; } else if (bytelen <= clen) { - /* match current character, jump ahead to out of - * NFA_SKIP */ + // match current character, jump ahead to out of + // NFA_SKIP add_state = t->state->out->out; add_off = clen; } else { - /* skip over the matched characters, set character - * count in NFA_SKIP */ + // skip over the matched characters, set character + // count in NFA_SKIP add_state = t->state->out; add_off = bytelen; add_count = bytelen - clen; @@ -5715,13 +5721,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } case NFA_SKIP: - /* character of previous matching \1 .. \9 or \@> */ + // character of previous matching \1 .. \9 or \@> if (t->count - clen <= 0) { - /* end of match, go to what follows */ + // end of match, go to what follows add_state = t->state->out; add_off = clen; } else { - /* add state again with decremented count */ + // add state again with decremented count add_state = t->state; add_off = 0; add_count = t->count - clen; @@ -5773,7 +5779,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } - result = FALSE; + result = false; win_T *wp = reg_win == NULL ? curwin : reg_win; if (op == 1 && col - 1 > t->state->val && col > 100) { long ts = wp->w_buffer->b_p_ts; @@ -5803,9 +5809,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm { pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE); - /* Compare the mark position to the match position. */ - result = (pos != NULL /* mark doesn't exist */ - && pos->lnum > 0 /* mark isn't set in reg_buf */ + // Compare the mark position to the match position. + result = (pos != NULL // mark doesn't exist + && pos->lnum > 0 // mark isn't set in reg_buf && (pos->lnum == reglnum + reg_firstlnum ? (pos->col == (colnr_T)(reginput - regline) ? t->state->c == NFA_MARK @@ -5862,11 +5868,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_ZOPEN9: case NFA_NOPEN: case NFA_ZSTART: - /* These states are only added to be able to bail out when - * they are added again, nothing is to be done. */ + // These states are only added to be able to bail out when + // they are added again, nothing is to be done. break; - default: /* regular character */ + default: // regular character { int c = t->state->c; @@ -5888,8 +5894,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm ADD_STATE_IF_MATCH(t->state); break; } - - } /* switch (t->state->c) */ + } // switch (t->state->c) if (add_state != NULL) { nfa_pim_T *pim; @@ -5900,8 +5905,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm else pim = &t->pim; - /* Handle the postponed invisible match if the match might end - * without advancing and before the end of the line. */ + // Handle the postponed invisible match if the match might end + // without advancing and before the end of the line. if (pim != NULL && (clen == 0 || match_follows(add_state, 0))) { if (pim->result == NFA_PIM_TODO) { #ifdef REGEXP_DEBUG @@ -5913,15 +5918,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm result = recursive_regmatch(pim->state, pim, prog, submatch, m, &listids); pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH; - /* for \@! and \@<! it is a match when the result is - * FALSE */ + // for \@! and \@<! it is a match when the result is + // FALSE if (result != (pim->state->c == NFA_START_INVISIBLE_NEG || pim->state->c == NFA_START_INVISIBLE_NEG_FIRST || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&pim->subs.norm, &m->norm); if (nfa_has_zsubexpr) copy_sub_off(&pim->subs.synt, &m->synt); @@ -5934,34 +5939,35 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm log_fd, "Using previous recursive nfa_regmatch() result, result == %d\n", pim->result); - fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE"); + fprintf(log_fd, "MATCH = %s\n", result ? "OK" : "FALSE"); fprintf(log_fd, "\n"); #endif } - /* for \@! and \@<! it is a match when result is FALSE */ + // for \@! and \@<! it is a match when result is FALSE if (result != (pim->state->c == NFA_START_INVISIBLE_NEG || pim->state->c == NFA_START_INVISIBLE_NEG_FIRST || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &pim->subs.norm); if (nfa_has_zsubexpr) copy_sub_off(&t->subs.synt, &pim->subs.synt); - } else - /* look-behind match failed, don't add the state */ + } else { + // look-behind match failed, don't add the state continue; + } - /* Postponed invisible match was handled, don't add it to - * following states. */ + // Postponed invisible match was handled, don't add it to + // following states. pim = NULL; } - /* If "pim" points into l->t it will become invalid when - * adding the state causes the list to be reallocated. Make a - * local copy to avoid that. */ + // If "pim" points into l->t it will become invalid when + // adding the state causes the list to be reallocated. Make a + // local copy to avoid that. if (pim == &t->pim) { copy_pim(&pim_copy, pim); pim = &pim_copy; @@ -5975,18 +5981,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm nextlist->t[nextlist->n - 1].count = add_count; } } - - } /* for (thislist = thislist; thislist->state; thislist++) */ - - /* Look for the start of a match in the current position by adding the - * start state to the list of states. - * The first found match is the leftmost one, thus the order of states - * matters! - * Do not add the start state in recursive calls of nfa_regmatch(), - * because recursive calls should only start in the first position. - * Unless "nfa_endp" is not NULL, then we match the end position. - * Also don't start a match past the first line. */ - if (nfa_match == FALSE + } // for (thislist = thislist; thislist->state; thislist++) + + // Look for the start of a match in the current position by adding the + // start state to the list of states. + // The first found match is the leftmost one, thus the order of states + // matters! + // Do not add the start state in recursive calls of nfa_regmatch(), + // because recursive calls should only start in the first position. + // Unless "nfa_endp" is not NULL, then we match the end position. + // Also don't start a match past the first line. + if (!nfa_match && ((toplevel && reglnum == 0 && clen != 0 @@ -6002,8 +6007,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #ifdef REGEXP_DEBUG fprintf(log_fd, "(---) STARTSTATE\n"); #endif - /* Inline optimized code for addstate() if we know the state is - * the first MOPEN. */ + // Inline optimized code for addstate() if we know the state is + // the first MOPEN. if (toplevel) { int add = TRUE; int c; @@ -6012,18 +6017,19 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (nextlist->n == 0) { colnr_T col = (colnr_T)(reginput - regline) + clen; - /* Nextlist is empty, we can skip ahead to the - * character that must appear at the start. */ - if (skip_to_start(prog->regstart, &col) == FAIL) + // Nextlist is empty, we can skip ahead to the + // character that must appear at the start. + if (skip_to_start(prog->regstart, &col) == FAIL) { break; + } #ifdef REGEXP_DEBUG fprintf(log_fd, " Skipping ahead %d bytes to regstart\n", col - ((colnr_T)(reginput - regline) + clen)); #endif reginput = regline + col - clen; } else { - /* Checking if the required start character matches is - * cheaper than adding a state that won't match. */ + // Checking if the required start character matches is + // cheaper than adding a state that won't match. c = PTR2CHAR(reginput + clen); if (c != prog->regstart && (!ireg_ic || vim_tolower(c) != vim_tolower(prog->regstart))) { @@ -6060,21 +6066,29 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #endif nextchar: - /* Advance to the next character, or advance to the next line, or - * finish. */ - if (clen != 0) + // Advance to the next character, or advance to the next line, or + // finish. + if (clen != 0) { reginput += clen; - else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI - && reglnum < nfa_endp->se_u.pos.lnum)) + } else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI + && reglnum < nfa_endp->se_u.pos.lnum)) { reg_nextline(); - else + } else { break; + } // Allow interrupting with CTRL-C. - fast_breakcheck(); + line_breakcheck(); if (got_int) { break; } + // Check for timeout once every twenty times to avoid overhead. + if (nfa_time_limit != NULL && ++nfa_time_count == 20) { + nfa_time_count = 0; + if (profile_passed_limit(*nfa_time_limit)) { + break; + } + } } #ifdef REGEXP_DEBUG @@ -6084,7 +6098,7 @@ nextchar: #endif theend: - /* Free memory */ + // Free memory xfree(list[0].t); xfree(list[1].t); xfree(listids); @@ -6096,11 +6110,9 @@ theend: return nfa_match; } -/* - * Try match of "prog" with at regline["col"]. - * Returns <= 0 for failure, number of lines contained in the match otherwise. - */ -static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) +// Try match of "prog" with at regline["col"]. +// Returns <= 0 for failure, number of lines contained in the match otherwise. +static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) { int i; regsubs_T subs, m; @@ -6110,6 +6122,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) #endif reginput = regline + col; + nfa_time_limit = tm; + nfa_time_count = 0; #ifdef REGEXP_DEBUG f = fopen(NFA_REGEXP_RUN_LOG, "a"); @@ -6134,7 +6148,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) clear_sub(&m.synt); int result = nfa_regmatch(prog, start, &subs, &m); - if (result == FALSE) { + if (!result) { return 0; } else if (result == NFA_TOO_EXPENSIVE) { return result; @@ -6207,17 +6221,16 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) return 1 + reglnum; } -/* - * Match a regexp against a string ("line" points to the string) or multiple - * lines ("line" is NULL, use reg_getline()). - * - * Returns <= 0 for failure, number of lines contained in the match otherwise. - */ -static long -nfa_regexec_both ( - char_u *line, - colnr_T startcol /* column to start looking for match */ -) +/// Match a regexp against a string ("line" points to the string) or multiple +/// lines ("line" is NULL, use reg_getline()). +/// +/// @param line String in which to search or NULL +/// @param startcol Column to start looking for match +/// @param tm Timeout limit or NULL +/// +/// @return <= 0 if there is no match and number of lines contained in the +/// match otherwise. +static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) { nfa_regprog_T *prog; long retval = 0L; @@ -6297,7 +6310,7 @@ nfa_regexec_both ( prog->state[i].lastlist[1] = 0; } - retval = nfa_regtry(prog, col); + retval = nfa_regtry(prog, col, tm); nfa_regengine.expr = NULL; @@ -6449,7 +6462,7 @@ nfa_regexec_nl ( ireg_ic = rmp->rm_ic; ireg_icombine = FALSE; ireg_maxcol = 0; - return nfa_regexec_both(line, col); + return nfa_regexec_both(line, col, NULL); } /// Matches a regexp against multiple lines. @@ -6500,5 +6513,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, ireg_icombine = FALSE; ireg_maxcol = rmp->rmm_maxcol; - return nfa_regexec_both(NULL, col); + return nfa_regexec_both(NULL, col, tm); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index cd440fe8dc..10b5b6bba4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -387,14 +387,15 @@ void update_screen(int type) if (wp->w_buffer->b_mod_set) { win_T *wwp; - /* Check if we already did this buffer. */ - for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) - if (wwp->w_buffer == wp->w_buffer) + // Check if we already did this buffer. + for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) { + if (wwp->w_buffer == wp->w_buffer) { break; - if ( - wwp == wp && - syntax_present(wp)) + } + } + if (wwp == wp && syntax_present(wp)) { syn_stack_apply_changes(wp->w_buffer); + } } } @@ -1231,16 +1232,16 @@ static void win_update(win_T *wp) || did_update == DID_FOLD || (did_update == DID_LINE && syntax_present(wp) - && ( - (foldmethodIsSyntax(wp) - && hasAnyFolding(wp)) || - syntax_check_changed(lnum))) + && ((foldmethodIsSyntax(wp) + && hasAnyFolding(wp)) + || syntax_check_changed(lnum))) // match in fixed position might need redraw // if lines were inserted or deleted - || (wp->w_match_head != NULL && buf->b_mod_xlines != 0) - ))))) { - if (lnum == mod_top) - top_to_mod = FALSE; + || (wp->w_match_head != NULL + && buf->b_mod_xlines != 0)))))) { + if (lnum == mod_top) { + top_to_mod = false; + } /* * When at start of changed lines: May scroll following lines @@ -2472,21 +2473,16 @@ win_line ( mb_ptr_adv(ptr); } - /* When: - * - 'cuc' is set, or - * - 'colorcolumn' is set, or - * - 'virtualedit' is set, or - * - the visual mode is active, - * the end of the line may be before the start of the displayed part. - */ - if (vcol < v && ( - wp->w_p_cuc - || draw_color_col - || - virtual_active() - || - (VIsual_active && wp->w_buffer == curwin->w_buffer) - )) { + // When: + // - 'cuc' is set, or + // - 'colorcolumn' is set, or + // - 'virtualedit' is set, or + // - the visual mode is active, + // the end of the line may be before the start of the displayed part. + if (vcol < v && (wp->w_p_cuc + || draw_color_col + || virtual_active() + || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { vcol = v; } @@ -3273,12 +3269,12 @@ win_line ( * contains the @Spell cluster. */ if (has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; - if (!attr_pri) + if (!attr_pri) { char_attr = syntax_attr; - if (c != 0 && ( - !has_syntax || - can_spell)) { - char_u *prev_ptr, *p; + } + if (c != 0 && (!has_syntax || can_spell)) { + char_u *prev_ptr; + char_u *p; int len; hlf_T spell_hlf = HLF_COUNT; if (has_mbyte) { @@ -3607,25 +3603,22 @@ win_line ( wp->w_p_rl ? (col >= 0) : (col < wp->w_width))) { c = ' '; - --ptr; /* put it back at the NUL */ - } else if (( - diff_hlf != (hlf_T)0 || - line_attr != 0 - ) && ( - wp->w_p_rl ? (col >= 0) : - (col - - boguscols - < wp->w_width))) { - /* Highlight until the right side of the window */ + ptr--; // put it back at the NUL + } else if ((diff_hlf != (hlf_T)0 || line_attr != 0) + && (wp->w_p_rl + ? (col >= 0) + : (col - boguscols < wp->w_width))) { + // Highlight until the right side of the window c = ' '; - --ptr; /* put it back at the NUL */ + ptr--; // put it back at the NUL - /* Remember we do the char for line highlighting. */ - ++did_line_attr; + // Remember we do the char for line highlighting. + did_line_attr++; - /* don't do search HL for the rest of the line */ - if (line_attr != 0 && char_attr == search_attr && col > 0) + // don't do search HL for the rest of the line + if (line_attr != 0 && char_attr == search_attr && col > 0) { char_attr = line_attr; + } if (diff_hlf == HLF_TXD) { diff_hlf = HLF_CHD; if (attr == 0 || char_attr != attr) { @@ -3639,8 +3632,8 @@ win_line ( } if (wp->w_p_cole > 0 - && (wp != curwin || lnum != wp->w_cursor.lnum || - conceal_cursor_line(wp)) + && (wp != curwin || lnum != wp->w_cursor.lnum + || conceal_cursor_line(wp)) && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc) && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { @@ -3700,9 +3693,13 @@ win_line ( && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) && (int)wp->w_virtcol <= vcol + n_skip) { - wp->w_wcol = col - boguscols; + if (wp->w_p_rl) { + wp->w_wcol = wp->w_width - col + boguscols - 1; + } else { + wp->w_wcol = col - boguscols; + } wp->w_wrow = row; - did_wcol = TRUE; + did_wcol = true; } /* Don't override visual selection highlighting. */ @@ -6780,8 +6777,8 @@ int showmode(void) if (Recording && edit_submode == NULL /* otherwise it gets too long */ ) { - MSG_PUTS_ATTR(_("recording"), attr); - need_clear = TRUE; + recording_mode(attr); + need_clear = true; } mode_displayed = TRUE; @@ -6820,26 +6817,33 @@ static void msg_pos_mode(void) msg_row = Rows - 1; } -/* - * Delete mode message. Used when ESC is typed which is expected to end - * Insert mode (but Insert mode didn't end yet!). - * Caller should check "mode_displayed". - */ -void unshowmode(int force) +/// Delete mode message. Used when ESC is typed which is expected to end +/// Insert mode (but Insert mode didn't end yet!). +/// Caller should check "mode_displayed". +void unshowmode(bool force) { - /* - * Don't delete it right now, when not redrawing or inside a mapping. - */ - if (!redrawing() || (!force && char_avail() && !KeyTyped)) - redraw_cmdline = TRUE; /* delete mode later */ - else { + // Don't delete it right now, when not redrawing or inside a mapping. + if (!redrawing() || (!force && char_avail() && !KeyTyped)) { + redraw_cmdline = true; // delete mode later + } else { msg_pos_mode(); - if (Recording) - MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM)); + if (Recording) { + recording_mode(hl_attr(HLF_CM)); + } msg_clr_eos(); } } +static void recording_mode(int attr) +{ + MSG_PUTS_ATTR(_("recording"), attr); + if (!shortmess(SHM_RECORDING)) { + char_u s[4]; + vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording); + MSG_PUTS_ATTR(s, attr); + } +} + /* * Draw the tab pages line at the top of the Vim window. */ diff --git a/src/nvim/search.c b/src/nvim/search.c index fffae1ecb2..6e2b69fff7 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -453,25 +453,24 @@ void last_pat_prog(regmmatch_T *regmatch) --emsg_off; } -/* - * lowest level search function. - * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. - * Start at position 'pos' and return the found position in 'pos'. - * - * if (options & SEARCH_MSG) == 0 don't give any messages - * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages - * if (options & SEARCH_MSG) == SEARCH_MSG give all messages - * if (options & SEARCH_HIS) put search pattern in history - * if (options & SEARCH_END) return position at end of match - * if (options & SEARCH_START) accept match at pos itself - * if (options & SEARCH_KEEP) keep previous search pattern - * if (options & SEARCH_FOLD) match only once in a closed fold - * if (options & SEARCH_PEEK) check for typed char, cancel search - * - * Return FAIL (zero) for failure, non-zero for success. - * Returns the index of the first matching - * subpattern plus one; one if there was none. - */ +/// lowest level search function. +/// Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. +/// Start at position 'pos' and return the found position in 'pos'. +/// +/// if (options & SEARCH_MSG) == 0 don't give any messages +/// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages +/// if (options & SEARCH_MSG) == SEARCH_MSG give all messages +/// if (options & SEARCH_HIS) put search pattern in history +/// if (options & SEARCH_END) return position at end of match +/// if (options & SEARCH_START) accept match at pos itself +/// if (options & SEARCH_KEEP) keep previous search pattern +/// if (options & SEARCH_FOLD) match only once in a closed fold +/// if (options & SEARCH_PEEK) check for typed char, cancel search +/// if (options & SEARCH_COL) start at pos->col instead of zero +/// +/// @returns FAIL (zero) for failure, non-zero for success. +/// the index of the first matching +/// subpattern plus one; one if there was none. int searchit( win_T *win, /* window to search in, can be NULL for a buffer without a window! */ @@ -571,16 +570,14 @@ int searchit( if (tm != NULL && profile_passed_limit(*tm)) break; - /* - * Look for a match somewhere in line "lnum". - */ + // Look for a match somewhere in line "lnum". + colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, (colnr_T)0, - tm - ); - /* Abort searching on an error (e.g., out of stack). */ - if (called_emsg) + lnum, col, tm); + // Abort searching on an error (e.g., out of stack). + if (called_emsg) { break; + } if (nmatched > 0) { /* match may actually be in another line when using \zs */ matchpos = regmatch.startpos[0]; @@ -641,9 +638,9 @@ int searchit( break; } - if (ptr[matchcol] == NUL || - (nmatched = vim_regexec_multi(®match, win, buf, lnum, - matchcol, tm)) == 0) { + if (ptr[matchcol] == NUL + || (nmatched = vim_regexec_multi(®match, win, buf, lnum, + matchcol, tm)) == 0) { match_ok = false; break; } @@ -881,9 +878,8 @@ static void set_vv_searchforward(void) set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/')); } -/* - * Return the number of the first subpat that matched. - */ +// Return the number of the first subpat that matched. +// Return zero if none of them matched. static int first_submatch(regmmatch_T *rp) { int submatch; @@ -1031,19 +1027,18 @@ int do_search( spats[0].off.line = FALSE; spats[0].off.end = FALSE; spats[0].off.off = 0; - /* - * Check for a line offset or a character offset. - * For get_address (echo off) we don't check for a character - * offset, because it is meaningless and the 's' could be a - * substitute command. - */ - if (*p == '+' || *p == '-' || ascii_isdigit(*p)) - spats[0].off.line = TRUE; - else if ((options & SEARCH_OPT) && - (*p == 'e' || *p == 's' || *p == 'b')) { - if (*p == 'e') /* end */ + // Check for a line offset or a character offset. + // For get_address (echo off) we don't check for a character + // offset, because it is meaningless and the 's' could be a + // substitute command. + if (*p == '+' || *p == '-' || ascii_isdigit(*p)) { + spats[0].off.line = true; + } else if ((options & SEARCH_OPT) + && (*p == 'e' || *p == 's' || *p == 'b')) { + if (*p == 'e') { // end spats[0].off.end = true; - ++p; + } + p++; } if (ascii_isdigit(*p) || *p == '+' || *p == '-') { /* got an offset */ /* 'nr' or '+nr' or '-nr' */ @@ -1651,8 +1646,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) /* * Look for matching #if, #else, #elif, or #endif */ - if (oap != NULL) - oap->motion_type = MLINE; /* Linewise for this case only */ + if (oap != NULL) { + oap->motion_type = kMTLineWise; // Linewise for this case only + } if (initc != '#') { ptr = skipwhite(skipwhite(linep) + 1); if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) @@ -1787,14 +1783,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } } - /* - * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. - */ - if (pos.col == 0 && (flags & FM_BLOCKSTOP) && - (linep[0] == '{' || linep[0] == '}')) { - if (linep[0] == findc && count == 0) /* match! */ + // If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. + if (pos.col == 0 && (flags & FM_BLOCKSTOP) + && (linep[0] == '{' || linep[0] == '}')) { + if (linep[0] == findc && count == 0) { // match! return &pos; - break; /* out of scope */ + } + break; // out of scope } if (comment_dir) { @@ -1963,15 +1958,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (linep[pos.col - 2] == '\'') { pos.col -= 2; break; - } else if (linep[pos.col - 2] == '\\' && - pos.col > 2 && linep[pos.col - 3] == '\'') { + } else if (linep[pos.col - 2] == '\\' + && pos.col > 2 && linep[pos.col - 3] == '\'') { pos.col -= 3; break; } } - } else if (linep[pos.col + 1]) { /* forward search */ - if (linep[pos.col + 1] == '\\' && - linep[pos.col + 2] && linep[pos.col + 3] == '\'') { + } else if (linep[pos.col + 1]) { // forward search + if (linep[pos.col + 1] == '\\' + && linep[pos.col + 2] && linep[pos.col + 3] == '\'') { pos.col += 3; break; } else if (linep[pos.col + 2] == '\'') { @@ -2188,30 +2183,32 @@ int findsent(int dir, long count) * if on an empty line, skip up to a non-empty line */ if (gchar_pos(&pos) == NUL) { - do - if ((*func)(&pos) == -1) + do { + if ((*func)(&pos) == -1) { break; - while (gchar_pos(&pos) == NUL); - if (dir == FORWARD) + } + } while (gchar_pos(&pos) == NUL); + if (dir == FORWARD) { goto found; - } - /* - * if on the start of a paragraph or a section and searching forward, - * go to the next line - */ - else if (dir == FORWARD && pos.col == 0 && - startPS(pos.lnum, NUL, FALSE)) { - if (pos.lnum == curbuf->b_ml.ml_line_count) + } + // if on the start of a paragraph or a section and searching forward, + // go to the next line + } else if (dir == FORWARD && pos.col == 0 + && startPS(pos.lnum, NUL, false)) { + if (pos.lnum == curbuf->b_ml.ml_line_count) { return FAIL; - ++pos.lnum; + } + pos.lnum++; goto found; - } else if (dir == BACKWARD) + } else if (dir == BACKWARD) { decl(&pos); + } - /* go back to the previous non-blank char */ - found_dot = FALSE; - while ((c = gchar_pos(&pos)) == ' ' || c == '\t' || - (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) { + // go back to the previous non-blank char + found_dot = false; + while ((c = gchar_pos(&pos)) == ' ' || c == '\t' + || (dir == BACKWARD + && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) { if (vim_strchr((char_u *)".!?", c) != NULL) { /* Only skip over a '.', '!' and '?' once. */ if (found_dot) @@ -2375,12 +2372,14 @@ int startPS(linenr_T lnum, int para, int both) char_u *s; s = ml_get(lnum); - if (*s == para || *s == '\f' || (both && *s == '}')) - return TRUE; - if (*s == '.' && (inmacro(p_sections, s + 1) || - (!para && inmacro(p_para, s + 1)))) - return TRUE; - return FALSE; + if (*s == para || *s == '\f' || (both && *s == '}')) { + return true; + } + if (*s == '.' && (inmacro(p_sections, s + 1) + || (!para && inmacro(p_para, s + 1)))) { + return true; + } + return false; } /* @@ -2797,7 +2796,7 @@ current_word ( redraw_curbuf_later(INVERTED); /* update the inversion */ } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } --count; } @@ -3032,7 +3031,7 @@ extend: else oap->inclusive = false; oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } return OK; } @@ -3156,7 +3155,7 @@ current_block ( showmode(); } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = false; if (sol) incl(&curwin->w_cursor); @@ -3403,7 +3402,7 @@ again: showmode(); } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; if (lt(end_pos, start_pos)) { /* End is before the start: there is no text between tags; operate * on an empty area. */ @@ -3568,7 +3567,7 @@ extend: } else { oap->start.lnum = start_lnum; oap->start.col = 0; - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; } curwin->w_cursor.lnum = end_lnum; curwin->w_cursor.col = 0; @@ -3810,7 +3809,7 @@ current_quote ( } } else { oap->start = curwin->w_cursor; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } /* Set end position. */ @@ -4154,19 +4153,20 @@ find_pattern_in_path ( FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL); already_searched = FALSE; if (new_fname != NULL) { - /* Check whether we have already searched in this file */ + // Check whether we have already searched in this file for (i = 0;; i++) { - if (i == depth + 1) + if (i == depth + 1) { i = old_files; - if (i == max_path_depth) + } + if (i == max_path_depth) { break; - if (path_full_compare(new_fname, files[i].name, TRUE) & kEqualFiles) { - if (type != CHECK_PATH && - action == ACTION_SHOW_ALL && files[i].matched) { - msg_putchar('\n'); /* cursor below last one */ - if (!got_int) { /* don't display if 'q' - typed at "--more--" - message */ + } + if (path_full_compare(new_fname, files[i].name, true) & kEqualFiles) { + if (type != CHECK_PATH + && action == ACTION_SHOW_ALL && files[i].matched) { + msg_putchar('\n'); // cursor below last one */ + if (!got_int) { // don't display if 'q' typed at "--more--" + // message msg_home_replace_hl(new_fname); MSG_PUTS(_(" (includes previously listed match)")); prev_fname = NULL; @@ -4181,15 +4181,15 @@ find_pattern_in_path ( } if (type == CHECK_PATH && (action == ACTION_SHOW_ALL - || (new_fname == NULL && - !already_searched))) { - if (did_show) - msg_putchar('\n'); /* cursor below last one */ - else { - gotocmdline(TRUE); /* cursor at status line */ + || (new_fname == NULL && !already_searched))) { + if (did_show) { + msg_putchar('\n'); // cursor below last one + } else { + gotocmdline(true); // cursor at status line MSG_PUTS_TITLE(_("--- Included files ")); - if (action != ACTION_SHOW_ALL) + if (action != ACTION_SHOW_ALL) { MSG_PUTS_TITLE(_("not found ")); + } MSG_PUTS_TITLE(_("in path ---\n")); } did_show = TRUE; @@ -4348,16 +4348,15 @@ search_line: && vim_regexec(®match, line, (colnr_T)(p - line))) { matched = TRUE; startp = regmatch.startp[0]; - /* - * Check if the line is not a comment line (unless we are - * looking for a define). A line starting with "# define" - * is not considered to be a comment line. - */ + // Check if the line is not a comment line (unless we are + // looking for a define). A line starting with "# define" + // is not considered to be a comment line. if (skip_comments) { - if ((*line != '#' || - STRNCMP(skipwhite(line + 1), "define", 6) != 0) - && get_leader_len(line, NULL, FALSE, TRUE)) - matched = FALSE; + if ((*line != '#' + || STRNCMP(skipwhite(line + 1), "define", 6) != 0) + && get_leader_len(line, NULL, false, true)) { + matched = false; + } /* * Also check for a "/ *" or "/ /" before the match. diff --git a/src/nvim/search.h b/src/nvim/search.h index 6947f79d49..d4e40cb287 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -15,19 +15,20 @@ #define ACTION_SHOW_ALL 4 #define ACTION_EXPAND 5 -/* Values for 'options' argument in do_search() and searchit() */ -#define SEARCH_REV 0x01 /* go in reverse of previous dir. */ -#define SEARCH_ECHO 0x02 /* echo the search command and handle options */ -#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */ -#define SEARCH_NFMSG 0x08 /* give all messages except not found */ -#define SEARCH_OPT 0x10 /* interpret optional flags */ -#define SEARCH_HIS 0x20 /* put search pattern in history */ -#define SEARCH_END 0x40 /* put cursor at end of match */ -#define SEARCH_NOOF 0x80 /* don't add offset to position */ -#define SEARCH_START 0x100 /* start search without col offset */ -#define SEARCH_MARK 0x200 /* set previous context mark */ -#define SEARCH_KEEP 0x400 /* keep previous search pattern */ -#define SEARCH_PEEK 0x800 /* peek for typed char, cancel search */ +// Values for 'options' argument in do_search() and searchit() +#define SEARCH_REV 0x01 ///< go in reverse of previous dir. +#define SEARCH_ECHO 0x02 ///< echo the search command and handle options +#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04) +#define SEARCH_NFMSG 0x08 ///< give all messages except not found +#define SEARCH_OPT 0x10 ///< interpret optional flags +#define SEARCH_HIS 0x20 ///< put search pattern in history +#define SEARCH_END 0x40 ///< put cursor at end of match +#define SEARCH_NOOF 0x80 ///< don't add offset to position +#define SEARCH_START 0x100 ///< start search without col offset +#define SEARCH_MARK 0x200 ///< set previous context mark +#define SEARCH_KEEP 0x400 ///< keep previous search pattern +#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search +#define SEARCH_COL 0x1000 ///< start at specified column instead of zero /* Values for flags argument for findmatchlimit() */ #define FM_BACKWARD 0x01 /* search backwards */ diff --git a/src/nvim/shada.c b/src/nvim/shada.c index def2de9b1a..51c8597d53 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,13 +38,15 @@ #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" #ifdef HAVE_BE64TOH # define _BSD_SOURCE 1 # define _DEFAULT_SOURCE 1 -# include <endian.h> +# include ENDIAN_INCLUDE_FILE #endif // Note: when using bufset hash pointers are intentionally casted to uintptr_t @@ -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) \ @@ -285,7 +283,7 @@ typedef struct { } history_item; struct reg { char name; - uint8_t type; + MotionType type; char **contents; size_t contents_size; size_t width; @@ -477,7 +475,7 @@ static const ShadaEntry sd_default_values[] = { .additional_elements = NULL), DEF_SDE(Register, reg, .name = NUL, - .type = MCHAR, + .type = kMTCharWise, .contents = NULL, .contents_size = 0, .width = 0, @@ -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); @@ -1409,9 +1407,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) break; } case kSDItemRegister: { - if (cur_entry.data.reg.type != MCHAR - && cur_entry.data.reg.type != MLINE - && cur_entry.data.reg.type != MBLOCK) { + if (cur_entry.data.reg.type != kMTCharWise + && cur_entry.data.reg.type != kMTLineWise + && cur_entry.data.reg.type != kMTBlockWise) { shada_free_shada_entry(&cur_entry); break; } @@ -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"), @@ -1882,7 +1882,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, msgpack_pack_char(spacker, entry.data.reg.name); if (!CHECK_DEFAULT(entry, reg.type)) { PACK_STATIC_STR(REG_KEY_TYPE); - msgpack_pack_uint8(spacker, entry.data.reg.type); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type); } if (!CHECK_DEFAULT(entry, reg.width)) { PACK_STATIC_STR(REG_KEY_WIDTH); @@ -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; @@ -2757,8 +2757,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .reg = { .contents = (char **) reg.y_array, .contents_size = (size_t) reg.y_size, - .type = (uint8_t) reg.y_type, - .width = (size_t) (reg.y_type == MBLOCK ? reg.y_width : 0), + .type = reg.y_type, + .width = (size_t) (reg.y_type == kMTBlockWise ? reg.y_width : 0), .additional_data = reg.additional_data, .name = name, } @@ -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..9a5484704e 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -62,8 +62,10 @@ struct hl_group { int sg_gui; // "gui=" highlighting attributes RgbValue sg_rgb_fg; // RGB foreground color RgbValue sg_rgb_bg; // RGB background color + RgbValue sg_rgb_sp; // RGB special color uint8_t *sg_rgb_fg_name; // RGB foreground color name uint8_t *sg_rgb_bg_name; // RGB background color name + uint8_t *sg_rgb_sp_name; // RGB special color name }; #define SG_CTERM 2 // cterm has been set @@ -2615,33 +2617,37 @@ find_endpos ( IF_SYN_TIME(&spp_skip->sp_time)); spp_skip->sp_prog = regmatch.regprog; if (r && regmatch.startpos[0].col <= best_regmatch.startpos[0].col) { - /* Add offset to skip pattern match */ + // Add offset to skip pattern match syn_add_end_off(&pos, ®match, spp_skip, SPO_ME_OFF, 1); - /* If the skip pattern goes on to the next line, there is no - * match with an end pattern in this line. */ - if (pos.lnum > startpos->lnum) + // If the skip pattern goes on to the next line, there is no + // match with an end pattern in this line. + if (pos.lnum > startpos->lnum) { break; + } - line = ml_get_buf(syn_buf, startpos->lnum, FALSE); + line = ml_get_buf(syn_buf, startpos->lnum, false); + int line_len = (int)STRLEN(line); - /* take care of an empty match or negative offset */ - if (pos.col <= matchcol) - ++matchcol; - else if (pos.col <= regmatch.endpos[0].col) + // take care of an empty match or negative offset + if (pos.col <= matchcol) { + matchcol++; + } else if (pos.col <= regmatch.endpos[0].col) { matchcol = pos.col; - else - /* Be careful not to jump over the NUL at the end-of-line */ + } else { + // Be careful not to jump over the NUL at the end-of-line for (matchcol = regmatch.endpos[0].col; - line[matchcol] != NUL && matchcol < pos.col; - ++matchcol) - ; + matchcol < line_len && matchcol < pos.col; + matchcol++) { + } + } - /* if the skip pattern includes end-of-line, break here */ - if (line[matchcol] == NUL) + // if the skip pattern includes end-of-line, break here + if (matchcol >= line_len) { break; + } - continue; /* start with first end pattern again */ + continue; // start with first end pattern again } } @@ -4478,12 +4484,10 @@ syn_cmd_region ( if (illegal || not_enough) rest = NULL; - /* - * Must have a "start" and "end" pattern. - */ - if (rest != NULL && (pat_ptrs[ITEM_START] == NULL || - pat_ptrs[ITEM_END] == NULL)) { - not_enough = TRUE; + // Must have a "start" and "end" pattern. + if (rest != NULL && (pat_ptrs[ITEM_START] == NULL + || pat_ptrs[ITEM_END] == NULL)) { + not_enough = true; rest = NULL; } @@ -5006,6 +5010,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) curwin->w_s->b_syn_sync_maxlines = 0; } } else if (STRCMP(key, "LINECONT") == 0) { + if (*next_arg == NUL) { // missing pattern + illegal = true; + break; + } if (curwin->w_s->b_syn_linecont_pat != NULL) { EMSG(_("E403: syntax sync: line continuations pattern specified twice")); finished = TRUE; @@ -5533,25 +5541,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); @@ -6159,12 +6171,11 @@ do_highlight ( break; } - /* - * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or - * "guibg"). - */ - while (*linep && !ascii_iswhite(*linep) && *linep != '=') - ++linep; + // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", + // "guibg" or "guisp"). + while (*linep && !ascii_iswhite(*linep) && *linep != '=') { + linep++; + } xfree(key); key = vim_strnsave_up(key_start, (int)(linep - key_start)); linep = skipwhite(linep); @@ -6360,18 +6371,14 @@ do_highlight ( } else HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; } - color &= 7; /* truncate to 8 colors */ - } else if (t_colors == 16 || t_colors == 88 || t_colors == 256) { - switch (t_colors) { - case 16: - color = color_numbers_8[i]; - break; - case 88: - color = color_numbers_88[i]; - break; - case 256: - color = color_numbers_256[i]; - break; + color &= 7; // truncate to 8 colors + } else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) { + if (t_colors == 88) { + color = color_numbers_88[i]; + } else if (t_colors >= 256) { + color = color_numbers_256[i]; + } else { + color = color_numbers_8[i]; } } } @@ -6391,7 +6398,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) @@ -6446,7 +6453,23 @@ do_highlight ( normal_bg = HL_TABLE()[idx].sg_rgb_bg; } } else if (STRCMP(key, "GUISP") == 0) { - // Ignored for now + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) + HL_TABLE()[idx].sg_set |= SG_GUI; + + xfree(HL_TABLE()[idx].sg_rgb_sp_name); + if (STRCMP(arg, "NONE") != 0) { + HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); + } else { + HL_TABLE()[idx].sg_rgb_sp_name = NULL; + HL_TABLE()[idx].sg_rgb_sp = -1; + } + } + + if (is_normal_group) { + normal_sp = HL_TABLE()[idx].sg_rgb_sp; + } } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) { // Ignored for now } else { @@ -6510,6 +6533,7 @@ void restore_cterm_colors(void) { normal_fg = -1; normal_bg = -1; + normal_sp = -1; cterm_normal_fg_color = 0; cterm_normal_fg_bold = 0; cterm_normal_bg_color = 0; @@ -6526,6 +6550,7 @@ static int hl_has_settings(int idx, int check_link) || HL_TABLE()[idx].sg_cterm_bg != 0 || HL_TABLE()[idx].sg_rgb_fg_name != NULL || HL_TABLE()[idx].sg_rgb_bg_name != NULL + || HL_TABLE()[idx].sg_rgb_sp_name != NULL || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)); } @@ -6542,14 +6567,18 @@ static void highlight_clear(int idx) HL_TABLE()[idx].sg_gui = 0; HL_TABLE()[idx].sg_rgb_fg = -1; HL_TABLE()[idx].sg_rgb_bg = -1; + HL_TABLE()[idx].sg_rgb_sp = -1; xfree(HL_TABLE()[idx].sg_rgb_fg_name); HL_TABLE()[idx].sg_rgb_fg_name = NULL; xfree(HL_TABLE()[idx].sg_rgb_bg_name); HL_TABLE()[idx].sg_rgb_bg_name = NULL; - /* Clear the script ID only when there is no link, since that is not - * cleared. */ - if (HL_TABLE()[idx].sg_link == 0) + xfree(HL_TABLE()[idx].sg_rgb_sp_name); + HL_TABLE()[idx].sg_rgb_sp_name = NULL; + // Clear the script ID only when there is no link, since that is not + // cleared. + if (HL_TABLE()[idx].sg_link == 0) { HL_TABLE()[idx].sg_scriptID = 0; + } } @@ -6591,7 +6620,8 @@ int get_attr_entry(attrentry_T *aep) && aep->cterm_bg_color == taep->cterm_bg_color && aep->rgb_ae_attr == taep->rgb_ae_attr && aep->rgb_fg_color == taep->rgb_fg_color - && aep->rgb_bg_color == taep->rgb_bg_color) { + && aep->rgb_bg_color == taep->rgb_bg_color + && aep->rgb_sp_color == taep->rgb_sp_color) { return i + ATTR_OFF; } } @@ -6629,6 +6659,7 @@ int get_attr_entry(attrentry_T *aep) taep->rgb_ae_attr = aep->rgb_ae_attr; taep->rgb_fg_color = aep->rgb_fg_color; taep->rgb_bg_color = aep->rgb_bg_color; + taep->rgb_sp_color = aep->rgb_sp_color; return table->ga_len - 1 + ATTR_OFF; } @@ -6690,6 +6721,10 @@ int hl_combine_attr(int char_attr, int prim_attr) if (spell_aep->rgb_bg_color >= 0) { new_en.rgb_bg_color = spell_aep->rgb_bg_color; } + + if (spell_aep->rgb_sp_color >= 0) { + new_en.rgb_sp_color = spell_aep->rgb_sp_color; + } } return get_attr_entry(&new_en); } @@ -6727,7 +6762,7 @@ static void highlight_list_one(int id) didh = highlight_list_arg(id, didh, LIST_STRING, 0, sgp->sg_rgb_bg_name, "guibg"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, NULL, "guisp"); + 0, sgp->sg_rgb_sp_name, "guisp"); if (sgp->sg_link && !got_int) { (void)syn_list_header(didh, 9999, id); @@ -6841,8 +6876,9 @@ highlight_color ( if (modec == 'g') { if (fg) return HL_TABLE()[id - 1].sg_rgb_fg_name; - if (sp) - return NULL; + if (sp) { + return HL_TABLE()[id - 1].sg_rgb_sp_name; + } return HL_TABLE()[id - 1].sg_rgb_bg_name; } if (font || sp) @@ -6929,7 +6965,14 @@ set_hl_attr ( // before setting attr_entry->{f,g}g_color to a other than -1 at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - sgp->sg_attr = get_attr_entry(&at_en); + at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; + + if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0 + || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1 + || at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0 + || at_en.rgb_ae_attr != 0) { + sgp->sg_attr = get_attr_entry(&at_en); + } } /* @@ -7260,6 +7303,10 @@ int highlight_changed(void) hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; } + if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { + hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; + } + highlight_ga.ga_len = hlcnt + i + 1; set_hl_attr(hlcnt + i); /* At long last we can apply */ highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index 67cf672ef2..8d207e6286 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -69,8 +69,8 @@ struct syn_state { // Structure shared between syntax.c, screen.c typedef struct attr_entry { - short rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. - RgbValue rgb_fg_color, rgb_bg_color; + int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. + RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; int cterm_fg_color, cterm_bg_color; } attrentry_T; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 8fcb02c3b6..7885d467d8 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -526,19 +526,17 @@ do_tag ( taglen_advance(taglen); MSG_PUTS_ATTR(_("file\n"), hl_attr(HLF_T)); - for (i = 0; i < num_matches && !got_int; ++i) { + for (i = 0; i < num_matches && !got_int; i++) { parse_match(matches[i], &tagp); - if (!new_tag && ( - (g_do_tagpreview != 0 - && i == ptag_entry.cur_match) || - (use_tagstack - && i == tagstack[tagstackidx].cur_match))) + if (!new_tag && ((g_do_tagpreview != 0 && i == ptag_entry.cur_match) + || (use_tagstack + && i == tagstack[tagstackidx].cur_match))) { *IObuff = '>'; - else + } else { *IObuff = ' '; - vim_snprintf((char *)IObuff + 1, IOSIZE - 1, - "%2d %s ", i + 1, - mt_names[matches[i][0] & MT_MASK]); + } + vim_snprintf((char *)IObuff + 1, IOSIZE - 1, "%2d %s ", i + 1, + mt_names[matches[i][0] & MT_MASK]); msg_puts(IObuff); if (tagp.tagkind != NULL) msg_outtrans_len(tagp.tagkind, @@ -872,7 +870,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 +1145,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; @@ -1210,20 +1224,15 @@ find_tags ( for (round = 1; round <= 2; ++round) { linear = (orgpat.headlen == 0 || !p_tbs || round == 2); - /* - * Try tag file names from tags option one by one. - */ - for (first_file = TRUE; - use_cscope || - get_tagfname(&tn, first_file, tag_fname) == OK; - first_file = FALSE) { - /* - * A file that doesn't exist is silently ignored. Only when not a - * single file is found, an error message is given (further on). - */ - if (use_cscope) - fp = NULL; /* avoid GCC warning */ - else { + // Try tag file names from tags option one by one. + for (first_file = true; + use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK; + first_file = false) { + // A file that doesn't exist is silently ignored. Only when not a + // single file is found, an error message is given (further on). + if (use_cscope) { + fp = NULL; // avoid GCC warning + } else { if (curbuf->b_help) { /* Prefer help tags according to 'helplang'. Put the * two-letter language name in help_lang[]. */ @@ -1955,6 +1964,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..104cc47cda 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -175,7 +175,16 @@ void terminal_init(void) for (int color_index = 0; color_index < 256; color_index++) { VTermColor color; - vterm_state_get_palette_color(state, color_index, &color); + // Some of the default 16 colors has the same color as the later + // 240 colors. To avoid collisions, we will use the custom colors + // below in non true color mode. + if (color_index < 16) { + color.red = 0; + color.green = 0; + color.blue = (uint8_t)(color_index + 1); + } else { + vterm_state_get_palette_color(state, color_index, &color); + } map_put(int, int)(color_indexes, RGB(color.red, color.green, color.blue), color_index + 1); } @@ -248,6 +257,15 @@ Terminal *terminal_open(TerminalOptions opts) rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); if (!true_color) { + // Change the first 16 colors so we can easily get the correct color + // index from them. + for (int i = 0; i < 16; i++) { + VTermColor color; + color.red = 0; + color.green = 0; + color.blue = (uint8_t)(i + 1); + vterm_state_set_palette_color(state, i, &color); + } return rv; } @@ -411,6 +429,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: @@ -544,9 +566,9 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, // Since libvterm does not expose the color index used by the program, we // use the rgb value to find the appropriate index in the cache computed by // `terminal_init`. - int vt_fg_idx = vt_fg != default_vt_fg ? + int vt_fg_idx = vt_fg != -1 ? map_get(int, int)(color_indexes, vt_fg) : 0; - int vt_bg_idx = vt_bg != default_vt_bg ? + int vt_bg_idx = vt_bg != -1 ? map_get(int, int)(color_indexes, vt_bg) : 0; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) @@ -623,6 +645,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 d16e28a377..90542a6a6c 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -28,17 +28,21 @@ SCRIPTS := \ test53.out \ test55.out \ test64.out \ - test68.out \ test69.out \ test73.out \ test79.out \ - test88.out \ test_listlbr.out \ test_close_count.out \ test_marks.out \ - test_match_conceal.out \ -NEW_TESTS = +# Tests using runtest.vim.vim. +# Keep test_alot*.res as the last one, sort the others. +NEW_TESTS = \ + test_cursor_func.res \ + test_help_tagjump.res \ + test_menu.res \ + 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..6601dcf52f 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,14 +49,20 @@ 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. +set nomore redir @q function /^Test_ redir END diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in index 7fef87d383..a8c81b832d 100644 --- a/src/nvim/testdir/test17.in +++ b/src/nvim/testdir/test17.in @@ -41,17 +41,17 @@ STARTTEST :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :e! Xdir1/dir2/foo.a -i#include "bar.a" +i#include "bar.a": :w :e Xdir1/dir2/bar.a -i#include "baz.a" +i#include "baz.a": :w :e Xdir1/dir2/baz.a -i#include "foo.a" +i#include "foo.a": :w :e Xbase.a :set path=Xdir1/dir2 -i#include <foo.a> +i#include <foo.a>: :w :redir! >>test.out :checkpath! @@ -71,17 +71,17 @@ STARTTEST :endfunction :let &includeexpr='DotsToSlashes()' :e! Xdir1/dir2/foo.b -i%inc /bar/ +i%inc /bar/: :w :e Xdir1/dir2/bar.b -i%inc /baz/ +i%inc /baz/: :w :e Xdir1/dir2/baz.b -i%inc /foo/ +i%inc /foo/: :w :e Xbase.b :set path=Xdir1/dir2 -i%inc /foo/ +i%inc /foo/: :w :redir! >>test.out :checkpath! @@ -104,20 +104,20 @@ STARTTEST :endfunction :let &includeexpr='StripNewlineChar()' :e! Xdir1/dir2/foo.c -i%inc bar.c +i%inc bar.c: :w :e Xdir1/dir2/bar.c -i%inc baz.c +i%inc baz.c: :w :e Xdir1/dir2/baz.c -i%inc foo.c +i%inc foo.c: :w :e Xdir1/dir2/FALSE.c -i%inc foo.c +i%inc foo.c: :w :e Xbase.c :set path=Xdir1/dir2 -i%inc FALSE.c foo.c +i%inc FALSE.c foo.c: :w :redir! >>test.out :checkpath! diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in index 1ce57246ee..d95052e14c 100644 --- a/src/nvim/testdir/test49.in +++ b/src/nvim/testdir/test49.in @@ -8,7 +8,9 @@ STARTTEST :se nomore :lang mess C :so test49.vim -GGGGGGGGGGGGGG"rp:.-,$w! test.out +:" Go back to this file and append the results from register r. +:buf test49.in +G"rp:/^Results/,$w! test.out :" :" make valgrind happy :redir => funclist diff --git a/src/nvim/testdir/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/test68.in b/src/nvim/testdir/test68.in deleted file mode 100644 index ca54e942b5..0000000000 --- a/src/nvim/testdir/test68.in +++ /dev/null @@ -1,131 +0,0 @@ -Test for text formatting. - -Results of test68: - -STARTTEST -:so small.vim -/^{/+1 -:set noai tw=2 fo=t -gRa b -ENDTEST - -{ - - -} - -STARTTEST -/^{/+1 -:set ai tw=2 fo=tw -gqgqjjllab -ENDTEST - -{ -a b - -a -} - -STARTTEST -/^{/+1 -:set tw=3 fo=t -gqgqo -a -ENDTEST - -{ -a -} - -STARTTEST -/^{/+1 -:set tw=2 fo=tcq1 comments=:# -gqgqjgqgqo -a b -#a b -ENDTEST - -{ -a b -#a b -} - -STARTTEST -/^{/+1 -:set tw=5 fo=tcn comments=:# -A bjA b -ENDTEST - -{ - 1 a -# 1 a -} - -STARTTEST -/^{/+3 -:set tw=5 fo=t2a si -i A_ -ENDTEST - -{ - - x a - b - c - -} - -STARTTEST -/^{/+1 -:set tw=5 fo=qn comments=:# -gwap -ENDTEST - -{ -# 1 a b -} - -STARTTEST -/^{/+1 -:set tw=5 fo=q2 comments=:# -gwap -ENDTEST - -{ -# x -# a b -} - -STARTTEST -/^{/+2 -:set tw& fo=a -I^^ -ENDTEST - -{ - 1aa - 2bb -} - -STARTTEST -/mno pqr/ -:setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/ -A vwx yz -ENDTEST - -/* abc def ghi jkl - * mno pqr stu - */ - -STARTTEST -/^#/ -:setl tw=12 fo=tqnc comments=:# -A foobar -ENDTEST - -# 1 xxxxx - -STARTTEST -:g/^STARTTEST/.,/^ENDTEST/d -:1;/^Results/,$wq! test.out -ENDTEST diff --git a/src/nvim/testdir/test68.ok b/src/nvim/testdir/test68.ok deleted file mode 100644 index b3726a0a27..0000000000 --- a/src/nvim/testdir/test68.ok +++ /dev/null @@ -1,77 +0,0 @@ -Results of test68: - - -{ -a -b -} - - -{ -a -b - -a -b -} - - -{ -a - - -a - -} - - -{ -a b -#a b - -a b -#a b -} - - -{ - 1 a - b -# 1 a -# b -} - - -{ - - x a - b_ - c - -} - - -{ -# 1 a -# b -} - - -{ -# x a -# b -} - - -{ 1aa ^^2bb } - - -/* abc def ghi jkl - * mno pqr stu - * vwx yz - */ - - -# 1 xxxxx -# foobar - 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_alot.vim b/src/nvim/testdir/test_alot.vim new file mode 100644 index 0000000000..1d1da94bac --- /dev/null +++ b/src/nvim/testdir/test_alot.vim @@ -0,0 +1,3 @@ +" A series of tests that can run in one Vim invocation. +" This makes testing go faster, since Vim doesn't need to restart. + diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim new file mode 100644 index 0000000000..d819b7b092 --- /dev/null +++ b/src/nvim/testdir/test_cursor_func.vim @@ -0,0 +1,52 @@ +" Tests for cursor(). + +func Test_wrong_arguments() + try + call cursor(1. 3) + " not reached + call assert_false(1) + catch + call assert_exception('E474:') + endtry +endfunc + +func Test_move_cursor() + new + call setline(1, ['aaa', 'bbb', 'ccc', 'ddd']) + + call cursor([1, 1, 0, 1]) + call assert_equal([1, 1, 0, 1], getcurpos()[1:]) + call cursor([4, 3, 0, 3]) + call assert_equal([4, 3, 0, 3], getcurpos()[1:]) + + call cursor(2, 2) + call assert_equal([2, 2, 0, 2], getcurpos()[1:]) + " line number zero keeps the line number + call cursor(0, 1) + call assert_equal([2, 1, 0, 1], getcurpos()[1:]) + " col number zero keeps the column + call cursor(3, 0) + call assert_equal([3, 1, 0, 1], getcurpos()[1:]) + " below last line goes to last line + call cursor(9, 1) + call assert_equal([4, 1, 0, 1], getcurpos()[1:]) + + quit! +endfunc + +" Very short version of what matchparen does. +function s:Highlight_Matching_Pair() + let save_cursor = getcurpos() + call setpos('.', save_cursor) +endfunc + +func Test_curswant_with_autocommand() + new + call setline(1, ['func()', '{', '}', '----']) + autocmd! CursorMovedI * call s:Highlight_Matching_Pair() + exe "normal! 3Ga\<Down>X\<Esc>" + call assert_equal('-X---', getline(4)) + autocmd! CursorMovedI * + quit! +endfunc + diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim new file mode 100644 index 0000000000..9f9207d27d --- /dev/null +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -0,0 +1,40 @@ +" Tests for :help! {subject} + +func SetUp() + " v:progpath is …/build/bin/nvim and we need …/build/runtime + " to be added to &rtp + let builddir = fnamemodify(exepath(v:progpath), ':h:h') + let s:rtp = &rtp + let &rtp .= printf(',%s/runtime', builddir) +endfunc + +func TearDown() + let &rtp = s:rtp +endfunc + +func Test_help_tagjump() + help + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*help.txt\*') + helpclose + + exec "help! ('textwidth'" + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ "\\*'textwidth'\\*") + helpclose + + exec "help! ('buflisted')," + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ "\\*'buflisted'\\*") + helpclose + + exec "help! abs({expr})" + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*abs()\*') + helpclose + + exec "help! arglistid([{winnr})" + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*arglistid()\*') + helpclose +endfunc 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_menu.vim b/src/nvim/testdir/test_menu.vim new file mode 100644 index 0000000000..be559467c8 --- /dev/null +++ b/src/nvim/testdir/test_menu.vim @@ -0,0 +1,9 @@ +" Test that the system menu can be loaded. + +func Test_load_menu() + try + source $VIMRUNTIME/menu.vim + catch + call assert_false(1, 'error while loading menus: ' . v:exception) + endtry +endfunc 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/tui/input.c b/src/nvim/tui/input.c index b41e4d2fba..99eb230a88 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -224,9 +224,9 @@ static void tk_getkeys(TermInput *input, bool force) while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) { if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { forward_simple_utf8(input, &key); - } else if (key.type == TERMKEY_TYPE_UNICODE || - key.type == TERMKEY_TYPE_FUNCTION || - key.type == TERMKEY_TYPE_KEYSYM) { + } else if (key.type == TERMKEY_TYPE_UNICODE + || key.type == TERMKEY_TYPE_FUNCTION + || key.type == TERMKEY_TYPE_KEYSYM) { forward_modified_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_MOUSE) { forward_mouse_event(input, &key); @@ -282,9 +282,9 @@ static bool handle_focus_event(TermInput *input) static bool handle_bracketed_paste(TermInput *input) { - if (rbuffer_size(input->read_stream.buffer) > 5 && - (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) || - !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { + if (rbuffer_size(input->read_stream.buffer) > 5 + && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) + || !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; // Advance past the sequence rbuffer_consumed(input->read_stream.buffer, 6); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 00e2821075..62bc81ba64 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -81,7 +81,7 @@ UI *tui_start(void) { UI *ui = xcalloc(1, sizeof(UI)); ui->stop = tui_stop; - ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL; + ui->rgb = p_tgc; ui->resize = tui_resize; ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; @@ -100,6 +100,7 @@ UI *tui_start(void) ui->visual_bell = tui_visual_bell; ui->update_fg = tui_update_fg; ui->update_bg = tui_update_bg; + ui->update_sp = tui_update_sp; ui->flush = tui_flush; ui->suspend = tui_suspend; ui->set_title = tui_set_title; @@ -573,6 +574,11 @@ static void tui_update_bg(UI *ui, int bg) ((TUIData *)ui->data)->grid.bg = bg; } +static void tui_update_sp(UI *ui, int sp) +{ + // Do nothing; 'special' color is for GUI only +} + static void tui_flush(UI *ui) { TUIData *data = ui->data; @@ -628,8 +634,8 @@ static void tui_suspend(UI *ui) static void tui_set_title(UI *ui, char *title) { TUIData *data = ui->data; - if (!(title && unibi_get_str(data->ut, unibi_to_status_line) && - unibi_get_str(data->ut, unibi_from_status_line))) { + if (!(title && unibi_get_str(data->ut, unibi_to_status_line) + && unibi_get_str(data->ut, unibi_from_status_line))) { return; } unibi_out(ui, unibi_to_status_line); @@ -694,8 +700,8 @@ static void update_size(UI *ui) } // 2 - try from a system call(ioctl/TIOCGWINSZ on unix) - if (data->out_isatty && - !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) { + if (data->out_isatty + && !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) { goto end; } diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index df51e1fced..ad6d96a168 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -21,7 +21,7 @@ struct ugrid { UCell **cells; }; -#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1}) +#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 }) #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ do { \ diff --git a/src/nvim/ui.c b/src/nvim/ui.c index d32969f149..05322a6f64 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -155,6 +155,7 @@ void ui_resize(int new_width, int new_height) UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1)); UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1)); + UI_CALL(update_sp, (ui->rgb ? normal_sp : -1)); sr.top = 0; sr.bot = height - 1; @@ -388,7 +389,7 @@ static void parse_control_character(uint8_t c) static void set_highlight_args(int attr_code) { - HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 }; + HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 }; HlAttrs cterm_attrs = rgb_attrs; if (attr_code == HL_NORMAL) { @@ -425,6 +426,10 @@ static void set_highlight_args(int attr_code) rgb_attrs.background = aep->rgb_bg_color; } + if (aep->rgb_sp_color != normal_sp) { + rgb_attrs.special = aep->rgb_sp_color; + } + if (cterm_normal_fg_color != aep->cterm_fg_color) { cterm_attrs.foreground = aep->cterm_fg_color - 1; } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 4c051fcfbf..5934d2fee9 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -7,7 +7,7 @@ typedef struct { bool bold, underline, undercurl, italic, reverse; - int foreground, background; + int foreground, background, special; } HlAttrs; typedef struct ui_t UI; @@ -35,6 +35,7 @@ struct ui_t { void (*flush)(UI *ui); void (*update_fg)(UI *ui, int fg); void (*update_bg)(UI *ui, int bg); + void (*update_sp)(UI *ui, int sp); void (*suspend)(UI *ui); void (*set_title)(UI *ui, char *title); void (*set_icon)(UI *ui, char *icon); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 359fffe3bf..fd9d4671e3 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -49,6 +49,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.visual_bell = ui_bridge_visual_bell; rv->bridge.update_fg = ui_bridge_update_fg; rv->bridge.update_bg = ui_bridge_update_bg; + rv->bridge.update_sp = ui_bridge_update_sp; rv->bridge.flush = ui_bridge_flush; rv->bridge.suspend = ui_bridge_suspend; rv->bridge.set_title = ui_bridge_set_title; @@ -305,6 +306,16 @@ static void ui_bridge_update_bg_event(void **argv) ui->update_bg(ui, PTR2INT(argv[1])); } +static void ui_bridge_update_sp(UI *b, int sp) +{ + UI_CALL(b, update_sp, 2, b, INT2PTR(sp)); +} +static void ui_bridge_update_sp_event(void **argv) +{ + UI *ui = UI(argv[0]); + ui->update_sp(ui, PTR2INT(argv[1])); +} + static void ui_bridge_flush(UI *b) { UI_CALL(b, flush, 1, b); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 4a8a24d79d..f16b4264d7 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 @@ -2870,9 +2876,7 @@ static char_u *u_save_line(linenr_T lnum) /// @return true if the buffer has changed bool bufIsChanged(buf_T *buf) { - return - !bt_dontwrite(buf) && - (buf->b_changed || file_ff_differs(buf, true)); + return !bt_dontwrite(buf) && (buf->b_changed || file_ff_differs(buf, true)); } /// Check if the 'modified' flag is set, or 'ff' has changed (only need to @@ -2882,9 +2886,8 @@ bool bufIsChanged(buf_T *buf) /// @return true if the current buffer has changed bool curbufIsChanged(void) { - return - !bt_dontwrite(curbuf) && - (curbuf->b_changed || file_ff_differs(curbuf, true)); + return (!bt_dontwrite(curbuf) + && (curbuf->b_changed || file_ff_differs(curbuf, true))); } /* diff --git a/src/nvim/version.c b/src/nvim/version.c index 80b1b236dd..be66ba14fa 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,8 +69,402 @@ static char *features[] = { // clang-format off static int included_patches[] = { - 1366, + 1832, + 1809, + 1808, + 1806, + 1799, + 1757, + 1755, + 1753, + 1728, + 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 NA + // 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 NA + // 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 +496,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, @@ -146,17 +540,17 @@ static int included_patches[] = { // 1147, // 1146 NA // 1145 NA - // 1144 NA - // 1143, + 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,26 +564,26 @@ static int included_patches[] = { // 1123, // 1122 NA // 1121, - // 1120, + 1120, // 1119, - // 1118, - // 1117, - // 1116, + 1118, + 1117, + 1116, // 1115 NA - // 1114, - // 1113, - // 1112, + 1114, + 1113, + 1112, // 1111, - // 1110, + 1110, // 1109 NA // 1108, - // 1107, + 1107, // 1106 NA - // 1105, + 1105, // 1104 NA // 1103 NA - // 1102, - // 1101, + 1102, + 1101, // 1100 NA // 1099 NA // 1098 NA @@ -197,107 +591,107 @@ static int included_patches[] = { // 1096, // 1095 NA // 1094, - // 1093, - // 1092, - // 1091, + 1093, + 1092, + 1091, // 1090, 1089, 1088, 1087, - // 1086, + 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, + 1075, // 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, + 1057, // 1056, 1055, - // 1054, + 1054, // 1053, - // 1052, + 1052, // 1051, // 1050, - // 1049, - // 1048, - // 1047, - // 1046, - // 1045 NA, - // 1044 NA, - // 1043 NA, - // 1042, + 1049, + 1048, + 1047, + 1046, + // 1045 NA + // 1044 NA + // 1043 NA + 1042, 1041, - // 1040 NA, + // 1040 NA // 1039, - // 1038 NA, - // 1037, - // 1036, - // 1035, + // 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, - // 1015, - // 1014 NA, + // 1016 NA + 1015, + // 1014 NA 1013, - // 1012 NA, - // 1011 NA, - // 1010, - // 1009 NA, - // 1008 NA, - // 1007, - // 1006, - // 1005, + // 1012 NA + // 1011 NA + // 1010 NA, + // 1009 NA + // 1008 NA + 1007, + 1006, + // 1005 NA, // 1004 NA, // 1003 NA, // 1002 NA, - // 1001, - // 1000, + 1001, + 1000, // 999 NA - // 998, + 998, // 997 NA // 996 NA // 995 NA // 994 NA - // 993, + // 993 NA // 992 NA 991, // 990 NA @@ -306,8 +700,8 @@ static int included_patches[] = { // 987 NA // 986 NA // 985 NA - // 984, - // 983, + 984, + // 983 NA // 982 NA 981, 980, @@ -316,12 +710,12 @@ static int included_patches[] = { 977, // 976 NA 975, - // 974, - // 973, + 974, + 973, 972, // 971 NA // 970 NA - // 969, + // 969 NA // 968 NA // 967 NA // 966 NA @@ -329,27 +723,27 @@ static int included_patches[] = { // 964 NA 963, // 962 NA - // 961, + 961, // 960 NA // 959 NA - // 958, - // 957, - // 956, + 958, + 957, + // 956 NA 955, // 954 NA 953, - // 952, - // 951, + 952, + 951, 950, 949, // 948 NA - // 947, + // 947 NA 946, 945, 944, - // 943, - // 942, - // 941, + // 943 NA + 942, + 941, // 940 NA 939, // 938 NA @@ -365,7 +759,7 @@ static int included_patches[] = { // 928 NA // 927 NA 926, - // 925, + 925, // 924 NA // 923 NA 922, @@ -376,17 +770,17 @@ static int included_patches[] = { // 917 NA 916, 915, - // 914, + // 914 NA // 913 NA 912, // 911 NA // 910 NA - // 909, + // 909 NA // 908 NA // 907 NA // 906 NA - // 905, - // 904, + // 905 NA + // 904 NA 903, // 902 NA 901, @@ -394,21 +788,21 @@ static int included_patches[] = { // 899 NA 898, // 897 NA - // 896, + 896, 895, // 894 NA 893, - // 892, + // 892 NA 891, // 890 NA - // 889, + 889, 888, 887, // 886 NA 885, // 884 NA 883, - // 882, + 882, 881, // 880 NA 879, @@ -419,7 +813,7 @@ static int included_patches[] = { // 874 NA // 873 NA // 872 NA - // 871, + 871, 870, // 869 NA 868, @@ -433,7 +827,7 @@ static int included_patches[] = { // 860 NA 859, 858, - // 857, + 857, 856, // 855 NA // 854 NA @@ -468,10 +862,10 @@ static int included_patches[] = { 825, // 824 NA 823, - // 822, + 822, // 821 NA 820, - // 819, + 819, 818, 817, 816, @@ -493,7 +887,7 @@ static int included_patches[] = { 800, 799, 798, - // 797, + // 797 NA // 796 NA 795, // 794 NA @@ -557,7 +951,7 @@ static int included_patches[] = { 736, // 735 NA 734, - // 733, + // 733 NA 732, // 731 NA // 730 NA @@ -618,7 +1012,7 @@ static int included_patches[] = { 675, // 674 NA 673, - // 672, + 672, 671, 670, // 669 NA @@ -677,7 +1071,7 @@ static int included_patches[] = { 616, 615, 614, - // 613, + 613, 612, // 611 NA // 610 NA @@ -1046,13 +1440,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 762d349470..165a44a148 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -27,12 +27,6 @@ Error: configure did not run properly.Check auto/config.log. # endif #endif - -/* Can't use "PACKAGE" here, conflicts with a Perl include file. */ -#ifndef VIMPACKAGE -# define VIMPACKAGE "nvim" -#endif - #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) @@ -207,15 +201,6 @@ enum { #define MAYBE 2 /* sometimes used for a variant on TRUE */ -/* - * Motion types, used for operators and for yank/delete registers. - */ -#define MCHAR 0 /* character-wise movement/register */ -#define MLINE 1 /* line-wise movement/register */ -#define MBLOCK 2 /* block-wise register */ - -#define MAUTO 0xff /* Decide between MLINE/MCHAR */ - #define STATUS_HEIGHT 1 /* height of a status line under a window */ #define QF_WINHEIGHT 10 /* default height for quickfix window */ @@ -277,26 +262,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 39106a7b8d..bea55c465f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -97,7 +97,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, 0); + (void)win_split((int)Prenum, 0); break; /* split current window in two parts, vertically */ @@ -108,7 +108,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, WSP_VERT); + (void)win_split((int)Prenum, WSP_VERT); break; /* split current window and edit alternate file */ @@ -248,7 +248,7 @@ newwindow: /* First create a new tab with the window, then go back to * the old tab and close the window there. */ wp = curwin; - if (win_new_tabpage((int)Prenum) == OK + if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; goto_tabpage_tp(oldtab, TRUE, TRUE); @@ -2948,18 +2948,19 @@ void free_tabpage(tabpage_T *tp) unref_var_dict(tp->tp_vars); - + xfree(tp->localdir); // Free tab-local working directory xfree(tp); } -/* - * Create a new Tab page with one window. - * It will edit the current buffer, like after ":split". - * When "after" is 0 put it just after the current Tab page. - * Otherwise put it just before tab page "after". - * Return FAIL or OK. - */ -int win_new_tabpage(int after) +/// Create a new tabpage with one window. +/// +/// It will edit the current buffer, like after :split. +/// +/// @param after Put new tabpage after tabpage "after", or after the current +/// tabpage in case of 0. +/// @param filename Will be passed to apply_autocmds(). +/// @return Was the new tabpage created successfully? FAIL or OK. +int win_new_tabpage(int after, char_u *filename) { tabpage_T *tp = curtab; tabpage_T *newtp; @@ -2999,10 +3000,12 @@ int win_new_tabpage(int after) newtp->tp_topframe = topframe; last_status(FALSE); - redraw_all_later(CLEAR); - apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); + + apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); + return OK; } @@ -3023,7 +3026,7 @@ int may_open_tabpage(void) if (n != 0) { cmdmod.tab = 0; /* reset it to avoid doing it twice */ postponed_split_tab = 0; - return win_new_tabpage(n); + return win_new_tabpage(n, NULL); } return FAIL; } @@ -3047,9 +3050,11 @@ int make_tabpages(int maxcount) */ block_autocmds(); - for (todo = count - 1; todo > 0; --todo) - if (win_new_tabpage(0) == FAIL) + for (todo = count - 1; todo > 0; --todo) { + if (win_new_tabpage(0, NULL) == FAIL) { break; + } + } unblock_autocmds(); @@ -3555,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. */ @@ -4619,10 +4630,8 @@ void win_drag_vsep_line(win_T *dragwin, int offset) #define FRACTION_MULT 16384L -/* - * Set wp->w_fraction for the current w_wrow and w_height. - */ -static void set_fraction(win_T *wp) +// Set wp->w_fraction for the current w_wrow and w_height. +void set_fraction(win_T *wp) { wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2) / (long)wp->w_height; |