aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt34
-rw-r--r--src/nvim/api/buffer.c29
-rw-r--r--src/nvim/api/private/defs.h1
-rw-r--r--src/nvim/api/private/helpers.c36
-rw-r--r--src/nvim/api/tabpage.c29
-rw-r--r--src/nvim/api/vim.c24
-rw-r--r--src/nvim/api/window.c29
-rw-r--r--src/nvim/assert.h101
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/buffer_defs.h56
-rw-r--r--src/nvim/diff.c66
-rw-r--r--src/nvim/edit.c38
-rw-r--r--src/nvim/edit.h6
-rw-r--r--src/nvim/eval.c2372
-rw-r--r--src/nvim/eval.h45
-rw-r--r--src/nvim/eval/decode.c1116
-rw-r--r--src/nvim/eval/decode.h13
-rw-r--r--src/nvim/eval/encode.c1296
-rw-r--r--src/nvim/eval/encode.h75
-rw-r--r--src/nvim/eval_defs.h84
-rw-r--r--src/nvim/ex_cmds.c226
-rw-r--r--src/nvim/ex_cmds.lua12
-rw-r--r--src/nvim/ex_cmds2.c19
-rw-r--r--src/nvim/ex_docmd.c139
-rw-r--r--src/nvim/ex_docmd.h12
-rw-r--r--src/nvim/ex_eval.c38
-rw-r--r--src/nvim/fileio.c208
-rw-r--r--src/nvim/fold.c29
-rw-r--r--src/nvim/garray.c19
-rw-r--r--src/nvim/globals.h12
-rw-r--r--src/nvim/hardcopy.c9
-rw-r--r--src/nvim/if_cscope.c5
-rw-r--r--src/nvim/indent.c2
-rw-r--r--src/nvim/lib/kvec.h1
-rw-r--r--src/nvim/macros.h11
-rw-r--r--src/nvim/main.c13
-rw-r--r--src/nvim/memline.c3
-rw-r--r--src/nvim/message.c97
-rw-r--r--src/nvim/message.h51
-rw-r--r--src/nvim/misc1.c466
-rw-r--r--src/nvim/misc1.h1
-rw-r--r--src/nvim/misc2.c5
-rw-r--r--src/nvim/mouse.c94
-rw-r--r--src/nvim/mouse.h6
-rw-r--r--src/nvim/msgpack_rpc/server.c4
-rw-r--r--src/nvim/normal.c33
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c76
-rw-r--r--src/nvim/option_defs.h10
-rw-r--r--src/nvim/options.lua7
-rw-r--r--src/nvim/os/fs.c17
-rw-r--r--src/nvim/os_unix.c1
-rw-r--r--src/nvim/path.c86
-rw-r--r--src/nvim/path.h2
-rw-r--r--src/nvim/po/es.po3
-rw-r--r--src/nvim/po/it.po502
-rw-r--r--src/nvim/quickfix.c39
-rw-r--r--src/nvim/search.c56
-rw-r--r--src/nvim/search.h27
-rw-r--r--src/nvim/shada.c130
-rw-r--r--src/nvim/spell.c1
-rw-r--r--src/nvim/tag.c20
-rw-r--r--src/nvim/tempfile.c139
-rw-r--r--src/nvim/tempfile.h8
-rw-r--r--src/nvim/terminal.c1
-rw-r--r--src/nvim/testdir/Makefile7
-rw-r--r--src/nvim/testdir/test17.in26
-rw-r--r--src/nvim/testdir/test49.in4
-rw-r--r--src/nvim/testdir/test_alot.vim3
-rw-r--r--src/nvim/testdir/test_marks.in34
-rw-r--r--src/nvim/testdir/test_marks.ok16
-rw-r--r--src/nvim/testdir/test_viml.vim18
-rw-r--r--src/nvim/version.c581
-rw-r--r--src/nvim/vim.h25
-rw-r--r--src/nvim/window.c26
75 files changed, 5694 insertions, 3139 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 487b554d6d..a473f99278 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -41,22 +41,25 @@ include_directories(${GENERATED_DIR})
include_directories(${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${GENERATED_DIR})
-file(MAKE_DIRECTORY ${GENERATED_DIR}/os)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/api)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/tui)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/event)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/event)
-
-file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c
- tui/*.c event/*.c)
+
+file(GLOB NEOVIM_SOURCES *.c)
+
+foreach(subdir
+ os
+ api
+ api/private
+ msgpack_rpc
+ tui
+ event
+ eval
+ )
+ file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
+ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
+ file(GLOB sources ${subdir}/*.c)
+ list(APPEND NEOVIM_SOURCES ${sources})
+endforeach()
+
file(GLOB_RECURSE NEOVIM_HEADERS *.h)
file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c)
@@ -88,7 +91,6 @@ set(CONV_SOURCES
mbyte.c
memline.c
message.c
- misc1.c
ops.c
regexp.c
screen.c
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index c25a9789c5..55b535c78c 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -423,13 +423,16 @@ Object buffer_get_var(Buffer buffer, String name, Error *err)
return dict_get_value(buf->b_vars, name, err);
}
-/// Sets a buffer-scoped (b:) variable. 'nil' value deletes the variable.
+/// Sets a buffer-scoped (b:) variable
///
/// @param buffer The buffer handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -438,7 +441,27 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(buf->b_vars, name, value, err);
+ return dict_set_value(buf->b_vars, name, value, false, err);
+}
+
+/// Removes a buffer-scoped (b:) variable
+///
+/// @param buffer The buffer handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object buffer_del_var(Buffer buffer, String name, Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(buf->b_vars, name, NIL, true, err);
}
/// Gets a buffer option value
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 6c8e324649..fbfa87d5ae 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -99,4 +99,3 @@ struct key_value_pair {
#endif // NVIM_API_PRIVATE_DEFS_H
-
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 7a0b5191d7..db3e499427 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -90,14 +90,17 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
}
/// Set a value in a dict. Objects are recursively expanded into their
-/// vimscript equivalents. Passing 'nil' as value deletes the key.
+/// vimscript equivalents.
///
/// @param dict The vimscript dict
/// @param key The key
/// @param value The new value
+/// @param del Delete key in place of setting it. Argument `value` is ignored in
+/// this case.
/// @param[out] err Details of an error that may have occurred
/// @return the old value, if any
-Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
+Object dict_set_value(dict_T *dict, String key, Object value, bool del,
+ Error *err)
{
Object rv = OBJECT_INIT;
@@ -118,7 +121,7 @@ Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size);
- if (value.type == kObjectTypeNil) {
+ if (del) {
// Delete the key
if (di == NULL) {
// Doesn't exist, fail
@@ -397,13 +400,13 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
switch (obj.type) {
case kObjectTypeNil:
- tv->v_type = VAR_NUMBER;
- tv->vval.v_number = 0;
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = kSpecialVarNull;
break;
case kObjectTypeBoolean:
- tv->v_type = VAR_NUMBER;
- tv->vval.v_number = obj.data.boolean;
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse;
break;
case kObjectTypeBuffer:
@@ -651,6 +654,21 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
switch (obj->v_type) {
+ case VAR_SPECIAL:
+ switch (obj->vval.v_special) {
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ rv.type = kObjectTypeBoolean;
+ rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue);
+ break;
+ }
+ case kSpecialVarNull: {
+ rv.type = kObjectTypeNil;
+ break;
+ }
+ }
+ break;
+
case VAR_STRING:
rv.type = kObjectTypeString;
rv.data.string = cstr_to_string((char *) obj->vval.v_string);
@@ -730,6 +748,10 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
}
break;
+
+ case VAR_UNKNOWN:
+ case VAR_FUNC:
+ break;
}
return rv;
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 126ee4072d..c8311b0aa0 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -54,13 +54,16 @@ Object tabpage_get_var(Tabpage tabpage, String name, Error *err)
return dict_get_value(tab->tp_vars, name, err);
}
-/// Sets a tab-scoped (t:) variable. 'nil' value deletes the variable.
+/// Sets a tab-scoped (t:) variable
///
/// @param tabpage handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The tab page handle
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
{
tabpage_T *tab = find_tab_by_handle(tabpage, err);
@@ -69,7 +72,27 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(tab->tp_vars, name, value, err);
+ return dict_set_value(tab->tp_vars, name, value, false, err);
+}
+
+/// Removes a tab-scoped (t:) variable
+///
+/// @param tabpage handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
+{
+ tabpage_T *tab = find_tab_by_handle(tabpage, err);
+
+ if (!tab) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(tab->tp_vars, name, NIL, true, err);
}
/// Gets the current window in a tab page
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 1c1822aa32..46ac3c9022 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -297,7 +297,7 @@ void vim_change_directory(String dir, Error *err)
return;
}
- post_chdir(false);
+ post_chdir(kCdScopeGlobal);
try_end(err);
}
@@ -337,15 +337,31 @@ Object vim_get_var(String name, Error *err)
return dict_get_value(&globvardict, name, err);
}
-/// Sets a global variable. Passing 'nil' as value deletes the variable.
+/// Sets a global variable
///
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return the old value if any
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err)
{
- return dict_set_value(&globvardict, name, value, err);
+ return dict_set_value(&globvardict, name, value, false, err);
+}
+
+/// Removes a global variable
+///
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object vim_del_var(String name, Error *err)
+{
+ return dict_set_value(&globvardict, name, NIL, true, err);
}
/// Gets a vim variable
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index aad616c7bf..a52f53a3e6 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -197,13 +197,16 @@ Object window_get_var(Window window, String name, Error *err)
return dict_get_value(win->w_vars, name, err);
}
-/// Sets a window-scoped (w:) variable. 'nil' value deletes the variable.
+/// Sets a window-scoped (w:) variable
///
/// @param window The window handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object window_set_var(Window window, String name, Object value, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -212,7 +215,27 @@ Object window_set_var(Window window, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(win->w_vars, name, value, err);
+ return dict_set_value(win->w_vars, name, value, false, err);
+}
+
+/// Removes a window-scoped (w:) variable
+///
+/// @param window The window handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object window_del_var(Window window, String name, Error *err)
+{
+ win_T *win = find_window_by_handle(window, err);
+
+ if (!win) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(win->w_vars, name, NIL, true, err);
}
/// Gets a window option value
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 3a900aca65..0ce48e4766 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -8,17 +8,32 @@
// defined(__has_feature) && __has_feature(...). Therefore we define Clang's
// __has_feature and __has_extension macro's before referring to them.
#ifndef __has_feature
- #define __has_feature(x) 0
+# define __has_feature(x) 0
#endif
#ifndef __has_extension
- #define __has_extension __has_feature
+# define __has_extension __has_feature
#endif
-/// STATIC_ASSERT(condition, message) - assert at compile time if !cond
+/// @def STATIC_ASSERT
+/// @brief Assert at compile time if condition is not satisfied.
///
-/// example:
-/// STATIC_ASSERT(sizeof(void *) == 8, "need 64-bits mode");
+/// Should be put on its own line, followed by a semicolon.
+///
+/// Example:
+///
+/// STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode");
+///
+/// @param[in] condition Condition to check, should be an integer constant
+/// expression.
+/// @param[in] message Message which will be given if check fails.
+
+/// @def STATIC_ASSERT_EXPR
+/// @brief Like #STATIC_ASSERT, but can be used where expressions are used.
+///
+/// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message
+/// given in this case is not very nice with the current implementation though
+/// and `message` argument is ignored.
// define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is
// detected or the compiler is known to support it. Note that Clang in C99
@@ -29,50 +44,74 @@
// clearer messages we get from _Static_assert, we suppress the warnings
// temporarily.
+#define STATIC_ASSERT_PRAGMA_START
+#define STATIC_ASSERT_PRAGMA_END
+#define STATIC_ASSERT(...) \
+ do { \
+ STATIC_ASSERT_PRAGMA_START \
+ STATIC_ASSERT_STATEMENT(__VA_ARGS__); \
+ STATIC_ASSERT_PRAGMA_END \
+ } while (0)
+
// the easiest case, when the mode is C11 (generic compiler) or Clang
// advertises explicit support for c_static_assert, meaning it won't warn.
#if __STDC_VERSION__ >= 201112L || __has_feature(c_static_assert)
- #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
// if we're dealing with gcc >= 4.6 in C99 mode, we can still use
// _Static_assert but we need to suppress warnings, this is pretty ugly.
#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
- #define STATIC_ASSERT(cond, msg) \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-pedantic\"") \
- _Static_assert(cond, msg); \
- _Pragma("GCC diagnostic pop") \
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
+
+# undef STATIC_ASSERT_PRAGMA_START
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-pedantic\"") \
+
+# undef STATIC_ASSERT_PRAGMA_END
+# define STATIC_ASSERT_PRAGMA_END \
+ _Pragma("GCC diagnostic pop") \
// the same goes for clang in C99 mode, but we suppress a different warning
#elif defined(__clang__) && __has_extension(c_static_assert)
- #define STATIC_ASSERT(cond, msg) \
- _Pragma("clang diagnostic push") \
- _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
- _Static_assert(cond, msg); \
- _Pragma("clang diagnostic pop") \
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
+
+# undef STATIC_ASSERT_PRAGMA_START
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
+
+# undef STATIC_ASSERT_PRAGMA_END
+# define STATIC_ASSERT_PRAGMA_END \
+ _Pragma("clang diagnostic pop") \
// TODO(aktau): verify that this works, don't have MSVC on hand.
#elif _MSC_VER >= 1600
- #define STATIC_ASSERT(cond, msg) static_assert(cond, msg)
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg)
// fallback for compilers that don't support _Static_assert or static_assert
// not as pretty but gets the job done. Credit goes to Pádraig Brady and
// contributors.
#else
- #define ASSERT_CONCAT_(a, b) a##b
- #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
- // These can't be used after statements in c89.
- #ifdef __COUNTER__
- #define STATIC_ASSERT(e,m) \
- { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; }
- #else
- // This can't be used twice on the same line so ensure if using in headers
- // that the headers are not included twice (by wrapping in #ifndef...#endif)
- // Note it doesn't cause an issue when used on same line of separate modules
- // compiled with gcc -combine -fwhole-program.
- #define STATIC_ASSERT(e,m) \
- { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; }
- #endif
+# define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR
+#endif
+
+#define ASSERT_CONCAT_(a, b) a##b
+#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
+// These can't be used after statements in c89.
+#ifdef __COUNTER__
+# define STATIC_ASSERT_EXPR(e, m) \
+ ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }) 0)
+#else
+// This can't be used twice on the same line so ensure if using in headers
+// that the headers are not included twice (by wrapping in #ifndef...#endif)
+// Note it doesn't cause an issue when used on same line of separate modules
+// compiled with gcc -combine -fwhole-program.
+# define STATIC_ASSERT_EXPR(e, m) \
+ ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }) 0)
#endif
#endif // NVIM_ASSERT_H
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c514c4378e..529d0889bd 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1553,6 +1553,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_ep);
clear_string_option(&buf->b_p_path);
clear_string_option(&buf->b_p_tags);
+ clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 86e63eb52c..c0cd801cd4 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -666,19 +666,21 @@ struct file_buffer {
long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
char_u *b_p_keymap; ///< 'keymap'
- /* local values for options which are normally global */
- char_u *b_p_gp; /* 'grepprg' local value */
- char_u *b_p_mp; /* 'makeprg' local value */
- char_u *b_p_efm; /* 'errorformat' local value */
- char_u *b_p_ep; /* 'equalprg' local value */
- char_u *b_p_path; /* 'path' local value */
- int b_p_ar; /* 'autoread' local value */
- char_u *b_p_tags; /* 'tags' local value */
- char_u *b_p_dict; /* 'dictionary' local value */
- char_u *b_p_tsr; /* 'thesaurus' local value */
- long b_p_ul; /* 'undolevels' local value */
- int b_p_udf; /* 'undofile' */
- char_u *b_p_lw; // 'lispwords' local value
+ // local values for options which are normally global
+ char_u *b_p_gp; ///< 'grepprg' local value
+ char_u *b_p_mp; ///< 'makeprg' local value
+ char_u *b_p_efm; ///< 'errorformat' local value
+ char_u *b_p_ep; ///< 'equalprg' local value
+ char_u *b_p_path; ///< 'path' local value
+ int b_p_ar; ///< 'autoread' local value
+ char_u *b_p_tags; ///< 'tags' local value
+ char_u *b_p_tc; ///< 'tagcase' local value
+ unsigned b_tc_flags; ///< flags for 'tagcase'
+ char_u *b_p_dict; ///< 'dictionary' local value
+ char_u *b_p_tsr; ///< 'thesaurus' local value
+ long b_p_ul; ///< 'undolevels' local value
+ int b_p_udf; ///< 'undofile'
+ char_u *b_p_lw; ///< 'lispwords' local value
/* end of buffer options */
@@ -816,10 +818,12 @@ struct tabpage_S {
was set */
diff_T *tp_first_diff;
buf_T *(tp_diffbuf[DB_COUNT]);
- int tp_diff_invalid; /* list of diffs is outdated */
- frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */
- dictitem_T tp_winvar; /* variable for "t:" Dictionary */
- dict_T *tp_vars; /* internal variables, local to tab page */
+ int tp_diff_invalid; ///< list of diffs is outdated */
+ frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots
+ dictitem_T tp_winvar; ///< variable for "t:" Dictionary
+ dict_T *tp_vars; ///< internal variables, local to tab page
+ char_u *localdir; ///< Absolute path of local directory or
+ ///< NULL
};
/*
@@ -953,16 +957,14 @@ struct window_S {
time through cursupdate() to the
current virtual column */
- /*
- * the next six are used to update the visual part
- */
- char w_old_visual_mode; /* last known VIsual_mode */
- linenr_T w_old_cursor_lnum; /* last known end of visual part */
- colnr_T w_old_cursor_fcol; /* first column for block visual part */
- colnr_T w_old_cursor_lcol; /* last column for block visual part */
- linenr_T w_old_visual_lnum; /* last known start of visual part */
- colnr_T w_old_visual_col; /* last known start of visual part */
- colnr_T w_old_curswant; /* last known value of Curswant */
+ // the next seven are used to update the visual part
+ char w_old_visual_mode; ///< last known VIsual_mode
+ linenr_T w_old_cursor_lnum; ///< last known end of visual part
+ colnr_T w_old_cursor_fcol; ///< first column for block visual part
+ colnr_T w_old_cursor_lcol; ///< last column for block visual part
+ linenr_T w_old_visual_lnum; ///< last known start of visual part
+ colnr_T w_old_visual_col; ///< last known start of visual part
+ colnr_T w_old_curswant; ///< last known value of Curswant
/*
* "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 1149ca1e62..4826e70727 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -29,7 +29,6 @@
#include "nvim/path.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
@@ -658,9 +657,9 @@ void ex_diffupdate(exarg_T *eap)
}
// We need three temp file names.
- char_u *tmp_orig = vim_tempname();
- char_u *tmp_new = vim_tempname();
- char_u *tmp_diff = vim_tempname();
+ char *tmp_orig = (char *) vim_tempname();
+ char *tmp_new = (char *) vim_tempname();
+ char *tmp_diff = (char *) vim_tempname();
if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) {
goto theend;
@@ -670,11 +669,11 @@ void ex_diffupdate(exarg_T *eap)
// are no differences. Can't use the return value, it's non-zero when
// there are differences.
// May try twice, first with "-a" and then without.
- int io_error = FALSE;
- int ok = FALSE;
+ int io_error = false;
+ bool ok = false;
for (;;) {
- ok = FALSE;
- FILE *fd = mch_fopen((char *)tmp_orig, "w");
+ ok = false;
+ FILE *fd = mch_fopen(tmp_orig, "w");
if (fd == NULL) {
io_error = TRUE;
@@ -683,7 +682,7 @@ void ex_diffupdate(exarg_T *eap)
io_error = TRUE;
}
fclose(fd);
- fd = mch_fopen((char *)tmp_new, "w");
+ fd = mch_fopen(tmp_new, "w");
if (fd == NULL) {
io_error = TRUE;
@@ -693,7 +692,7 @@ void ex_diffupdate(exarg_T *eap)
}
fclose(fd);
diff_file(tmp_orig, tmp_new, tmp_diff);
- fd = mch_fopen((char *)tmp_diff, "r");
+ fd = mch_fopen(tmp_diff, "r");
if (fd == NULL) {
io_error = TRUE;
@@ -712,10 +711,10 @@ void ex_diffupdate(exarg_T *eap)
}
fclose(fd);
}
- os_remove((char *)tmp_diff);
- os_remove((char *)tmp_new);
+ os_remove(tmp_diff);
+ os_remove(tmp_new);
}
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
}
// When using 'diffexpr' break here.
@@ -756,7 +755,7 @@ void ex_diffupdate(exarg_T *eap)
// Write the first buffer to a tempfile.
buf_T *buf = curtab->tp_diffbuf[idx_orig];
- if (diff_write(buf, tmp_orig) == FAIL) {
+ if (diff_write(buf, (char_u *) tmp_orig) == FAIL) {
goto theend;
}
@@ -767,17 +766,17 @@ void ex_diffupdate(exarg_T *eap)
continue; // skip buffer that isn't loaded
}
- if (diff_write(buf, tmp_new) == FAIL) {
+ if (diff_write(buf, (char_u *) tmp_new) == FAIL) {
continue;
}
diff_file(tmp_orig, tmp_new, tmp_diff);
// Read the diff output and add each entry to the diff list.
- diff_read(idx_orig, idx_new, tmp_diff);
- os_remove((char *)tmp_diff);
- os_remove((char *)tmp_new);
+ diff_read(idx_orig, idx_new, (char_u *) tmp_diff);
+ os_remove(tmp_diff);
+ os_remove(tmp_new);
}
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
// force updating cursor position on screen
curwin->w_valid_cursor.lnum = 0;
@@ -795,15 +794,16 @@ theend:
/// @param tmp_orig
/// @param tmp_new
/// @param tmp_diff
-static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff)
+static void diff_file(const char *const tmp_orig, const char *const tmp_new,
+ const char *const tmp_diff)
{
if (*p_dex != NUL) {
// Use 'diffexpr' to generate the diff file.
eval_diff(tmp_orig, tmp_new, tmp_diff);
} else {
- size_t len = STRLEN(tmp_orig) + STRLEN(tmp_new) + STRLEN(tmp_diff)
- + STRLEN(p_srr) + 27;
- char_u *cmd = xmalloc(len);
+ const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
+ + STRLEN(p_srr) + 27);
+ char *const cmd = xmalloc(len);
/* We don't want $DIFF_OPTIONS to get in the way. */
if (os_getenv("DIFF_OPTIONS")) {
@@ -813,19 +813,17 @@ static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff)
/* Build the diff command and execute it. Always use -a, binary
* differences are of no use. Ignore errors, diff returns
* non-zero when differences have been found. */
- vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s",
- diff_a_works == FALSE ? "" : "-a ",
+ vim_snprintf(cmd, len, "diff %s%s%s%s%s %s",
+ diff_a_works ? "-a " : "",
"",
(diff_flags & DIFF_IWHITE) ? "-b " : "",
(diff_flags & DIFF_ICASE) ? "-i " : "",
tmp_orig, tmp_new);
- append_redir(cmd, len, p_srr, tmp_diff);
+ append_redir(cmd, len, (char *) p_srr, tmp_diff);
block_autocmds(); // Avoid ShellCmdPost stuff
- (void)call_shell(
- cmd,
- kShellOptFilter | kShellOptSilent | kShellOptDoOut,
- NULL
- );
+ (void)call_shell((char_u *) cmd,
+ kShellOptFilter | kShellOptSilent | kShellOptDoOut,
+ NULL);
unblock_autocmds();
xfree(cmd);
}
@@ -902,9 +900,11 @@ void ex_diffpatch(exarg_T *eap)
if (*p_pex != NUL) {
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
- eval_patch(tmp_orig, fullname != NULL ? fullname : eap->arg, tmp_new);
+ eval_patch((char *) tmp_orig,
+ (char *) (fullname != NULL ? fullname : eap->arg),
+ (char *) tmp_new);
#else
- eval_patch(tmp_orig, eap->arg, tmp_new);
+ eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new);
#endif // ifdef UNIX
} else {
// Build the patch command and execute it. Ignore errors. Switch to
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 614a5d43be..667ce1e779 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -199,7 +199,7 @@ typedef struct insert_state {
int did_restart_edit; // remember if insert mode was restarted
// after a ctrl+o
bool nomove;
- uint8_t *ptr;
+ char_u *ptr;
} InsertState;
@@ -270,8 +270,8 @@ static void insert_enter(InsertState *s)
s->ptr = (char_u *)"i";
}
- set_vim_var_string(VV_INSERTMODE, s->ptr, 1);
- set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
+ set_vim_var_string(VV_INSERTMODE, (char *) s->ptr, 1);
+ set_vim_var_string(VV_CHAR, NULL, -1);
apply_autocmds(EVENT_INSERTENTER, NULL, NULL, false, curbuf);
// Make sure the cursor didn't move. Do call check_cursor_col() in
@@ -1815,7 +1815,7 @@ static bool del_char_after_col(int limit_col)
if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) {
return false;
}
- del_bytes((long)(ecol - curwin->w_cursor.col), false, true);
+ del_bytes(ecol - curwin->w_cursor.col, false, true);
} else {
del_char(false);
}
@@ -7239,15 +7239,15 @@ static void ins_insert(int replaceState)
return;
}
- set_vim_var_string(VV_INSERTMODE,
- (char_u *)((State & REPLACE_FLAG) ? "i" :
- replaceState == VREPLACE ? "v" :
- "r"), 1);
- apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf);
- if (State & REPLACE_FLAG)
+ set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" :
+ replaceState == VREPLACE ? "v" :
+ "r"), 1);
+ apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, false, curbuf);
+ if (State & REPLACE_FLAG) {
State = INSERT | (State & LANGMAP);
- else
+ } else {
State = replaceState | (State & LANGMAP);
+ }
AppendCharToRedobuff(K_INS);
showmode();
ui_cursor_shape(); /* may show different cursor shape */
@@ -7741,6 +7741,8 @@ static void ins_mousescroll(int dir)
(long)(curwin->w_botline - curwin->w_topline));
else
scroll_redraw(dir, 3L);
+ } else {
+ mouse_scroll_horiz(dir);
}
did_scroll = TRUE;
}
@@ -8480,22 +8482,22 @@ static colnr_T get_nolist_virtcol(void)
*/
static char_u *do_insert_char_pre(int c)
{
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
// Return quickly when there is nothing to do.
if (!has_event(EVENT_INSERTCHARPRE)) {
return NULL;
}
if (has_mbyte) {
- buf[(*mb_char2bytes)(c, buf)] = NUL;
+ buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL;
} else {
buf[0] = c;
buf[1] = NUL;
}
- /* Lock the text to avoid weird things from happening. */
- ++textlock;
- set_vim_var_string(VV_CHAR, buf, -1); /* set v:char */
+ // Lock the text to avoid weird things from happening.
+ textlock++;
+ set_vim_var_string(VV_CHAR, buf, -1);
char_u *res = NULL;
if (apply_autocmds(EVENT_INSERTCHARPRE, NULL, NULL, FALSE, curbuf)) {
@@ -8506,8 +8508,8 @@ static char_u *do_insert_char_pre(int c)
res = vim_strsave(get_vim_var_str(VV_CHAR));
}
- set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
- --textlock;
+ set_vim_var_string(VV_CHAR, NULL, -1);
+ textlock--;
return res;
}
diff --git a/src/nvim/edit.h b/src/nvim/edit.h
index 0289b2c3a6..0d61f26bcc 100644
--- a/src/nvim/edit.h
+++ b/src/nvim/edit.h
@@ -36,12 +36,6 @@ typedef int (*IndentGetter)(void);
#define INSCHAR_NO_FEX 8 /* don't use 'formatexpr' */
#define INSCHAR_COM_LIST 16 /* format comments with list/2nd line indent */
-/* direction for nv_mousescroll() and ins_mousescroll() */
-#define MSCR_DOWN 0 /* DOWN must be FALSE */
-#define MSCR_UP 1
-#define MSCR_LEFT -1
-#define MSCR_RIGHT -2
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "edit.h.generated.h"
#endif
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a72f17a2b9..8b04d9afa4 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -66,13 +66,14 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/terminal.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
+#include "nvim/eval/encode.h"
+#include "nvim/eval/decode.h"
#include "nvim/os/os.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/pty_process.h"
@@ -87,7 +88,6 @@
#include "nvim/os/dl.h"
#include "nvim/os/input.h"
#include "nvim/event/loop.h"
-#include "nvim/lib/kvec.h"
#include "nvim/lib/queue.h"
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -142,13 +142,6 @@ typedef struct lval_S {
char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
} lval_T;
-/// Structure defining state for read_from_list()
-typedef struct {
- const listitem_T *li; ///< Item currently read.
- size_t offset; ///< Byte offset inside the read item.
- size_t li_length; ///< Length of the string inside the read item.
-} ListReaderState;
-
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_listidx = N_("E684: list index out of range: %" PRId64);
@@ -185,17 +178,7 @@ static dictitem_T globvars_var; /* variable used for g: */
*/
static hashtab_T compat_hashtab;
-/*
- * When recursively copying lists and dicts we need to remember which ones we
- * have done to avoid endless recursiveness. This unique ID is used for that.
- * The last bit is used for previous_funccal, ignored when comparing.
- */
-static int current_copyID = 0;
-#define COPYID_INC 2
-#define COPYID_MASK (~0x1)
-
-/// Abort conversion to string after a recursion error.
-static bool did_echo_string_emsg = false;
+hashtab_T func_hashtab;
/*
* Array to hold the hashtab with variables local to each sourced script.
@@ -298,94 +281,112 @@ typedef enum {
#define VV_RO 2 /* read-only */
#define VV_RO_SBX 4 /* read-only in the sandbox */
-#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0}
+#define VV(idx, name, type, flags) \
+ [idx] = { \
+ .vv_name = name, \
+ .vv_di = { \
+ .di_tv = { .v_type = type }, \
+ .di_flags = 0, \
+ }, \
+ .vv_filler = { 0 }, \
+ .vv_flags = flags, \
+ }
// Array to hold the value of v: variables.
// The value is in a dictitem, so that it can also be used in the v: scope.
// The reason to use this table anyway is for very quick access to the
// variables with the VV_ defines.
static struct vimvar {
- char *vv_name; /* name of variable, without v: */
- dictitem_T vv_di; /* value and name for key */
- char vv_filler[16]; /* space for LONGEST name below!!! */
- char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */
-} vimvars[VV_LEN] =
-{
- /*
- * The order here must match the VV_ defines in eval.h!
- * Initializing a union does not work, leave tv.vval empty to get zero's.
- */
- { VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("count1", VAR_NUMBER), VV_RO },
- { VV_NAME("prevcount", VAR_NUMBER), VV_RO },
- { VV_NAME("errmsg", VAR_STRING), VV_COMPAT },
- { VV_NAME("warningmsg", VAR_STRING), 0 },
- { VV_NAME("statusmsg", VAR_STRING), 0 },
- { VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("this_session", VAR_STRING), VV_COMPAT },
- { VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("termresponse", VAR_STRING), VV_RO },
- { VV_NAME("fname", VAR_STRING), VV_RO },
- { VV_NAME("lang", VAR_STRING), VV_RO },
- { VV_NAME("lc_time", VAR_STRING), VV_RO },
- { VV_NAME("ctype", VAR_STRING), VV_RO },
- { VV_NAME("charconvert_from", VAR_STRING), VV_RO },
- { VV_NAME("charconvert_to", VAR_STRING), VV_RO },
- { VV_NAME("fname_in", VAR_STRING), VV_RO },
- { VV_NAME("fname_out", VAR_STRING), VV_RO },
- { VV_NAME("fname_new", VAR_STRING), VV_RO },
- { VV_NAME("fname_diff", VAR_STRING), VV_RO },
- { VV_NAME("cmdarg", VAR_STRING), VV_RO },
- { VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("folddashes", VAR_STRING), VV_RO_SBX },
- { VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("progname", VAR_STRING), VV_RO },
- { VV_NAME("servername", VAR_STRING), VV_RO },
- { VV_NAME("dying", VAR_NUMBER), VV_RO },
- { VV_NAME("exception", VAR_STRING), VV_RO },
- { VV_NAME("throwpoint", VAR_STRING), VV_RO },
- { VV_NAME("register", VAR_STRING), VV_RO },
- { VV_NAME("cmdbang", VAR_NUMBER), VV_RO },
- { VV_NAME("insertmode", VAR_STRING), VV_RO },
- { VV_NAME("val", VAR_UNKNOWN), VV_RO },
- { VV_NAME("key", VAR_UNKNOWN), VV_RO },
- { VV_NAME("profiling", VAR_NUMBER), VV_RO },
- { VV_NAME("fcs_reason", VAR_STRING), VV_RO },
- { VV_NAME("fcs_choice", VAR_STRING), 0 },
- { VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_winnr", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_lnum", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_col", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_text", VAR_STRING), VV_RO },
- { VV_NAME("scrollstart", VAR_STRING), 0 },
- { VV_NAME("swapname", VAR_STRING), VV_RO },
- { VV_NAME("swapchoice", VAR_STRING), 0 },
- { VV_NAME("swapcommand", VAR_STRING), VV_RO },
- { VV_NAME("char", VAR_STRING), 0 },
- { VV_NAME("mouse_win", VAR_NUMBER), 0 },
- { VV_NAME("mouse_lnum", VAR_NUMBER), 0 },
- { VV_NAME("mouse_col", VAR_NUMBER), 0 },
- { VV_NAME("operator", VAR_STRING), VV_RO },
- { VV_NAME("searchforward", VAR_NUMBER), 0 },
- { VV_NAME("hlsearch", VAR_NUMBER), 0 },
- { VV_NAME("oldfiles", VAR_LIST), 0 },
- { VV_NAME("windowid", VAR_NUMBER), VV_RO },
- { VV_NAME("progpath", VAR_STRING), VV_RO },
- { VV_NAME("command_output", VAR_STRING), 0 },
- { VV_NAME("completed_item", VAR_DICT), VV_RO },
- { VV_NAME("option_new", VAR_STRING), VV_RO },
- { VV_NAME("option_old", VAR_STRING), VV_RO },
- { VV_NAME("option_type", VAR_STRING), VV_RO },
- { VV_NAME("errors", VAR_LIST), 0 },
- { VV_NAME("msgpack_types", VAR_DICT), VV_RO },
- { VV_NAME("event", VAR_DICT), VV_RO },
+ char *vv_name; ///< Name of the variable, without v:.
+ dictitem_T vv_di; ///< Value of the variable, with name.
+ char vv_filler[16]; ///< Space for longest name from below.
+ char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
+} vimvars[] =
+{
+ // VV_ tails differing from upcased string literals:
+ // VV_CC_FROM "charconvert_from"
+ // VV_CC_TO "charconvert_to"
+ // VV_SEND_SERVER "servername"
+ // VV_REG "register"
+ // VV_OP "operator"
+ VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
+ VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
+ VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT),
+ VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
+ VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
+ VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT),
+ VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
+ VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
+ VV(VV_LANG, "lang", VAR_STRING, VV_RO),
+ VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
+ VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
+ VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
+ VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
+ VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
+ VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
+ VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
+ VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
+ VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
+ VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
+ VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
+ VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
+ VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
+ VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
+ VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_REG, "register", VAR_STRING, VV_RO),
+ VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
+ VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
+ VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
+ VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
+ VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
+ VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
+ VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
+ VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
+ VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
+ VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
+ VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
+ VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
+ VV(VV_CHAR, "char", VAR_STRING, 0),
+ VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
+ VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
+ VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
+ VV(VV_OP, "operator", VAR_STRING, VV_RO),
+ VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
+ VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
+ VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
+ VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
+ VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0),
+ VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO),
+ VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
+ VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
+ VV(VV_ERRORS, "errors", VAR_LIST, 0),
+ VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
+ VV(VV_EVENT, "event", VAR_DICT, VV_RO),
+ VV(VV_FALSE, "false", VAR_SPECIAL, VV_RO),
+ VV(VV_TRUE, "true", VAR_SPECIAL, VV_RO),
+ VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
+ VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
+ VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
};
+#undef VV
/* shorthand */
#define vv_type vv_di.di_tv.v_type
#define vv_nr vv_di.di_tv.vval.v_number
+#define vv_special vv_di.di_tv.vval.v_special
#define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string
#define vv_list vv_di.di_tv.vval.v_list
@@ -419,29 +420,6 @@ typedef struct dict_watcher {
bool busy; // prevent recursion if the dict is changed in the callback
} DictWatcher;
-/// Structure representing current VimL to messagepack conversion state
-typedef struct {
- enum {
- kMPConvDict, ///< Convert dict_T *dictionary.
- kMPConvList, ///< Convert list_T *list.
- kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
- } type;
- union {
- struct {
- dict_T *dict; ///< Currently converted dictionary.
- hashitem_T *hi; ///< Currently converted dictionary item.
- size_t todo; ///< Amount of items left to process.
- } d; ///< State of dictionary conversion.
- struct {
- list_T *list; ///< Currently converted list.
- listitem_T *li; ///< Currently converted list item.
- } l; ///< State of list or generic mapping conversion.
- } data; ///< Data to convert.
-} MPConvStackVal;
-
-/// Stack used to convert VimL values to messagepack.
-typedef kvec_t(MPConvStackVal) MPConvStack;
-
typedef struct {
TerminalJobData *data;
ufunc_T *callback;
@@ -460,17 +438,6 @@ typedef struct {
static uint64_t current_job_id = 1;
static PMap(uint64_t) *jobs = NULL;
-typedef enum {
- kMPNil,
- kMPBoolean,
- kMPInteger,
- kMPFloat,
- kMPString,
- kMPBinary,
- kMPArray,
- kMPMap,
- kMPExt,
-} MessagePackType;
static const char *const msgpack_type_names[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -482,7 +449,7 @@ static const char *const msgpack_type_names[] = {
[kMPMap] = "map",
[kMPExt] = "ext",
};
-static const list_T *msgpack_type_lists[] = {
+const list_T *eval_msgpack_type_lists[] = {
[kMPNil] = NULL,
[kMPBoolean] = NULL,
[kMPInteger] = NULL,
@@ -499,8 +466,9 @@ static const list_T *msgpack_type_lists[] = {
*/
void eval_init(void)
{
+ vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+
jobs = pmap_new(uint64_t)();
- int i;
struct vimvar *p;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
@@ -509,7 +477,7 @@ void eval_init(void)
hash_init(&compat_hashtab);
hash_init(&func_hashtab);
- for (i = 0; i < VV_LEN; ++i) {
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
p = &vimvars[i];
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO)
@@ -538,7 +506,7 @@ void eval_init(void)
.v_type = VAR_LIST,
.vval = { .v_list = type_list, },
};
- msgpack_type_lists[i] = type_list;
+ eval_msgpack_type_lists[i] = type_list;
if (dict_add(msgpack_types_dict, di) == FAIL) {
// There must not be duplicate items in this dictionary by definition.
assert(false);
@@ -555,7 +523,12 @@ void eval_init(void)
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
- set_reg_var(0); /* default for v:register is not 0 but '"' */
+
+ set_vim_var_special(VV_FALSE, kSpecialVarFalse);
+ set_vim_var_special(VV_TRUE, kSpecialVarTrue);
+ set_vim_var_special(VV_NULL, kSpecialVarNull);
+
+ set_reg_var(0); // default for v:register is not 0 but '"'
}
#if defined(EXITFREE)
@@ -563,7 +536,7 @@ void eval_clear(void)
{
struct vimvar *p;
- for (int i = 0; i < VV_LEN; ++i) {
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING) {
xfree(p->vv_str);
@@ -798,45 +771,50 @@ void var_redir_stop(void)
redir_varname = NULL;
}
-int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to)
+int eval_charconvert(const char *const enc_from, const char *const enc_to,
+ const char *const fname_from, const char *const fname_to)
{
- int err = FALSE;
+ int err = false;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
- if (eval_to_bool(p_ccv, &err, NULL, FALSE))
- err = TRUE;
+ if (eval_to_bool(p_ccv, &err, NULL, false)) {
+ err = true;
+ }
set_vim_var_string(VV_CC_FROM, NULL, -1);
set_vim_var_string(VV_CC_TO, NULL, -1);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
- if (err)
+ if (err) {
return FAIL;
+ }
return OK;
}
-int eval_printexpr(char_u *fname, char_u *args)
+int eval_printexpr(const char *const fname, const char *const args)
{
- int err = FALSE;
+ int err = false;
set_vim_var_string(VV_FNAME_IN, fname, -1);
set_vim_var_string(VV_CMDARG, args, -1);
- if (eval_to_bool(p_pexpr, &err, NULL, FALSE))
- err = TRUE;
+ if (eval_to_bool(p_pexpr, &err, NULL, false)) {
+ err = true;
+ }
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_CMDARG, NULL, -1);
if (err) {
- os_remove((char *)fname);
+ os_remove(fname);
return FAIL;
}
return OK;
}
-void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile)
+void eval_diff(const char *const origfile, const char *const newfile,
+ const char *const outfile)
{
int err = FALSE;
@@ -849,7 +827,8 @@ void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile)
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
}
-void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile)
+void eval_patch(const char *const origfile, const char *const difffile,
+ const char *const outfile)
{
int err;
@@ -1733,7 +1712,7 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
} else {
int c;
- char_u *s = (char_u *) echo_string(&tv, NULL);
+ char_u *s = (char_u *) encode_tv2echo(&tv, NULL);
c = *arg;
*arg = NUL;
list_one_var_a((char_u *)"",
@@ -2417,11 +2396,12 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
char_u numbuf[NUMBUFLEN];
char_u *s;
- /* Can't do anything with a Funcref or a Dict on the right. */
+ // Can't do anything with a Funcref, a Dict or special value on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
+ case VAR_SPECIAL:
break;
case VAR_LIST:
@@ -2489,6 +2469,9 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
tv1->vval.v_float -= f;
}
return OK;
+
+ case VAR_UNKNOWN:
+ assert(false);
}
}
@@ -3126,6 +3109,15 @@ static void item_lock(typval_T *tv, int deep, int lock)
}
}
}
+ break;
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_STRING:
+ case VAR_FUNC:
+ case VAR_SPECIAL:
+ break;
+ case VAR_UNKNOWN:
+ assert(false);
}
--recurse;
}
@@ -3204,7 +3196,7 @@ char_u *get_user_var_name(expand_T *xp, int idx)
static size_t bdone;
static size_t wdone;
static size_t tdone;
- static int vidx;
+ static size_t vidx;
static hashitem_T *hi;
hashtab_T *ht;
@@ -3266,9 +3258,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
return cat_prefix_varname('t', hi->hi_key);
}
- /* v: variables */
- if (vidx < VV_LEN)
+ // v: variables
+ if (vidx < ARRAY_SIZE(vimvars)) {
return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
+ }
xfree(varnamebuf);
varnamebuf = NULL;
@@ -4158,7 +4151,7 @@ static int eval7(
if (get_float) {
float_T f;
- *arg += string2float(*arg, &f);
+ *arg += string2float((char *) *arg, &f);
if (evaluate) {
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f;
@@ -4347,14 +4340,37 @@ eval_index (
char_u *s;
char_u *key = NULL;
- if (rettv->v_type == VAR_FUNC) {
- if (verbose)
- EMSG(_("E695: Cannot index a Funcref"));
- return FAIL;
- } else if (rettv->v_type == VAR_FLOAT) {
- if (verbose)
- EMSG(_(e_float_as_string));
- return FAIL;
+ switch (rettv->v_type) {
+ case VAR_FUNC: {
+ if (verbose) {
+ EMSG(_("E695: Cannot index a Funcref"));
+ }
+ return FAIL;
+ }
+ case VAR_FLOAT: {
+ if (verbose) {
+ EMSG(_(e_float_as_string));
+ }
+ return FAIL;
+ }
+ case VAR_SPECIAL: {
+ if (verbose) {
+ EMSG(_("E909: Cannot index a special variable"));
+ }
+ return FAIL;
+ }
+ case VAR_UNKNOWN: {
+ if (evaluate) {
+ return FAIL;
+ }
+ // fallthrough
+ }
+ case VAR_STRING:
+ case VAR_NUMBER:
+ case VAR_LIST:
+ case VAR_DICT: {
+ break;
+ }
}
init_tv(&var1);
@@ -4545,6 +4561,11 @@ eval_index (
*rettv = var1;
}
break;
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break; // Not evaluating, skipping over subscript
}
}
@@ -5090,10 +5111,18 @@ tv_equal (
s1 = get_tv_string_buf(tv1, buf1);
s2 = get_tv_string_buf(tv2, buf2);
return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0;
+
+ case VAR_SPECIAL:
+ return tv1->vval.v_special == tv2->vval.v_special;
+
+ case VAR_UNKNOWN:
+ // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does
+ // not equal anything, not even self.
+ return false;
}
- EMSG2(_(e_intern2), "tv_equal()");
- return TRUE;
+ assert(false);
+ return false;
}
/*
@@ -5494,9 +5523,10 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) {
char *s;
size_t len;
- s = echo_string(&item->li_tv, &len);
- if (s == NULL)
+ s = encode_tv2echo(&item->li_tv, &len);
+ if (s == NULL) {
return FAIL;
+ }
sumlen += (int) len;
@@ -5504,9 +5534,6 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
p->tofree = p->s = (char_u *) s;
line_breakcheck();
- if (did_echo_string_emsg) { // recursion error, bail out
- break;
- }
}
/* Allocate result buffer with its total size, avoid re-allocation and
@@ -5558,6 +5585,22 @@ static int list_join(garray_T *const gap, list_T *const l,
return retval;
}
+/// Get next (unique) copy ID
+///
+/// Used for traversing nested structures e.g. when serializing them or garbage
+/// collecting.
+int get_copyID(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // CopyID for recursively traversing lists and dicts
+ //
+ // This value is needed to avoid endless recursiveness. Last bit is used for
+ // previous_funccal and normally ignored when comparing.
+ static int current_copyID = 0;
+ current_copyID += COPYID_INC;
+ return current_copyID;
+}
+
/*
* Garbage collection for lists and dictionaries.
*
@@ -5593,8 +5636,7 @@ bool garbage_collect(void)
// We advance by two because we add one for items referenced through
// previous_funccal.
- current_copyID += COPYID_INC;
- int copyID = current_copyID;
+ const int copyID = get_copyID();
// 1. Go through all accessible variables and mark all lists and dicts
// with copyID.
@@ -5939,6 +5981,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
}
break;
}
+
+ case VAR_FUNC:
+ case VAR_UNKNOWN:
+ case VAR_SPECIAL:
+ case VAR_FLOAT:
+ case VAR_NUMBER:
+ case VAR_STRING: {
+ break;
+ }
}
return abort;
}
@@ -6530,596 +6581,22 @@ failret:
return OK;
}
-#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
- do { \
- if ((val)->copyID_attr == copyID) { \
- CONV_RECURSE((val), conv_type); \
- } \
- (val)->copyID_attr = copyID; \
- } while (0)
-
-/// Define functions which convert VimL value to something else
-///
-/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
-/// tv)` which returns OK or FAIL and helper functions.
-///
-/// @param firstargtype Type of the first argument. It will be used to return
-/// the results.
-/// @param firstargname Name of the first argument.
-/// @param name Name of the target converter.
-#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
-static int name##_convert_one_value(firstargtype firstargname, \
- MPConvStack *const mpstack, \
- typval_T *const tv, \
- const int copyID, \
- const char *const objname) \
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- switch (tv->v_type) { \
- case VAR_STRING: { \
- CONV_STRING(tv->vval.v_string, STRLEN(tv->vval.v_string)); \
- break; \
- } \
- case VAR_NUMBER: { \
- CONV_NUMBER(tv->vval.v_number); \
- break; \
- } \
- case VAR_FLOAT: { \
- CONV_FLOAT(tv->vval.v_float); \
- break; \
- } \
- case VAR_FUNC: { \
- CONV_FUNC(tv->vval.v_string); \
- break; \
- } \
- case VAR_LIST: { \
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
- CONV_EMPTY_LIST(); \
- break; \
- } \
- CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \
- CONV_LIST_START(tv->vval.v_list); \
- kv_push( \
- MPConvStackVal, \
- *mpstack, \
- ((MPConvStackVal) { \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = tv->vval.v_list, \
- .li = tv->vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_DICT: { \
- if (tv->vval.v_dict == NULL \
- || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
- CONV_EMPTY_DICT(); \
- break; \
- } \
- const dictitem_T *type_di; \
- const dictitem_T *val_di; \
- if (CONV_ALLOW_SPECIAL \
- && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
- && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_TYPE", -1)) != NULL \
- && type_di->di_tv.v_type == VAR_LIST \
- && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_VAL", -1)) != NULL) { \
- size_t i; \
- for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) { \
- if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) { \
- break; \
- } \
- } \
- if (i == ARRAY_SIZE(msgpack_type_lists)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- switch ((MessagePackType) i) { \
- case kMPNil: { \
- CONV_SPECIAL_NIL(); \
- break; \
- } \
- case kMPBoolean: { \
- if (val_di->di_tv.v_type != VAR_NUMBER) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_SPECIAL_BOOL(val_di->di_tv.vval.v_number); \
- break; \
- } \
- case kMPInteger: { \
- const list_T *val_list; \
- varnumber_T sign; \
- varnumber_T highest_bits; \
- varnumber_T high_bits; \
- varnumber_T low_bits; \
- /* List of 4 integers; first is signed (should be 1 or -1, but */ \
- /* this is not checked), second is unsigned and have at most */ \
- /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
- /* bits is not checked), other unsigned and have at most 31 */ \
- /* non-zero bits (number of bits is not checked).*/ \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 4 \
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
- || (highest_bits = \
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
- || (high_bits = \
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
- | (uint64_t) (((uint64_t) high_bits) << 31) \
- | (uint64_t) low_bits); \
- if (sign > 0) { \
- CONV_UNSIGNED_NUMBER(number); \
- } else { \
- CONV_NUMBER(-number); \
- } \
- break; \
- } \
- case kMPFloat: { \
- if (val_di->di_tv.v_type != VAR_FLOAT) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_FLOAT(val_di->di_tv.vval.v_float); \
- break; \
- } \
- case kMPString: \
- case kMPBinary: { \
- const bool is_string = ((MessagePackType) i == kMPString); \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (is_string) { \
- CONV_STR_STRING(buf, len); \
- } else { \
- CONV_STRING(buf, len); \
- } \
- xfree(buf); \
- break; \
- } \
- case kMPArray: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \
- kMPConvList); \
- CONV_LIST_START(val_di->di_tv.vval.v_list); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = val_di->di_tv.vval.v_list, \
- .li = val_di->di_tv.vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPMap: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (val_di->di_tv.vval.v_list == NULL) { \
- CONV_EMPTY_DICT(); \
- break; \
- } \
- list_T *const val_list = val_di->di_tv.vval.v_list; \
- for (const listitem_T *li = val_list->lv_first; li != NULL; \
- li = li->li_next) { \
- if (li->li_tv.v_type != VAR_LIST \
- || li->li_tv.vval.v_list->lv_len != 2) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- } \
- CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \
- CONV_SPECIAL_MAP_START(val_list); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvPairs, \
- .data = { \
- .l = { \
- .list = val_list, \
- .li = val_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPExt: { \
- const list_T *val_list; \
- varnumber_T type; \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 2 \
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
- || type < INT8_MIN \
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
- &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_EXT_STRING(buf, len, type); \
- xfree(buf); \
- break; \
- } \
- } \
- break; \
- } \
-name##_convert_one_value_regular_dict: \
- CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \
- CONV_DICT_START(tv->vval.v_dict); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvDict, \
- .data = { \
- .d = { \
- .dict = tv->vval.v_dict, \
- .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
- .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
- }, \
- }, \
- })); \
- break; \
- } \
- default: { \
- EMSG2(_(e_intern2), #name "_convert_one_value()"); \
- return FAIL; \
- } \
- } \
- return OK; \
-} \
-\
-scope int vim_to_##name(firstargtype firstargname, typval_T *const tv, \
- const char *const objname) \
- FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- current_copyID += COPYID_INC; \
- const int copyID = current_copyID; \
- MPConvStack mpstack; \
- kv_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
- == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- while (kv_size(mpstack)) { \
- MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
- typval_T *cur_tv = NULL; \
- switch (cur_mpsv->type) { \
- case kMPConvDict: { \
- if (!cur_mpsv->data.d.todo) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
- CONV_DICT_END(cur_mpsv->data.d.dict); \
- continue; \
- } else if (cur_mpsv->data.d.todo \
- != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
- CONV_DICT_BETWEEN_ITEMS(cur_mpsv->data.d.dict); \
- } \
- while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
- cur_mpsv->data.d.hi++; \
- } \
- dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
- cur_mpsv->data.d.todo--; \
- cur_mpsv->data.d.hi++; \
- CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \
- CONV_DICT_AFTER_KEY(cur_mpsv->data.d.dict); \
- cur_tv = &di->di_tv; \
- break; \
- } \
- case kMPConvList: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- CONV_LIST_END(cur_mpsv->data.l.list); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- CONV_LIST_BETWEEN_ITEMS(cur_mpsv->data.l.list); \
- } \
- cur_tv = &cur_mpsv->data.l.li->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- case kMPConvPairs: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- continue; \
- } \
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
- if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID, \
- objname) == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- cur_tv = &kv_pair->lv_last->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- } \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
- objname) == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- } \
- kv_destroy(mpstack); \
- return OK; \
-vim_to_msgpack_error_ret: \
- kv_destroy(mpstack); \
- return FAIL; \
-}
-
-#define CONV_STRING(buf, len) \
- do { \
- const char *const buf_ = (const char *) buf; \
- if (buf == NULL) { \
- ga_concat(gap, (char_u *) "''"); \
- } else { \
- const size_t len_ = (len); \
- size_t num_quotes = 0; \
- for (size_t i = 0; i < len_; i++) { \
- if (buf_[i] == '\'') { \
- num_quotes++; \
- } \
- } \
- ga_grow(gap, 2 + len_ + num_quotes); \
- ga_append(gap, '\''); \
- for (size_t i = 0; i < len_; i++) { \
- if (buf_[i] == '\'') { \
- num_quotes++; \
- ga_append(gap, '\''); \
- } \
- ga_append(gap, buf_[i]); \
- } \
- ga_append(gap, '\''); \
- } \
- } while (0)
-
-#define CONV_STR_STRING(buf, len) \
- CONV_STRING(buf, len)
-
-#define CONV_EXT_STRING(buf, len, type)
-
-#define CONV_NUMBER(num) \
- do { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%" PRId64, (int64_t) (num)); \
- ga_concat(gap, (char_u *) numbuf); \
- } while (0)
-
-#define CONV_FLOAT(flt) \
- do { \
- const float_T flt_ = (flt); \
- switch (fpclassify(flt_)) { \
- case FP_NAN: { \
- ga_concat(gap, (char_u *) "str2float('nan')"); \
- break; \
- } \
- case FP_INFINITE: { \
- if (flt_ < 0) { \
- ga_append(gap, '-'); \
- } \
- ga_concat(gap, (char_u *) "str2float('inf')"); \
- break; \
- } \
- default: { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \
- ga_concat(gap, (char_u *) numbuf); \
- } \
- } \
- } while (0)
-
-#define CONV_FUNC(fun) \
- do { \
- ga_concat(gap, (char_u *) "function("); \
- CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
- } while (0)
-
-#define CONV_EMPTY_LIST() \
- ga_concat(gap, (char_u *) "[]")
-
-#define CONV_LIST_START(lst) \
- ga_append(gap, '[')
-
-#define CONV_EMPTY_DICT() \
- ga_concat(gap, (char_u *) "{}")
-
-#define CONV_SPECIAL_NIL()
-
-#define CONV_SPECIAL_BOOL(num)
-
-#define CONV_UNSIGNED_NUMBER(num)
-
-#define CONV_SPECIAL_MAP_START(lst)
-
-#define CONV_DICT_START(dct) \
- ga_append(gap, '{')
-
-#define CONV_DICT_END(dct) \
- ga_append(gap, '}')
-
-#define CONV_DICT_AFTER_KEY(dct) \
- ga_concat(gap, (char_u *) ": ")
-
-#define CONV_DICT_BETWEEN_ITEMS(dct) \
- ga_concat(gap, (char_u *) ", ")
-
-#define CONV_LIST_END(lst) \
- ga_append(gap, ']')
-
-#define CONV_LIST_BETWEEN_ITEMS(lst) \
- CONV_DICT_BETWEEN_ITEMS(NULL)
-
-#define CONV_RECURSE(val, conv_type) \
- do { \
- if (!did_echo_string_emsg) { \
- /* Only give this message once for a recursive call to avoid */ \
- /* flooding the user with errors. */ \
- did_echo_string_emsg = true; \
- EMSG(_("E724: unable to correctly dump variable " \
- "with self-referencing container")); \
- } \
- char ebuf[NUMBUFLEN + 7]; \
- size_t backref = 0; \
- for (; backref < kv_size(*mpstack); backref++) { \
- const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
- if ((void *) mpval.data.d.dict == (void *) (val)) { \
- break; \
- } \
- } else if (conv_type == kMPConvList) { \
- if ((void *) mpval.data.l.list == (void *) (val)) { \
- break; \
- } \
- } \
- } \
- } \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "{E724@%zu}", backref); \
- ga_concat(gap, (char_u *) &ebuf[0]); \
- return OK; \
- } while (0)
-
-#define CONV_ALLOW_SPECIAL false
-
-DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
-
-#undef CONV_RECURSE
-#define CONV_RECURSE(val, conv_type) \
- do { \
- char ebuf[NUMBUFLEN + 7]; \
- size_t backref = 0; \
- for (; backref < kv_size(*mpstack); backref++) { \
- const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
- if ((void *) mpval.data.d.dict == (void *) val) { \
- break; \
- } \
- } else if (conv_type == kMPConvList) { \
- if ((void *) mpval.data.l.list == (void *) val) { \
- break; \
- } \
- } \
- } \
- } \
- if (conv_type == kMPConvDict) { \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "{...@%zu}", backref); \
- } else { \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "[...@%zu]", backref); \
- } \
- ga_concat(gap, (char_u *) &ebuf[0]); \
- return OK; \
- } while (0)
-
-DEFINE_VIML_CONV_FUNCTIONS(static, echo, garray_T *const, gap)
-
-#undef CONV_STRING
-#undef CONV_STR_STRING
-#undef CONV_EXT_STRING
-#undef CONV_NUMBER
-#undef CONV_FLOAT
-#undef CONV_FUNC
-#undef CONV_EMPTY_LIST
-#undef CONV_LIST_START
-#undef CONV_EMPTY_DICT
-#undef CONV_SPECIAL_NIL
-#undef CONV_SPECIAL_BOOL
-#undef CONV_UNSIGNED_NUMBER
-#undef CONV_SPECIAL_MAP_START
-#undef CONV_DICT_START
-#undef CONV_DICT_END
-#undef CONV_DICT_AFTER_KEY
-#undef CONV_DICT_BETWEEN_ITEMS
-#undef CONV_LIST_END
-#undef CONV_LIST_BETWEEN_ITEMS
-#undef CONV_RECURSE
-#undef CONV_ALLOW_SPECIAL
-
-/// Return a string with the string representation of a variable.
-/// Puts quotes around strings, so that they can be parsed back by eval().
-///
-/// @param[in] tv typval_T to convert.
-/// @param[out] len Location where length of the result will be saved.
+/// Convert the string to a floating point number
///
-/// @return String representation of the variable or NULL.
-static char *tv2string(typval_T *tv, size_t *len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
-{
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
- vim_to_string(&ga, tv, "tv2string() argument");
- did_echo_string_emsg = false;
- if (len != NULL) {
- *len = (size_t) ga.ga_len;
- }
- ga_append(&ga, '\0');
- return (char *) ga.ga_data;
-}
-
-/// Return a string with the string representation of a variable.
-/// Does not put quotes around strings, as ":echo" displays values.
+/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to
+/// make sure this always uses a decimal point.
///
-/// @param[in] tv typval_T to convert.
-/// @param[out] len Location where length of the result will be saved.
+/// @param[in] text String to convert.
+/// @param[out] ret_value Location where conversion result is saved.
///
-/// @return String representation of the variable or NULL.
-static char *echo_string(typval_T *tv, size_t *len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
-{
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
- if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
- if (tv->vval.v_string != NULL) {
- ga_concat(&ga, tv->vval.v_string);
- }
- } else {
- vim_to_echo(&ga, tv, ":echo argument");
- did_echo_string_emsg = false;
- }
- if (len != NULL) {
- *len = (size_t) ga.ga_len;
- }
- ga_append(&ga, '\0');
- return (char *) ga.ga_data;
-}
-
-/*
- * Convert the string "text" to a floating point number.
- * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure
- * this always uses a decimal point.
- * Returns the length of the text that was consumed.
- */
-static int
-string2float (
- char_u *text,
- float_T *value /* result stored here */
-)
+/// @return Length of the text that was consumed.
+size_t string2float(const char *const text, float_T *const ret_value)
+ FUNC_ATTR_NONNULL_ALL
{
- char *s = (char *)text;
- float_T f;
+ char *s = NULL;
- f = strtod(s, &s);
- *value = f;
- return (int)((char_u *)s - text);
+ *ret_value = strtod(text, &s);
+ return (size_t) (s - text);
}
/// Get the value of an environment variable.
@@ -7179,7 +6656,7 @@ static struct fst {
} functions[] =
{
{ "abs", 1, 1, f_abs },
- { "acos", 1, 1, f_acos }, // WJMc
+ { "acos", 1, 1, f_acos }, // WJMc
{ "add", 2, 2, f_add },
{ "and", 2, 2, f_and },
{ "append", 2, 2, f_append },
@@ -7196,9 +6673,9 @@ static struct fst {
{ "browse", 4, 4, f_browse },
{ "browsedir", 2, 2, f_browsedir },
{ "bufexists", 1, 1, f_bufexists },
- { "buffer_exists", 1, 1, f_bufexists }, // obsolete
- { "buffer_name", 1, 1, f_bufname }, // obsolete
- { "buffer_number", 1, 1, f_bufnr }, // obsolete
+ { "buffer_exists", 1, 1, f_bufexists }, // obsolete
+ { "buffer_name", 1, 1, f_bufname }, // obsolete
+ { "buffer_number", 1, 1, f_bufnr }, // obsolete
{ "buflisted", 1, 1, f_buflisted },
{ "bufloaded", 1, 1, f_bufloaded },
{ "bufname", 1, 1, f_bufname },
@@ -7225,7 +6702,7 @@ static struct fst {
{ "cscope_connection", 0, 3, f_cscope_connection },
{ "cursor", 1, 3, f_cursor },
{ "deepcopy", 1, 2, f_deepcopy },
- { "delete", 1, 1, f_delete },
+ { "delete", 1, 2, f_delete },
{ "dictwatcheradd", 3, 3, f_dictwatcheradd },
{ "dictwatcherdel", 3, 3, f_dictwatcherdel },
{ "did_filetype", 0, 0, f_did_filetype },
@@ -7242,7 +6719,7 @@ static struct fst {
{ "expand", 1, 3, f_expand },
{ "extend", 2, 3, f_extend },
{ "feedkeys", 1, 2, f_feedkeys },
- { "file_readable", 1, 1, f_filereadable }, // obsolete
+ { "file_readable", 1, 1, f_filereadable }, // obsolete
{ "filereadable", 1, 1, f_filereadable },
{ "filewritable", 1, 1, f_filewritable },
{ "filter", 2, 2, f_filter },
@@ -7272,7 +6749,7 @@ static struct fst {
{ "getcmdtype", 0, 0, f_getcmdtype },
{ "getcmdwintype", 0, 0, f_getcmdwintype },
{ "getcurpos", 0, 0, f_getcurpos },
- { "getcwd", 0, 0, f_getcwd },
+ { "getcwd", 0, 2, f_getcwd },
{ "getfontname", 0, 1, f_getfontname },
{ "getfperm", 1, 1, f_getfperm },
{ "getfsize", 1, 1, f_getfsize },
@@ -7296,7 +6773,7 @@ static struct fst {
{ "globpath", 2, 5, f_globpath },
{ "has", 1, 1, f_has },
{ "has_key", 2, 2, f_has_key },
- { "haslocaldir", 0, 0, f_haslocaldir },
+ { "haslocaldir", 0, 2, f_haslocaldir },
{ "hasmapto", 1, 3, f_hasmapto },
{ "highlightID", 1, 1, f_hlID }, // obsolete
{ "highlight_exists", 1, 1, f_hlexists }, // obsolete
@@ -7329,6 +6806,8 @@ static struct fst {
{ "jobstop", 1, 1, f_jobstop },
{ "jobwait", 1, 2, f_jobwait },
{ "join", 1, 2, f_join },
+ { "json_decode", 1, 1, f_json_decode },
+ { "json_encode", 1, 1, f_json_encode },
{ "keys", 1, 1, f_keys },
{ "last_buffer_nr", 0, 0, f_last_buffer_nr }, // obsolete
{ "len", 1, 1, f_len },
@@ -8103,19 +7582,19 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
char_u *tofree;
if (opt_msg_tv->v_type != VAR_UNKNOWN) {
- tofree = (char_u *) tv2string(opt_msg_tv, NULL);
+ tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL);
ga_concat(gap, tofree);
xfree(tofree);
} else {
ga_concat(gap, (char_u *)"Expected ");
if (exp_str == NULL) {
- tofree = (char_u *) tv2string(exp_tv, NULL);
+ tofree = (char_u *) encode_tv2string(exp_tv, NULL);
ga_concat(gap, tofree);
xfree(tofree);
} else {
ga_concat(gap, exp_str);
}
- tofree = (char_u *) tv2string(got_tv, NULL);
+ tofree = (char_u *) encode_tv2string(got_tv, NULL);
ga_concat(gap, (char_u *)" but got ");
ga_concat(gap, tofree);
xfree(tofree);
@@ -8150,16 +7629,21 @@ static void f_assert_equal(typval_T *argvars, typval_T *rettv)
}
// Common for assert_true() and assert_false().
-static void assert_bool(typval_T *argvars, bool isTrue)
+static void assert_bool(typval_T *argvars, bool is_true)
{
int error = (int)false;
garray_T ga;
- if (argvars[0].v_type != VAR_NUMBER ||
- (get_tv_number_chk(&argvars[0], &error) == 0) == isTrue || error) {
+ if ((argvars[0].v_type != VAR_NUMBER ||
+ (get_tv_number_chk(&argvars[0], &error) == 0) == is_true || error)
+ && (argvars[0].v_type != VAR_SPECIAL
+ || (argvars[0].vval.v_special
+ != (SpecialVarValue) (is_true
+ ? kSpecialVarTrue
+ : kSpecialVarFalse)))) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1],
- (char_u *)(isTrue ? "True" : "False"),
+ (char_u *)(is_true ? "True" : "False"),
NULL, &argvars[0]);
assert_error(&ga);
ga_clear(&ga);
@@ -8821,12 +8305,12 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = cs_connection(num, dbpath, prepend);
}
-/*
- * "cursor(lnum, col)" function
- *
- * Moves the cursor to the specified line and column.
- * Returns 0 when the position could be set, -1 otherwise.
- */
+/// "cursor(lnum, col)" function, or
+/// "cursor(list)"
+///
+/// Moves the cursor to the specified line and column.
+///
+/// @returns 0 when the position could be set, -1 otherwise.
static void f_cursor(typval_T *argvars, typval_T *rettv)
{
long line, col;
@@ -8838,8 +8322,10 @@ static void f_cursor(typval_T *argvars, typval_T *rettv)
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) {
+ EMSG(_(e_invarg));
return;
}
+
line = pos.lnum;
col = pos.col;
coladd = pos.coladd;
@@ -8881,25 +8367,51 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
noref = get_tv_number_chk(&argvars[1], NULL);
- if (noref < 0 || noref > 1)
+ if (noref < 0 || noref > 1) {
EMSG(_(e_invarg));
- else {
- current_copyID += COPYID_INC;
+ } else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
- ? current_copyID
+ ? get_copyID()
: 0));
}
}
-/*
- * "delete()" function
- */
+// "delete()" function
static void f_delete(typval_T *argvars, typval_T *rettv)
{
- if (check_restricted() || check_secure())
- rettv->vval.v_number = -1;
- else
- rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0]));
+ char_u nbuf[NUMBUFLEN];
+ char_u *name;
+ char_u *flags;
+
+ rettv->vval.v_number = -1;
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ name = get_tv_string(&argvars[0]);
+ if (name == NULL || *name == NUL) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ flags = get_tv_string_buf(&argvars[1], nbuf);
+ } else {
+ flags = (char_u *)"";
+ }
+
+ if (*flags == NUL) {
+ // delete a file
+ rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1;
+ } else if (STRCMP(flags, "d") == 0) {
+ // delete an empty directory
+ rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1;
+ } else if (STRCMP(flags, "rf") == 0) {
+ // delete a directory recursively
+ rettv->vval.v_number = delete_recursive(name);
+ } else {
+ EMSG2(_(e_invexpr2), flags);
+ }
}
// dictwatcheradd(dict, key, funcref) function
@@ -9076,7 +8588,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv)
*/
static void f_empty(typval_T *argvars, typval_T *rettv)
{
- int n;
+ bool n = true;
switch (argvars[0].v_type) {
case VAR_STRING:
@@ -9098,9 +8610,12 @@ static void f_empty(typval_T *argvars, typval_T *rettv)
n = argvars[0].vval.v_dict == NULL
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
break;
- default:
- EMSG2(_(e_intern2), "f_empty()");
- n = 0;
+ case VAR_SPECIAL:
+ n = argvars[0].vval.v_special != kSpecialVarTrue;
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
+ break;
}
rettv->vval.v_number = n;
@@ -10272,22 +9787,143 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string[0] = cmdwin_type;
}
-/*
- * "getcwd()" function
- */
+/// `getcwd([{win}[, {tab}]])` function
+///
+/// Every scope not specified implies the currently selected scope object.
+///
+/// @pre The arguments must be of type number.
+/// @pre There may not be more than two arguments.
+/// @pre An argument may not be -1 if preceding arguments are not all -1.
+///
+/// @post The return value will be a string.
static void f_getcwd(typval_T *argvars, typval_T *rettv)
{
- char_u *cwd;
+ // Possible scope of working directory to return.
+ CdScope scope = kCdScopeWindow;
+
+ // Numbers of the scope objects (window, tab) we want the working directory
+ // of. A `-1` means to skip this scope, a `0` means the current object.
+ int scope_number[] = {
+ [kCdScopeWindow] = 0, // Number of window to look at.
+ [kCdScopeTab ] = 0, // Number of tab to look at.
+ };
+
+ char_u *cwd = NULL; // Current working directory to print
+ char_u *from = NULL; // The original string to copy
+
+ tabpage_T *tp = curtab; // The tabpage to look at.
+ win_T *win = curwin; // The window to look at.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+
+ // Pre-conditions and scope extraction together
+ for (int i = 0; i < kCdScopeGlobal; i++) {
+ if (argvars[i].v_type == VAR_UNKNOWN) {
+ break;
+ }
+ if (argvars[i].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ scope_number[i] = argvars[i].vval.v_number;
+ // The scope is the current iteration step.
+ scope = i;
+
+ if (scope_number[i] < -1) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ }
+
+ // Allocate and initialize the string to return.
cwd = xmalloc(MAXPATHL);
- if (os_dirname(cwd, MAXPATHL) != FAIL) {
- rettv->vval.v_string = vim_strsave(cwd);
+
+ // Get the scope and numbers from the arguments
+ for (int i = 0; i < MAX_CD_SCOPE; i++) {
+ // If there is no argument there are no more scopes after it, break out.
+ if (argvars[i].v_type == VAR_UNKNOWN) {
+ break;
+ }
+ scope_number[i] = argvars[i].vval.v_number;
+ // The scope is the current iteration step.
+ scope = i;
+ // It is an error for the scope number to be less than `-1`.
+ if (scope_number[i] < -1) {
+ EMSG(_(e_invarg));
+ goto end;
+ }
+ }
+
+ // If the deepest scope number is `-1` advance the scope.
+ if (scope_number[scope] < 0) {
+ scope++;
+ }
+
+ // Find the tabpage by number
+ if (scope_number[kCdScopeTab] == -1) {
+ tp = NULL;
+ } else if (scope_number[kCdScopeTab] > 0) {
+ tp = find_tabpage(scope_number[kCdScopeTab]);
+ if (!tp) {
+ EMSG(_("E5000: Cannot find tab number."));
+ goto end;
+ }
+ }
+
+ // Find the window in `tp` by number, `NULL` if none.
+ if (scope_number[kCdScopeWindow] == -1) {
+ win = NULL;
+ } else if (scope_number[kCdScopeWindow] >= 0) {
+ if (!tp) {
+ EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
+ goto end;
+ }
+
+ if (scope_number[kCdScopeWindow] > 0) {
+ win = find_win_by_nr(&argvars[0], curtab);
+ if (!win) {
+ EMSG(_("E5002: Cannot find window number."));
+ goto end;
+ }
+ }
+ }
+
+ switch (scope) {
+ case kCdScopeWindow:
+ from = win->w_localdir;
+ if (from) {
+ break;
+ }
+ case kCdScopeTab: // FALLTHROUGH
+ from = tp->localdir;
+ if (from) {
+ break;
+ }
+ case kCdScopeGlobal: // FALLTHROUGH
+ // The `globaldir` variable is not always set.
+ if (globaldir) {
+ from = globaldir;
+ } else {
+ // Copy the OS path directly into output string and jump to the end.
+ if (os_dirname(cwd, MAXPATHL) == FAIL) {
+ EMSG(_("E41: Could not display path."));
+ goto end;
+ }
+ }
+ break;
+ }
+
+ if (from) {
+ xstrlcpy((char *)cwd, (char *)from, MAXPATHL);
+ }
+
+ rettv->vval.v_string = vim_strsave(cwd);
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(rettv->vval.v_string);
+ slash_adjust(rettv->vval.v_string);
#endif
- }
+
+end:
xfree(cwd);
}
@@ -10610,9 +10246,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_LIST;
rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
- if (rettv->vval.v_list != NULL) {
- rettv->vval.v_list->lv_refcount++;
+ if (rettv->vval.v_list == NULL) {
+ rettv->vval.v_list = list_alloc();
}
+ rettv->vval.v_list->lv_refcount++;
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0);
@@ -11106,12 +10743,98 @@ static void f_has_key(typval_T *argvars, typval_T *rettv)
get_tv_string(&argvars[1]), -1) != NULL;
}
-/*
- * "haslocaldir()" function
- */
+/// `haslocaldir([{win}[, {tab}]])` function
+///
+/// Returns `1` if the scope object has a local directory, `0` otherwise. If a
+/// scope object is not specified the current one is implied. This function
+/// share a lot of code with `f_getcwd`.
+///
+/// @pre The arguments must be of type number.
+/// @pre There may not be more than two arguments.
+/// @pre An argument may not be -1 if preceding arguments are not all -1.
+///
+/// @post The return value will be either the number `1` or `0`.
static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
- rettv->vval.v_number = (curwin->w_localdir != NULL);
+ // Possible scope of working directory to return.
+ CdScope scope = kCdScopeWindow;
+
+ // Numbers of the scope objects (window, tab) we want the working directory
+ // of. A `-1` means to skip this scope, a `0` means the current object.
+ int scope_number[] = {
+ [kCdScopeWindow] = 0, // Number of window to look at.
+ [kCdScopeTab ] = 0, // Number of tab to look at.
+ };
+
+ tabpage_T *tp = curtab; // The tabpage to look at.
+ win_T *win = curwin; // The window to look at.
+
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ // Pre-conditions and scope extraction together
+ for (int i = 0; i < kCdScopeGlobal; i++) {
+ if (argvars[i].v_type == VAR_UNKNOWN) {
+ break;
+ }
+ if (argvars[i].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ scope_number[i] = argvars[i].vval.v_number;
+ // The scope is the current iteration step.
+ scope = i;
+ if (scope_number[i] < -1) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ }
+
+ // It the deepest scope number is `-1` advance the scope by one.
+ if (scope_number[scope] < 0) {
+ ++scope;
+ }
+
+ // Find the tabpage by number
+ if (scope_number[kCdScopeTab] == -1) {
+ tp = NULL;
+ } else if (scope_number[kCdScopeTab] > 0) {
+ tp = find_tabpage(scope_number[kCdScopeTab]);
+ if (!tp) {
+ EMSG(_("5000: Cannot find tab number."));
+ return;
+ }
+ }
+
+ // Find the window in `tp` by number, `NULL` if none.
+ if (scope_number[kCdScopeWindow] == -1) {
+ win = NULL;
+ } else if (scope_number[kCdScopeWindow] >= 0) {
+ if (!tp) {
+ EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
+ return;
+ }
+
+ if (scope_number[kCdScopeWindow] > 0) {
+ win = find_win_by_nr(&argvars[0], curtab);
+ if (!win) {
+ EMSG(_("E5002: Cannot find window number."));
+ return;
+ }
+ }
+ }
+
+ switch (scope) {
+ case kCdScopeWindow:
+ rettv->vval.v_number = win->w_localdir ? 1 : 0;
+ break;
+ case kCdScopeTab:
+ rettv->vval.v_number = tp->localdir ? 1 : 0;
+ break;
+ case kCdScopeGlobal:
+ assert(0);
+ break;
+ }
}
/*
@@ -12124,6 +11847,47 @@ static void f_join(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;
}
+/// json_decode() function
+static void f_json_decode(typval_T *argvars, typval_T *rettv)
+{
+ char numbuf[NUMBUFLEN];
+ char *s = NULL;
+ char *tofree = NULL;
+ size_t len;
+ if (argvars[0].v_type == VAR_LIST) {
+ if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) {
+ EMSG(_("E474: Failed to convert list to string"));
+ return;
+ }
+ tofree = s;
+ if (s == NULL) {
+ assert(len == 0);
+ s = "";
+ }
+ } else {
+ s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf);
+ if (s) {
+ len = strlen(s);
+ } else {
+ return;
+ }
+ }
+ if (json_decode_string(s, len, rettv) == FAIL) {
+ emsgf(_("E474: Failed to parse %.*s"), (int) len, s);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+ }
+ assert(rettv->v_type != VAR_UNKNOWN);
+ xfree(tofree);
+}
+
+/// json_encode() function
+static void f_json_encode(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char_u *) encode_tv2json(&argvars[0], NULL);
+}
+
/*
* "keys()" function
*/
@@ -12165,7 +11929,10 @@ static void f_len(typval_T *argvars, typval_T *rettv)
case VAR_DICT:
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
break;
- default:
+ case VAR_UNKNOWN:
+ case VAR_SPECIAL:
+ case VAR_FLOAT:
+ case VAR_FUNC:
EMSG(_("E701: Invalid type for len()"));
break;
}
@@ -12489,9 +12256,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
break;
}
xfree(tofree);
- tofree = str = (char_u *) echo_string(&li->li_tv, NULL);
- if (str == NULL)
+ tofree = str = (char_u *) encode_tv2echo(&li->li_tv, NULL);
+ if (str == NULL) {
break;
+ }
}
match = vim_regexec_nl(&regmatch, str, (colnr_T)startcol);
@@ -12889,289 +12657,6 @@ static void f_mode(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_STRING;
}
-/// Msgpack callback for writing to readfile()-style list
-static int msgpack_list_write(void *data, const char *buf, size_t len)
-{
- if (len == 0) {
- return 0;
- }
- list_T *const list = (list_T *) data;
- const char *const end = buf + len;
- const char *line_end = buf;
- if (list->lv_last == NULL) {
- list_append_string(list, NULL, 0);
- }
- listitem_T *li = list->lv_last;
- do {
- const char *line_start = line_end;
- line_end = xmemscan(line_start, NL, end - line_start);
- if (line_end == line_start) {
- list_append_allocated_string(list, NULL);
- } else {
- const size_t line_length = line_end - line_start;
- char *str;
- if (li == NULL) {
- str = xmemdupz(line_start, line_length);
- } else {
- const size_t li_len = (li->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(li->li_tv.vval.v_string));
- li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string,
- li_len + line_length + 1);
- str = (char *) li->li_tv.vval.v_string + li_len;
- memmove(str, line_start, line_length);
- str[line_length] = 0;
- }
- for (size_t i = 0; i < line_length; i++) {
- if (str[i] == NUL) {
- str[i] = NL;
- }
- }
- if (li == NULL) {
- list_append_allocated_string(list, str);
- } else {
- li = NULL;
- }
- if (line_end == end - 1) {
- list_append_allocated_string(list, NULL);
- }
- }
- line_end++;
- } while (line_end < end);
- return 0;
-}
-
-/// Convert readfile()-style list to a char * buffer with length
-///
-/// @param[in] list Converted list.
-/// @param[out] ret_len Resulting buffer length.
-/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
-/// zero.
-///
-/// @return true in case of success, false in case of failure.
-static inline bool vim_list_to_buf(const list_T *const list,
- size_t *const ret_len, char **const ret_buf)
- FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT
-{
- size_t len = 0;
- if (list != NULL) {
- for (const listitem_T *li = list->lv_first;
- li != NULL;
- li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
- return false;
- }
- len++;
- if (li->li_tv.vval.v_string != 0) {
- len += STRLEN(li->li_tv.vval.v_string);
- }
- }
- if (len) {
- len--;
- }
- }
- *ret_len = len;
- if (len == 0) {
- *ret_buf = NULL;
- return true;
- }
- ListReaderState lrstate = init_lrstate(list);
- char *const buf = xmalloc(len);
- size_t read_bytes;
- if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
- assert(false);
- }
- assert(len == read_bytes);
- *ret_buf = buf;
- return true;
-}
-
-/// Show a error message when converting to msgpack value
-///
-/// @param[in] msg Error message to dump. Must contain exactly two %s that
-/// will be replaced with what was being dumped: first with
-/// something like “F†or “function argumentâ€, second with path
-/// to the failed value.
-/// @param[in] mpstack Path to the failed value.
-/// @param[in] objname Dumped object name.
-///
-/// @return FAIL.
-static int conv_error(const char *const msg, const MPConvStack *const mpstack,
- const char *const objname)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T msg_ga;
- ga_init(&msg_ga, (int)sizeof(char), 80);
- char *const key_msg = _("key %s");
- char *const key_pair_msg = _("key %s at index %i from special map");
- char *const idx_msg = _("index %i");
- for (size_t i = 0; i < kv_size(*mpstack); i++) {
- if (i != 0) {
- ga_concat(&msg_ga, (char_u *) ", ");
- }
- MPConvStackVal v = kv_A(*mpstack, i);
- switch (v.type) {
- case kMPConvDict: {
- typval_T key_tv = {
- .v_type = VAR_STRING,
- .vval = { .v_string = (v.data.d.hi == NULL
- ? v.data.d.dict->dv_hashtab.ht_array
- : (v.data.d.hi - 1))->hi_key },
- };
- char *const key = tv2string(&key_tv, NULL);
- vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
- xfree(key);
- ga_concat(&msg_ga, IObuff);
- break;
- }
- case kMPConvPairs:
- case kMPConvList: {
- int idx = 0;
- const listitem_T *li;
- for (li = v.data.l.list->lv_first;
- li != NULL && li->li_next != v.data.l.li;
- li = li->li_next) {
- idx++;
- }
- if (v.type == kMPConvList
- || li == NULL
- || (li->li_tv.v_type != VAR_LIST
- && li->li_tv.vval.v_list->lv_len <= 0)) {
- vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
- ga_concat(&msg_ga, IObuff);
- } else {
- typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
- char *const key = echo_string(&key_tv, NULL);
- vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
- xfree(key);
- ga_concat(&msg_ga, IObuff);
- }
- break;
- }
- }
- }
- EMSG3(msg, objname, (kv_size(*mpstack) == 0
- ? _("itself")
- : (char *) msg_ga.ga_data));
- ga_clear(&msg_ga);
- return FAIL;
-}
-
-#define CONV_STRING(buf, len) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_bin(packer, 0); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_bin(packer, len_); \
- msgpack_pack_bin_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_STR_STRING(buf, len) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_str(packer, 0); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_str(packer, len_); \
- msgpack_pack_str_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_EXT_STRING(buf, len, type) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_ext(packer, 0, type); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_ext(packer, len_, (int8_t) type); \
- msgpack_pack_ext_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_NUMBER(num) \
- msgpack_pack_int64(packer, (int64_t) (num))
-
-#define CONV_FLOAT(flt) \
- msgpack_pack_double(packer, (double) (flt))
-
-#define CONV_FUNC(fun) \
- return conv_error(_("E951: Error while dumping %s, %s: " \
- "attempt to dump function reference"), \
- mpstack, objname)
-
-#define CONV_EMPTY_LIST() \
- msgpack_pack_array(packer, 0)
-
-#define CONV_LIST_START(lst) \
- msgpack_pack_array(packer, (lst)->lv_len)
-
-#define CONV_EMPTY_DICT() \
- msgpack_pack_map(packer, 0)
-
-#define CONV_SPECIAL_NIL() \
- msgpack_pack_nil(packer)
-
-#define CONV_SPECIAL_BOOL(num) \
- do { \
- if ((num)) { \
- msgpack_pack_true(packer); \
- } else { \
- msgpack_pack_false(packer); \
- } \
- } while (0)
-
-#define CONV_UNSIGNED_NUMBER(num) \
- msgpack_pack_uint64(packer, (num))
-
-#define CONV_SPECIAL_MAP_START(lst) \
- msgpack_pack_map(packer, (lst)->lv_len)
-
-#define CONV_DICT_START(dct) \
- msgpack_pack_map(packer, (dct)->dv_hashtab.ht_used)
-
-#define CONV_DICT_END(dct)
-
-#define CONV_DICT_AFTER_KEY(dct)
-
-#define CONV_DICT_BETWEEN_ITEMS(dct)
-
-#define CONV_LIST_END(lst)
-
-#define CONV_LIST_BETWEEN_ITEMS(lst)
-
-#define CONV_RECURSE(val, conv_type) \
- return conv_error(_("E952: Unable to dump %s: " \
- "container references itself in %s"), \
- mpstack, objname)
-
-#define CONV_ALLOW_SPECIAL true
-
-DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
-
-#undef CONV_STRING
-#undef CONV_STR_STRING
-#undef CONV_EXT_STRING
-#undef CONV_NUMBER
-#undef CONV_FLOAT
-#undef CONV_FUNC
-#undef CONV_EMPTY_LIST
-#undef CONV_LIST_START
-#undef CONV_EMPTY_DICT
-#undef CONV_SPECIAL_NIL
-#undef CONV_SPECIAL_BOOL
-#undef CONV_UNSIGNED_NUMBER
-#undef CONV_SPECIAL_MAP_START
-#undef CONV_DICT_START
-#undef CONV_DICT_END
-#undef CONV_DICT_AFTER_KEY
-#undef CONV_DICT_BETWEEN_ITEMS
-#undef CONV_LIST_END
-#undef CONV_LIST_BETWEEN_ITEMS
-#undef CONV_RECURSE
-#undef CONV_ALLOW_SPECIAL
-
/// "msgpackdump()" function
static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
@@ -13185,7 +12670,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
if (list == NULL) {
return;
}
- msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write);
+ msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);
const char *const msg = _("msgpackdump() argument, index %i");
// Assume that translation will not take more then 4 times more space
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
@@ -13193,307 +12678,13 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx);
idx++;
- if (vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
+ if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
break;
}
}
msgpack_packer_free(lpacker);
}
-/// Read bytes from list
-///
-/// @param[in,out] state Structure describing position in list from which
-/// reading should start. Is updated to reflect position
-/// at which reading ended.
-/// @param[out] buf Buffer to write to.
-/// @param[in] nbuf Buffer length.
-/// @param[out] read_bytes Is set to amount of bytes read.
-///
-/// @return OK when reading was finished, FAIL in case of error (i.e. list item
-/// was not a string), NOTDONE if reading was successfull, but there are
-/// more bytes to read.
-static int read_from_list(ListReaderState *const state, char *const buf,
- const size_t nbuf, size_t *const read_bytes)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- char *const buf_end = buf + nbuf;
- char *p = buf;
- while (p < buf_end) {
- for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
- const char ch = state->li->li_tv.vval.v_string[state->offset++];
- *p++ = (ch == NL ? NUL : ch);
- }
- if (p < buf_end) {
- state->li = state->li->li_next;
- if (state->li == NULL) {
- *read_bytes = (size_t) (p - buf);
- return OK;
- }
- *p++ = NL;
- if (state->li->li_tv.v_type != VAR_STRING) {
- *read_bytes = (size_t) (p - buf);
- return FAIL;
- }
- state->offset = 0;
- state->li_length = (state->li->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(state->li->li_tv.vval.v_string));
- }
- }
- *read_bytes = nbuf;
- return (state->offset < state->li_length || state->li->li_next != NULL
- ? NOTDONE
- : OK);
-}
-
-/// Initialize ListReaderState structure
-static inline ListReaderState init_lrstate(const list_T *const list)
- FUNC_ATTR_NONNULL_ALL
-{
- return (ListReaderState) {
- .li = list->lv_first,
- .offset = 0,
- .li_length = (list->lv_first->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(list->lv_first->li_tv.vval.v_string)),
- };
-}
-
-/// Convert msgpack object to a VimL one
-int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
-#define INIT_SPECIAL_DICT(tv, type, val) \
- do { \
- dict_T *const dict = dict_alloc(); \
- dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \
- type_di->di_tv.v_type = VAR_LIST; \
- type_di->di_tv.v_lock = 0; \
- type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \
- type_di->di_tv.vval.v_list->lv_refcount++; \
- dict_add(dict, type_di); \
- dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \
- val_di->di_tv = val; \
- dict_add(dict, val_di); \
- tv->v_type = VAR_DICT; \
- dict->dv_refcount++; \
- tv->vval.v_dict = dict; \
- } while (0)
- switch (mobj.type) {
- case MSGPACK_OBJECT_NIL: {
- INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = 0 },
- }));
- break;
- }
- case MSGPACK_OBJECT_BOOLEAN: {
- INIT_SPECIAL_DICT(rettv, kMPBoolean,
- ((typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = {
- .v_number = (varnumber_T) mobj.via.boolean,
- },
- }));
- break;
- }
- case MSGPACK_OBJECT_POSITIVE_INTEGER: {
- if (mobj.via.u64 <= VARNUMBER_MAX) {
- *rettv = (typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = (varnumber_T) mobj.via.u64 },
- };
- } else {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPInteger,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- uint64_t n = mobj.via.u64;
- list_append_number(list, 1);
- list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
- list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
- list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
- }
- break;
- }
- case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
- if (mobj.via.i64 >= VARNUMBER_MIN) {
- *rettv = (typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = (varnumber_T) mobj.via.i64 },
- };
- } else {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPInteger,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- uint64_t n = -((uint64_t) mobj.via.i64);
- list_append_number(list, -1);
- list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
- list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
- list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
- }
- break;
- }
- case MSGPACK_OBJECT_FLOAT: {
- *rettv = (typval_T) {
- .v_type = VAR_FLOAT,
- .v_lock = 0,
- .vval = { .v_float = mobj.via.f64 },
- };
- break;
- }
- case MSGPACK_OBJECT_STR: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPString,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
- == -1) {
- return FAIL;
- }
- break;
- }
- case MSGPACK_OBJECT_BIN: {
- if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
- *rettv = (typval_T) {
- .v_type = VAR_STRING,
- .v_lock = 0,
- .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
- };
- break;
- }
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPBinary,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
- == -1) {
- return FAIL;
- }
- break;
- }
- case MSGPACK_OBJECT_ARRAY: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- *rettv = (typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- };
- for (size_t i = 0; i < mobj.via.array.size; i++) {
- listitem_T *const li = listitem_alloc();
- li->li_tv.v_type = VAR_UNKNOWN;
- list_append(list, li);
- if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
- }
- case MSGPACK_OBJECT_MAP: {
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
- || mobj.via.map.ptr[i].key.via.str.size == 0
- || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
- mobj.via.map.ptr[i].key.via.str.size) != NULL) {
- goto msgpack_to_vim_generic_map;
- }
- }
- dict_T *const dict = dict_alloc();
- dict->dv_refcount++;
- *rettv = (typval_T) {
- .v_type = VAR_DICT,
- .v_lock = 0,
- .vval = { .v_dict = dict },
- };
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
- + mobj.via.map.ptr[i].key.via.str.size);
- memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr,
- mobj.via.map.ptr[i].key.via.str.size);
- di->di_tv.v_type = VAR_UNKNOWN;
- if (dict_add(dict, di) == FAIL) {
- // Duplicate key: fallback to generic map
- clear_tv(rettv);
- xfree(di);
- goto msgpack_to_vim_generic_map;
- }
- if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
-msgpack_to_vim_generic_map: {}
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPMap,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- list_T *const kv_pair = list_alloc();
- list_append_list(list, kv_pair);
- listitem_T *const key_li = listitem_alloc();
- key_li->li_tv.v_type = VAR_UNKNOWN;
- list_append(kv_pair, key_li);
- listitem_T *const val_li = listitem_alloc();
- val_li->li_tv.v_type = VAR_UNKNOWN;
- list_append(kv_pair, val_li);
- if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
- return FAIL;
- }
- if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
- }
- case MSGPACK_OBJECT_EXT: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- list_append_number(list, mobj.via.ext.type);
- list_T *const ext_val_list = list_alloc();
- list_append_list(list, ext_val_list);
- INIT_SPECIAL_DICT(rettv, kMPExt,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr,
- mobj.via.ext.size) == -1) {
- return FAIL;
- }
- break;
- }
- }
-#undef INIT_SPECIAL_DICT
- return OK;
-}
-
/// "msgpackparse" function
static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
@@ -13511,7 +12702,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
EMSG2(_(e_invarg2), "List item is not a string");
return;
}
- ListReaderState lrstate = init_lrstate(list);
+ ListReaderState lrstate = encode_init_lrstate(list);
msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE);
if (unpacker == NULL) {
EMSG(_(e_outofmem));
@@ -13525,7 +12716,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
goto f_msgpackparse_exit;
}
size_t read_bytes;
- const int rlret = read_from_list(
+ const int rlret = encode_read_from_list(
&lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes);
if (rlret == FAIL) {
EMSG2(_(e_invarg2), "List item is not a string");
@@ -14354,14 +13545,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv)
}
}
-#define SP_NOMOVE 0x01 /* don't move cursor */
-#define SP_REPEAT 0x02 /* repeat to find outer pair */
-#define SP_RETCOUNT 0x04 /* return matchcount */
-#define SP_SETPCMARK 0x08 /* set previous context mark */
-#define SP_START 0x10 /* accept match at start position */
-#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */
-#define SP_END 0x40 /* leave cursor at end of match */
-
+#define SP_NOMOVE 0x01 ///< don't move cursor
+#define SP_REPEAT 0x02 ///< repeat to find outer pair
+#define SP_RETCOUNT 0x04 ///< return matchcount
+#define SP_SETPCMARK 0x08 ///< set previous context mark
+#define SP_START 0x10 ///< accept match at start position
+#define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern
+#define SP_END 0x40 ///< leave cursor at end of match
+#define SP_COLUMN 0x80 ///< start at cursor column
/*
* Get flags for a search function.
@@ -14387,13 +13578,14 @@ static int get_search_arg(typval_T *varp, int *flagsp)
default: mask = 0;
if (flagsp != NULL)
switch (*flags) {
- case 'c': mask = SP_START; break;
- case 'e': mask = SP_END; break;
- case 'm': mask = SP_RETCOUNT; break;
- case 'n': mask = SP_NOMOVE; break;
- case 'p': mask = SP_SUBPAT; break;
- case 'r': mask = SP_REPEAT; break;
- case 's': mask = SP_SETPCMARK; break;
+ case 'c': mask = SP_START; break;
+ case 'e': mask = SP_END; break;
+ case 'm': mask = SP_RETCOUNT; break;
+ case 'n': mask = SP_NOMOVE; break;
+ case 'p': mask = SP_SUBPAT; break;
+ case 'r': mask = SP_REPEAT; break;
+ case 's': mask = SP_SETPCMARK; break;
+ case 'z': mask = SP_COLUMN; break;
}
if (mask == 0) {
EMSG2(_(e_invarg2), flags);
@@ -14409,9 +13601,7 @@ static int get_search_arg(typval_T *varp, int *flagsp)
return dir;
}
-/*
- * Shared by search() and searchpos() functions
- */
+// Shared by search() and searchpos() functions.
static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
@@ -14432,10 +13622,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
if (dir == 0)
goto theend;
flags = *flagsp;
- if (flags & SP_START)
+ if (flags & SP_START) {
options |= SEARCH_START;
- if (flags & SP_END)
+ }
+ if (flags & SP_END) {
options |= SEARCH_END;
+ }
+ if (flags & SP_COLUMN) {
+ options |= SEARCH_COL;
+ }
/* Optional arguments: line number to stop searching and timeout. */
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
@@ -15765,6 +14960,8 @@ typedef struct {
static int item_compare_ic;
static bool item_compare_numeric;
+static bool item_compare_numbers;
+static bool item_compare_float;
static char_u *item_compare_func;
static dict_T *item_compare_selfdict;
static int item_compare_func_err;
@@ -15786,9 +14983,24 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
si2 = (sortItem_T *)s2;
typval_T *tv1 = &si1->item->li_tv;
typval_T *tv2 = &si2->item->li_tv;
- // tv2string() puts quotes around a string and allocates memory. Don't do
- // that for string variables. Use a single quote when comparing with a
- // non-string to do what the docs promise.
+
+ if (item_compare_numbers) {
+ long v1 = get_tv_number(tv1);
+ long v2 = get_tv_number(tv2);
+
+ return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ }
+
+ if (item_compare_float) {
+ float_T v1 = get_tv_float(tv1);
+ float_T v2 = get_tv_float(tv2);
+
+ return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ }
+
+ // encode_tv2string() puts quotes around a string and allocates memory. Don't
+ // do that for string variables. Use a single quote when comparing with
+ // a non-string to do what the docs promise.
if (tv1->v_type == VAR_STRING) {
if (tv2->v_type != VAR_STRING || item_compare_numeric) {
p1 = (char_u *)"'";
@@ -15796,7 +15008,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
p1 = tv1->vval.v_string;
}
} else {
- tofree1 = p1 = (char_u *) tv2string(tv1, NULL);
+ tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL);
}
if (tv2->v_type == VAR_STRING) {
if (tv1->v_type != VAR_STRING || item_compare_numeric) {
@@ -15805,7 +15017,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
p2 = tv2->vval.v_string;
}
} else {
- tofree2 = p2 = (char_u *) tv2string(tv2, NULL);
+ tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL);
}
if (p1 == NULL)
p1 = (char_u *)"";
@@ -15932,6 +15144,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
item_compare_ic = FALSE;
item_compare_numeric = false;
+ item_compare_numbers = false;
+ item_compare_float = false;
item_compare_func = NULL;
item_compare_selfdict = NULL;
@@ -15953,6 +15167,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (STRCMP(item_compare_func, "n") == 0) {
item_compare_func = NULL;
item_compare_numeric = true;
+ } else if (STRCMP(item_compare_func, "N") == 0) {
+ item_compare_func = NULL;
+ item_compare_numbers = true;
+ } else if (STRCMP(item_compare_func, "f") == 0) {
+ item_compare_func = NULL;
+ item_compare_float = true;
} else if (STRCMP(item_compare_func, "i") == 0) {
item_compare_func = NULL;
item_compare_ic = TRUE;
@@ -16241,7 +15461,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv)
if (*p == '+')
p = skipwhite(p + 1);
- (void)string2float(p, &rettv->vval.v_float);
+ (void) string2float((char *) p, &rettv->vval.v_float);
rettv->v_type = VAR_FLOAT;
}
@@ -16372,7 +15592,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv)
static void f_string(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *) tv2string(&argvars[0], NULL);
+ rettv->vval.v_string = (char_u *) encode_tv2string(&argvars[0], NULL);
}
/*
@@ -17105,9 +16325,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
// Save the job id and pid in b:terminal_job_{id,pid}
Error err;
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ(rettv->vval.v_number), &err);
+ INTEGER_OBJ(rettv->vval.v_number), false, &err);
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
- INTEGER_OBJ(pid), &err);
+ INTEGER_OBJ(pid), false, &err);
Terminal *term = terminal_open(topts);
data->term = term;
@@ -17293,16 +16513,33 @@ static void f_trunc(typval_T *argvars, typval_T *rettv)
*/
static void f_type(typval_T *argvars, typval_T *rettv)
{
- int n;
+ int n = -1;
switch (argvars[0].v_type) {
- case VAR_NUMBER: n = 0; break;
- case VAR_STRING: n = 1; break;
- case VAR_FUNC: n = 2; break;
- case VAR_LIST: n = 3; break;
- case VAR_DICT: n = 4; break;
- case VAR_FLOAT: n = 5; break;
- default: EMSG2(_(e_intern2), "f_type()"); n = 0; break;
+ case VAR_NUMBER: n = 0; break;
+ case VAR_STRING: n = 1; break;
+ case VAR_FUNC: n = 2; break;
+ case VAR_LIST: n = 3; break;
+ case VAR_DICT: n = 4; break;
+ case VAR_FLOAT: n = 5; break;
+ case VAR_SPECIAL: {
+ switch (argvars[0].vval.v_special) {
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ n = 6;
+ break;
+ }
+ case kSpecialVarNull: {
+ n = 7;
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_UNKNOWN: {
+ EMSG2(_(e_intern2), "f_type(UNKNOWN)");
+ break;
+ }
}
rettv->vval.v_number = n;
}
@@ -18021,9 +17258,9 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start,
}
} else if (br_nest == 0 && mb_nest == 0 && *p == ':') {
// "s:" is start of "s:var", but "n:" is not and can be used in
- // slice "[n:]". Also "xx:" is not a namespace.
+ // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. */
len = (int)(p - arg);
- if (len > 1
+ if ((len > 1 && p[-1] != '}')
|| (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) {
break;
}
@@ -18129,14 +17366,6 @@ static int eval_isnamec1(int c)
}
/*
- * Set number v: variable to "val".
- */
-void set_vim_var_nr(int idx, long val)
-{
- vimvars[idx].vv_nr = val;
-}
-
-/*
* Get number v: variable value.
*/
long get_vim_var_nr(int idx) FUNC_ATTR_PURE
@@ -18173,11 +17402,11 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
*/
void set_vim_var_char(int c)
{
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
- if (has_mbyte)
- buf[(*mb_char2bytes)(c, buf)] = NUL;
- else {
+ if (has_mbyte) {
+ buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL;
+ } else {
buf[0] = c;
buf[1] = NUL;
}
@@ -18196,47 +17425,68 @@ void set_vcount(long count, long count1, int set_prevcount)
vimvars[VV_COUNT1].vv_nr = count1;
}
-/*
- * Set string v: variable to a copy of "val".
- */
-void set_vim_var_string (
- int idx,
- char_u *val,
- int len /* length of "val" to use or -1 (whole string) */
-)
+/// Set number v: variable to the given value
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to.
+void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val)
{
- /* Need to do this (at least) once, since we can't initialize a union.
- * Will always be invoked when "v:progname" is set. */
- vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+ vimvars[idx].vv_nr = val;
+}
+/// Set special v: variable to the given value
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to.
+void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val)
+{
+ vimvars[idx].vv_special = val;
+}
+
+/// Set string v: variable to the given string
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to. Will be copied.
+/// @param[in] len Legth of that value or -1 in which case strlen() will be
+/// used.
+void set_vim_var_string(const VimVarIndex idx, const char *const val,
+ const ptrdiff_t len)
+{
xfree(vimvars[idx].vv_str);
- if (val == NULL)
+ if (val == NULL) {
vimvars[idx].vv_str = NULL;
- else if (len == -1)
- vimvars[idx].vv_str = vim_strsave(val);
- else
- vimvars[idx].vv_str = vim_strnsave(val, len);
+ } else if (len == -1) {
+ vimvars[idx].vv_str = (char_u *) xstrdup(val);
+ } else {
+ vimvars[idx].vv_str = (char_u *) xstrndup(val, (size_t) len);
+ }
}
-/*
- * Set List v: variable to "val".
- */
-void set_vim_var_list(int idx, list_T *val)
+/// Set list v: variable to the given list
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in,out] val Value to set to. Reference count will be incremented.
+void set_vim_var_list(const VimVarIndex idx, list_T *const val)
{
list_unref(vimvars[idx].vv_list);
vimvars[idx].vv_list = val;
- if (val != NULL)
- ++val->lv_refcount;
+ if (val != NULL) {
+ val->lv_refcount++;
+ }
}
-/// Set Dictionary v: variable to "val".
-void set_vim_var_dict(int idx, dict_T *val)
+/// Set Dictionary v: variable to the given dictionary
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in,out] val Value to set to. Reference count will be incremented.
+/// Also keys of the dictionary will be made read-only.
+void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
{
dict_unref(vimvars[idx].vv_dict);
vimvars[idx].vv_dict = val;
if (val != NULL) {
- ++val->dv_refcount;
+ val->dv_refcount++;
// Set readonly
dict_set_keys_readonly(val);
}
@@ -18247,15 +17497,17 @@ void set_vim_var_dict(int idx, dict_T *val)
*/
void set_reg_var(int c)
{
- char_u regname;
+ char regname;
- if (c == 0 || c == ' ')
+ if (c == 0 || c == ' ') {
regname = '"';
- else
+ } else {
regname = c;
- /* Avoid free/alloc when the value is already right. */
- if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c)
+ }
+ // Avoid free/alloc when the value is already right.
+ if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) {
set_vim_var_string(VV_REG, &regname, 1);
+ }
}
/*
@@ -18485,25 +17737,23 @@ void free_tv(typval_T *varp)
{
if (varp != NULL) {
switch (varp->v_type) {
- case VAR_FUNC:
- func_unref(varp->vval.v_string);
- /*FALLTHROUGH*/
- case VAR_STRING:
- xfree(varp->vval.v_string);
- break;
- case VAR_LIST:
- list_unref(varp->vval.v_list);
- break;
- case VAR_DICT:
- dict_unref(varp->vval.v_dict);
- break;
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_UNKNOWN:
- break;
- default:
- EMSG2(_(e_intern2), "free_tv()");
- break;
+ case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ // FALLTHROUGH
+ case VAR_STRING:
+ xfree(varp->vval.v_string);
+ break;
+ case VAR_LIST:
+ list_unref(varp->vval.v_list);
+ break;
+ case VAR_DICT:
+ dict_unref(varp->vval.v_dict);
+ break;
+ case VAR_SPECIAL:
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break;
}
xfree(varp);
}
@@ -18541,10 +17791,11 @@ void clear_tv(typval_T *varp)
case VAR_FLOAT:
varp->vval.v_float = 0.0;
break;
+ case VAR_SPECIAL:
+ varp->vval.v_special = kSpecialVarFalse;
+ break;
case VAR_UNKNOWN:
break;
- default:
- EMSG2(_(e_intern2), "clear_tv()");
}
varp->v_lock = 0;
}
@@ -18599,8 +17850,19 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_DICT:
EMSG(_("E728: Using a Dictionary as a Number"));
break;
- default:
- EMSG2(_(e_intern2), "get_tv_number()");
+ case VAR_SPECIAL:
+ switch (varp->vval.v_special) {
+ case kSpecialVarTrue: {
+ return 1;
+ }
+ case kSpecialVarFalse:
+ case kSpecialVarNull: {
+ return 0;
+ }
+ }
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
break;
}
if (denote == NULL) {
@@ -18612,6 +17874,33 @@ long get_tv_number_chk(typval_T *varp, int *denote)
return n;
}
+static float_T get_tv_float(typval_T *varp)
+{
+ switch (varp->v_type) {
+ case VAR_NUMBER:
+ return (float_T)(varp->vval.v_number);
+ case VAR_FLOAT:
+ return varp->vval.v_float;
+ break;
+ case VAR_FUNC:
+ EMSG(_("E891: Using a Funcref as a Float"));
+ break;
+ case VAR_STRING:
+ EMSG(_("E892: Using a String as a Float"));
+ break;
+ case VAR_LIST:
+ EMSG(_("E893: Using a List as a Float"));
+ break;
+ case VAR_DICT:
+ EMSG(_("E894: Using a Dictionary as a Float"));
+ break;
+ default:
+ EMSG2(_(e_intern2), "get_tv_float()");
+ break;
+ }
+ return 0;
+}
+
/*
* Get the lnum from the first argument.
* Also accepts ".", "$", etc., but that only works for the current buffer.
@@ -18705,8 +17994,11 @@ static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
if (varp->vval.v_string != NULL)
return varp->vval.v_string;
return (char_u *)"";
- default:
- EMSG2(_(e_intern2), "get_tv_string_buf()");
+ case VAR_SPECIAL:
+ STRCPY(buf, encode_special_var_names[varp->vval.v_special]);
+ return buf;
+ case VAR_UNKNOWN:
+ EMSG(_("E908: using an invalid value as a String"));
break;
}
return NULL;
@@ -18977,7 +18269,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi)
*/
static void list_one_var(dictitem_T *v, char_u *prefix, int *first)
{
- char_u *s = (char_u *) echo_string(&v->di_tv, NULL);
+ char_u *s = (char_u *) encode_tv2echo(&v->di_tv, NULL);
list_one_var_a(prefix, v->di_key, v->di_tv.v_type,
s == NULL ? (char_u *)"" : s, first);
xfree(s);
@@ -19254,42 +18546,34 @@ void copy_tv(typval_T *from, typval_T *to)
{
to->v_type = from->v_type;
to->v_lock = 0;
+ memmove(&to->vval, &from->vval, sizeof(to->vval));
switch (from->v_type) {
- case VAR_NUMBER:
- to->vval.v_number = from->vval.v_number;
- break;
- case VAR_FLOAT:
- to->vval.v_float = from->vval.v_float;
- break;
- case VAR_STRING:
- case VAR_FUNC:
- if (from->vval.v_string == NULL)
- to->vval.v_string = NULL;
- else {
- to->vval.v_string = vim_strsave(from->vval.v_string);
- if (from->v_type == VAR_FUNC)
- func_ref(to->vval.v_string);
- }
- break;
- case VAR_LIST:
- if (from->vval.v_list == NULL)
- to->vval.v_list = NULL;
- else {
- to->vval.v_list = from->vval.v_list;
- ++to->vval.v_list->lv_refcount;
- }
- break;
- case VAR_DICT:
- if (from->vval.v_dict == NULL)
- to->vval.v_dict = NULL;
- else {
- to->vval.v_dict = from->vval.v_dict;
- ++to->vval.v_dict->dv_refcount;
- }
- break;
- default:
- EMSG2(_(e_intern2), "copy_tv()");
- break;
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_SPECIAL:
+ break;
+ case VAR_STRING:
+ case VAR_FUNC:
+ if (from->vval.v_string != NULL) {
+ to->vval.v_string = vim_strsave(from->vval.v_string);
+ if (from->v_type == VAR_FUNC) {
+ func_ref(to->vval.v_string);
+ }
+ }
+ break;
+ case VAR_LIST:
+ if (from->vval.v_list != NULL) {
+ to->vval.v_list->lv_refcount++;
+ }
+ break;
+ case VAR_DICT:
+ if (from->vval.v_dict != NULL) {
+ to->vval.v_dict->dv_refcount++;
+ }
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "copy_tv(UNKNOWN)");
+ break;
}
}
@@ -19329,6 +18613,7 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_FUNC:
+ case VAR_SPECIAL:
copy_tv(from, to);
break;
case VAR_STRING:
@@ -19375,8 +18660,8 @@ int var_item_copy(const vimconv_T *const conv,
if (to->vval.v_dict == NULL)
ret = FAIL;
break;
- default:
- EMSG2(_(e_intern2), "var_item_copy()");
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)");
ret = FAIL;
}
--recurse;
@@ -19431,7 +18716,7 @@ void ex_echo(exarg_T *eap)
}
} else if (eap->cmdidx == CMD_echo)
msg_puts_attr((char_u *)" ", echo_attr);
- char_u *tofree = p = (char_u *) echo_string(&rettv, NULL);
+ char_u *tofree = p = (char_u *) encode_tv2echo(&rettv, NULL);
if (p != NULL) {
for (; *p != NUL && !got_int; ++p) {
if (*p == '\n' || *p == '\r' || *p == TAB) {
@@ -21083,10 +20368,10 @@ call_user_func (
msg_outnum((long)argvars[i].vval.v_number);
} else {
// Do not want errors such as E724 here.
- ++emsg_off;
- char_u *s = (char_u *) tv2string(&argvars[i], NULL);
+ emsg_off++;
+ char_u *s = (char_u *) encode_tv2string(&argvars[i], NULL);
char_u *tofree = s;
- --emsg_off;
+ emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -21177,10 +20462,10 @@ call_user_func (
// The value may be very long. Skip the middle part, so that we
// have some idea how it starts and ends. smsg() would always
// truncate it at the end. Don't want errors such as E724 here.
- ++emsg_off;
- char_u *s = (char_u *) tv2string(fc->rettv, NULL);
+ emsg_off++;
+ char_u *s = (char_u *) encode_tv2string(fc->rettv, NULL);
char_u *tofree = s;
- --emsg_off;
+ emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -21451,7 +20736,7 @@ char_u *get_return_cmd(void *rettv)
char_u *tofree = NULL;
if (rettv != NULL) {
- tofree = s = (char_u *) echo_string((typval_T *) rettv, NULL);
+ tofree = s = (char_u *) encode_tv2echo((typval_T *) rettv, NULL);
}
if (s == NULL) {
s = (char_u *)"";
@@ -22614,4 +21899,3 @@ static bool is_watched(dict_T *d)
{
return d && !QUEUE_EMPTY(&d->watchers);
}
-
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index f51b0f4921..d6800afd52 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -1,12 +1,17 @@
#ifndef NVIM_EVAL_H
#define NVIM_EVAL_H
-#include <msgpack.h>
-
#include "nvim/profile.h"
+#include "nvim/hashtab.h" // For hashtab_T
+#include "nvim/garray.h" // For garray_T
+#include "nvim/buffer_defs.h" // For scid_T
+#include "nvim/ex_cmds_defs.h" // For exarg_T
+
+#define COPYID_INC 2
+#define COPYID_MASK (~0x1)
// All user-defined functions are found in this hashtable.
-EXTERN hashtab_T func_hashtab;
+extern hashtab_T func_hashtab;
// Structure to hold info for a user function.
typedef struct ufunc ufunc_T;
@@ -46,8 +51,8 @@ EXTERN ufunc_T dumuf;
#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf)))
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
-/* Defines for Vim variables. These must match vimvars[] in eval.c! */
-enum {
+/// Defines for Vim variables
+typedef enum {
VV_COUNT,
VV_COUNT1,
VV_PREVCOUNT,
@@ -114,15 +119,35 @@ enum {
VV_ERRORS,
VV_MSGPACK_TYPES,
VV_EVENT,
- VV_LEN, // number of v: vars
-};
+ VV_FALSE,
+ VV_TRUE,
+ VV_NULL,
+ VV__NULL_LIST, // List with NULL value. For test purposes only.
+ VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
+} VimVarIndex;
+
+/// All recognized msgpack types
+typedef enum {
+ kMPNil,
+ kMPBoolean,
+ kMPInteger,
+ kMPFloat,
+ kMPString,
+ kMPBinary,
+ kMPArray,
+ kMPMap,
+ kMPExt,
+#define LAST_MSGPACK_TYPE kMPExt
+} MessagePackType;
+
+/// Array mapping values from MessagePackType to corresponding list pointers
+extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
+
+#undef LAST_MSGPACK_TYPE
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
-int vim_to_msgpack(msgpack_packer *const, typval_T *const,
- const char *const objname);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
#endif
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
new file mode 100644
index 0000000000..0774ef515f
--- /dev/null
+++ b/src/nvim/eval/decode.c
@@ -0,0 +1,1116 @@
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval_defs.h"
+#include "nvim/eval.h"
+#include "nvim/eval/encode.h"
+#include "nvim/ascii.h"
+#include "nvim/message.h"
+#include "nvim/charset.h" // vim_str2nr
+#include "nvim/lib/kvec.h"
+#include "nvim/vim.h" // OK, FAIL
+
+/// Helper structure for container_struct
+typedef struct {
+ size_t stack_index; ///< Index of current container in stack.
+ list_T *special_val; ///< _VAL key contents for special maps.
+ ///< When container is not a special dictionary it is
+ ///< NULL.
+ const char *s; ///< Location where container starts.
+ typval_T container; ///< Container. Either VAR_LIST, VAR_DICT or VAR_LIST
+ ///< which is _VAL from special dictionary.
+} ContainerStackItem;
+
+/// Helper structure for values struct
+typedef struct {
+ bool is_special_string; ///< Indicates that current value is a special
+ ///< dictionary with string.
+ bool didcomma; ///< True if previous token was comma.
+ bool didcolon; ///< True if previous token was colon.
+ typval_T val; ///< Actual value.
+} ValuesStackItem;
+
+/// Vector containing values not yet saved in any container
+typedef kvec_t(ValuesStackItem) ValuesStack;
+
+/// Vector containing containers, each next container is located inside previous
+typedef kvec_t(ContainerStackItem) ContainerStack;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/decode.c.generated.h"
+#endif
+
+/// Create special dictionary
+///
+/// @param[out] rettv Location where created dictionary will be saved.
+/// @param[in] type Type of the dictionary.
+/// @param[in] val Value associated with the _VAL key.
+static inline void create_special_dict(typval_T *const rettv,
+ const MessagePackType type,
+ typval_T val)
+ FUNC_ATTR_NONNULL_ALL
+{
+ dict_T *const dict = dict_alloc();
+ dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE");
+ type_di->di_tv.v_type = VAR_LIST;
+ type_di->di_tv.v_lock = VAR_UNLOCKED;
+ type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type];
+ type_di->di_tv.vval.v_list->lv_refcount++;
+ dict_add(dict, type_di);
+ dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL");
+ val_di->di_tv = val;
+ dict_add(dict, val_di);
+ dict->dv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+}
+
+#define DICT_LEN(dict) (dict)->dv_hashtab.ht_used
+
+/// Helper function used for working with stack vectors used by JSON decoder
+///
+/// @param[in,out] obj New object. Will either be put into the stack (and,
+/// probably, also inside container) or freed.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[in,out] pp Position in string which is currently being parsed. Used
+/// for error reporting and is also set when decoding is
+/// restarted due to the necessity of converting regular
+/// dictionary to a special map.
+/// @param[out] next_map_special Is set to true when dictionary needs to be
+/// converted to a special map, otherwise not
+/// touched. Indicates that decoding has been
+/// restarted.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int json_decoder_pop(ValuesStackItem obj,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ const char **const pp,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (kv_size(*container_stack) == 0) {
+ kv_push(ValuesStackItem, *stack, obj);
+ return OK;
+ }
+ ContainerStackItem last_container = kv_last(*container_stack);
+ const char *val_location = *pp;
+ if (obj.val.v_type == last_container.container.v_type
+ // vval.v_list and vval.v_dict should have the same size and offset
+ && ((void *) obj.val.vval.v_list
+ == (void *) last_container.container.vval.v_list)) {
+ (void) kv_pop(*container_stack);
+ val_location = last_container.s;
+ last_container = kv_last(*container_stack);
+ }
+ if (last_container.container.v_type == VAR_LIST) {
+ if (last_container.container.vval.v_list->lv_len != 0
+ && !obj.didcomma) {
+ EMSG2(_("E474: Expected comma before list item: %s"), val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ assert(last_container.special_val == NULL);
+ listitem_T *obj_li = listitem_alloc();
+ obj_li->li_tv = obj.val;
+ list_append(last_container.container.vval.v_list, obj_li);
+ } else if (last_container.stack_index == kv_size(*stack) - 2) {
+ if (!obj.didcolon) {
+ EMSG2(_("E474: Expected colon before dictionary value: %s"),
+ val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ ValuesStackItem key = kv_pop(*stack);
+ if (last_container.special_val == NULL) {
+ // These cases should have already been handled.
+ assert(!(key.is_special_string
+ || key.val.vval.v_string == NULL
+ || *key.val.vval.v_string == NUL));
+ dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string);
+ clear_tv(&key.val);
+ if (dict_add(last_container.container.vval.v_dict, obj_di)
+ == FAIL) {
+ assert(false);
+ }
+ obj_di->di_tv = obj.val;
+ } else {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(last_container.special_val, kv_pair);
+ listitem_T *const key_li = listitem_alloc();
+ key_li->li_tv = key.val;
+ list_append(kv_pair, key_li);
+ listitem_T *const val_li = listitem_alloc();
+ val_li->li_tv = obj.val;
+ list_append(kv_pair, val_li);
+ }
+ } else {
+ // Object with key only
+ if (!obj.is_special_string && obj.val.v_type != VAR_STRING) {
+ EMSG2(_("E474: Expected string key: %s"), *pp);
+ clear_tv(&obj.val);
+ return FAIL;
+ } else if (!obj.didcomma
+ && (last_container.special_val == NULL
+ && (DICT_LEN(last_container.container.vval.v_dict) != 0))) {
+ EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ // Handle empty key and key represented as special dictionary
+ if (last_container.special_val == NULL
+ && (obj.is_special_string
+ || obj.val.vval.v_string == NULL
+ || *obj.val.vval.v_string == NUL
+ || dict_find(last_container.container.vval.v_dict,
+ obj.val.vval.v_string, -1))) {
+ clear_tv(&obj.val);
+
+ // Restart
+ (void) kv_pop(*container_stack);
+ ValuesStackItem last_container_val =
+ kv_A(*stack, last_container.stack_index);
+ while (kv_size(*stack) > last_container.stack_index) {
+ clear_tv(&(kv_pop(*stack).val));
+ }
+ *pp = last_container.s;
+ *didcomma = last_container_val.didcomma;
+ *didcolon = last_container_val.didcolon;
+ *next_map_special = true;
+ return OK;
+ }
+ kv_push(ValuesStackItem, *stack, obj);
+ }
+ return OK;
+}
+
+#define LENP(p, e) \
+ ((int) ((e) - (p))), (p)
+#define OBJ(obj_tv, is_sp_string, didcomma_, didcolon_) \
+ ((ValuesStackItem) { \
+ .is_special_string = (is_sp_string), \
+ .val = (obj_tv), \
+ .didcomma = (didcomma_), \
+ .didcolon = (didcolon_), \
+ })
+
+#define POP(obj_tv, is_sp_string) \
+ do { \
+ if (json_decoder_pop(OBJ(obj_tv, is_sp_string, *didcomma, *didcolon), \
+ stack, container_stack, \
+ &p, next_map_special, didcomma, didcolon) \
+ == FAIL) { \
+ goto parse_json_string_fail; \
+ } \
+ if (*next_map_special) { \
+ goto parse_json_string_ret; \
+ } \
+ } while (0)
+
+/// Parse JSON double-quoted string
+///
+/// @param[in] conv Defines conversion necessary to convert UTF-8 string to
+/// &encoding.
+/// @param[in] buf Buffer being converted.
+/// @param[in] buf_len Length of the buffer.
+/// @param[in,out] pp Pointer to the start of the string. Must point to '"'.
+/// Is advanced to the closing '"'. Also see
+/// json_decoder_pop(), it may set pp to another location
+/// and alter next_map_special, didcomma and didcolon.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[out] next_map_special Is set to true when dictionary is converted
+/// to a special map, otherwise not touched.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int parse_json_string(vimconv_T *const conv,
+ const char *const buf, const size_t buf_len,
+ const char **const pp,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *const e = buf + buf_len;
+ const char *p = *pp;
+ size_t len = 0;
+ const char *const s = ++p;
+ int ret = OK;
+ while (p < e && *p != '"') {
+ if (*p == '\\') {
+ p++;
+ if (p == e) {
+ emsgf(_("E474: Unfinished escape sequence: %.*s"),
+ (int) buf_len, buf);
+ goto parse_json_string_fail;
+ }
+ switch (*p) {
+ case 'u': {
+ if (p + 4 >= e) {
+ emsgf(_("E474: Unfinished unicode escape sequence: %.*s"),
+ (int) buf_len, buf);
+ goto parse_json_string_fail;
+ } else if (!ascii_isxdigit(p[1])
+ || !ascii_isxdigit(p[2])
+ || !ascii_isxdigit(p[3])
+ || !ascii_isxdigit(p[4])) {
+ emsgf(_("E474: Expected four hex digits after \\u: %.*s"),
+ LENP(p - 1, e));
+ goto parse_json_string_fail;
+ }
+ // One UTF-8 character below U+10000 can take up to 3 bytes,
+ // above up to 6, but they are encoded using two \u escapes.
+ len += 3;
+ p += 5;
+ break;
+ }
+ case '\\':
+ case '/':
+ case '"':
+ case 't':
+ case 'b':
+ case 'n':
+ case 'r':
+ case 'f': {
+ len++;
+ p++;
+ break;
+ }
+ default: {
+ emsgf(_("E474: Unknown escape sequence: %.*s"), LENP(p - 1, e));
+ goto parse_json_string_fail;
+ }
+ }
+ } else {
+ uint8_t p_byte = (uint8_t) *p;
+ // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ if (p_byte < 0x20) {
+ emsgf(_("E474: ASCII control characters cannot be present "
+ "inside string: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ }
+ const int ch = utf_ptr2char((char_u *) p);
+ // All characters above U+007F are encoded using two or more bytes
+ // and thus cannot possibly be equal to *p. But utf_ptr2char({0xFF,
+ // 0}) will return 0xFF, even though 0xFF cannot start any UTF-8
+ // code point at all.
+ //
+ // The only exception is U+00C3 which is represented as 0xC3 0x83.
+ if (ch >= 0x80 && p_byte == ch
+ && !(ch == 0xC3 && p + 1 < e && (uint8_t) p[1] == 0x83)) {
+ emsgf(_("E474: Only UTF-8 strings allowed: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ } else if (ch > 0x10FFFF) {
+ emsgf(_("E474: Only UTF-8 code points up to U+10FFFF "
+ "are allowed to appear unescaped: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ }
+ const size_t ch_len = (size_t) utf_char2len(ch);
+ assert(ch_len == (size_t) (ch ? utf_ptr2len((char_u *) p) : 1));
+ len += ch_len;
+ p += ch_len;
+ }
+ }
+ if (p == e || *p != '"') {
+ emsgf(_("E474: Expected string end: %.*s"), (int) buf_len, buf);
+ goto parse_json_string_fail;
+ }
+ if (len == 0) {
+ POP(((typval_T) {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = NULL },
+ }), false);
+ goto parse_json_string_ret;
+ }
+ char *str = xmalloc(len + 1);
+ int fst_in_pair = 0;
+ char *str_end = str;
+ bool hasnul = false;
+#define PUT_FST_IN_PAIR(fst_in_pair, str_end) \
+ do { \
+ if (fst_in_pair != 0) { \
+ str_end += utf_char2bytes(fst_in_pair, (char_u *) str_end); \
+ fst_in_pair = 0; \
+ } \
+ } while (0)
+ for (const char *t = s; t < p; t++) {
+ if (t[0] != '\\' || t[1] != 'u') {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ }
+ if (*t == '\\') {
+ t++;
+ switch (*t) {
+ case 'u': {
+ const char ubuf[] = { t[1], t[2], t[3], t[4] };
+ t += 4;
+ unsigned long ch;
+ vim_str2nr((char_u *) ubuf, NULL, NULL,
+ STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4);
+ if (ch == 0) {
+ hasnul = true;
+ }
+ if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ fst_in_pair = (int) ch;
+ } else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END
+ && fst_in_pair != 0) {
+ const int full_char = (
+ (int) (ch - SURROGATE_LO_START)
+ + ((fst_in_pair - SURROGATE_HI_START) << 10)
+ + SURROGATE_FIRST_CHAR);
+ str_end += utf_char2bytes(full_char, (char_u *) str_end);
+ fst_in_pair = 0;
+ } else {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ str_end += utf_char2bytes((int) ch, (char_u *) str_end);
+ }
+ break;
+ }
+ case '\\':
+ case '/':
+ case '"':
+ case 't':
+ case 'b':
+ case 'n':
+ case 'r':
+ case 'f': {
+ static const char escapes[] = {
+ ['\\'] = '\\',
+ ['/'] = '/',
+ ['"'] = '"',
+ ['t'] = TAB,
+ ['b'] = BS,
+ ['n'] = NL,
+ ['r'] = CAR,
+ ['f'] = FF,
+ };
+ *str_end++ = escapes[(int) *t];
+ break;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ } else {
+ *str_end++ = *t;
+ }
+ }
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+#undef PUT_FST_IN_PAIR
+ if (conv->vc_type != CONV_NONE) {
+ size_t str_len = (size_t) (str_end - str);
+ char *const new_str = (char *) string_convert(conv, (char_u *) str,
+ &str_len);
+ if (new_str == NULL) {
+ emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"),
+ (int) str_len, str);
+ xfree(str);
+ goto parse_json_string_fail;
+ }
+ xfree(str);
+ str = new_str;
+ str_end = new_str + str_len;
+ }
+ if (hasnul) {
+ typval_T obj;
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(&obj, kMPString, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, str, (size_t) (str_end - str))
+ == -1) {
+ clear_tv(&obj);
+ goto parse_json_string_fail;
+ }
+ xfree(str);
+ POP(obj, true);
+ } else {
+ *str_end = NUL;
+ POP(((typval_T) {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (char_u *) str },
+ }), false);
+ }
+ goto parse_json_string_ret;
+parse_json_string_fail:
+ ret = FAIL;
+parse_json_string_ret:
+ *pp = p;
+ return ret;
+}
+
+#undef POP
+
+/// Parse JSON number: both floating-point and integer
+///
+/// Number format: `-?\d+(?:.\d+)?(?:[eE][+-]?\d+)?`.
+///
+/// @param[in] buf Buffer being converted.
+/// @param[in] buf_len Length of the buffer.
+/// @param[in,out] pp Pointer to the start of the number. Must point to
+/// a digit or a minus sign. Is advanced to the last
+/// character of the number. Also see json_decoder_pop(), it
+/// may set pp to another location and alter
+/// next_map_special, didcomma and didcolon.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[out] next_map_special Is set to true when dictionary is converted
+/// to a special map, otherwise not touched.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int parse_json_number(const char *const buf, const size_t buf_len,
+ const char **const pp,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *const e = buf + buf_len;
+ const char *p = *pp;
+ int ret = OK;
+ const char *const s = p;
+ const char *ints = NULL;
+ const char *fracs = NULL;
+ const char *exps = NULL;
+ const char *exps_s = NULL;
+ if (*p == '-') {
+ p++;
+ }
+ ints = p;
+ if (p >= e) {
+ goto parse_json_number_check;
+ }
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ if (p != ints + 1 && *ints == '0') {
+ emsgf(_("E474: Leading zeroes are not allowed: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ }
+ if (p >= e || p == ints) {
+ goto parse_json_number_check;
+ }
+ if (*p == '.') {
+ p++;
+ fracs = p;
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ if (p >= e || p == fracs) {
+ goto parse_json_number_check;
+ }
+ }
+ if (*p == 'e' || *p == 'E') {
+ p++;
+ exps_s = p;
+ if (p < e && (*p == '-' || *p == '+')) {
+ p++;
+ }
+ exps = p;
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ }
+parse_json_number_check:
+ if (p == ints) {
+ emsgf(_("E474: Missing number after minus sign: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ } else if (p == fracs || exps_s == fracs + 1) {
+ emsgf(_("E474: Missing number after decimal dot: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ } else if (p == exps) {
+ emsgf(_("E474: Missing exponent: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ }
+ typval_T tv = {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ };
+ const size_t exp_num_len = (size_t) (p - s);
+ if (fracs || exps) {
+ // Convert floating-point number
+ const size_t num_len = string2float(s, &tv.vval.v_float);
+ if (exp_num_len != num_len) {
+ emsgf(_("E685: internal error: while converting number \"%.*s\" "
+ "to float string2float consumed %zu bytes in place of %zu"),
+ (int) exp_num_len, s, num_len, exp_num_len);
+ }
+ tv.v_type = VAR_FLOAT;
+ } else {
+ // Convert integer
+ long nr;
+ int num_len;
+ vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s));
+ if ((int) exp_num_len != num_len) {
+ emsgf(_("E685: internal error: while converting number \"%.*s\" "
+ "to integer vim_str2nr consumed %i bytes in place of %zu"),
+ (int) exp_num_len, s, num_len, exp_num_len);
+ }
+ tv.vval.v_number = (varnumber_T) nr;
+ }
+ if (json_decoder_pop(OBJ(tv, false, *didcomma, *didcolon),
+ stack, container_stack,
+ &p, next_map_special, didcomma, didcolon) == FAIL) {
+ goto parse_json_number_fail;
+ }
+ if (*next_map_special) {
+ goto parse_json_number_ret;
+ }
+ p--;
+ goto parse_json_number_ret;
+parse_json_number_fail:
+ ret = FAIL;
+parse_json_number_ret:
+ *pp = p;
+ return ret;
+}
+
+#define POP(obj_tv, is_sp_string) \
+ do { \
+ if (json_decoder_pop(OBJ(obj_tv, is_sp_string, didcomma, didcolon), \
+ &stack, &container_stack, \
+ &p, &next_map_special, &didcomma, &didcolon) \
+ == FAIL) { \
+ goto json_decode_string_fail; \
+ } \
+ if (next_map_special) { \
+ goto json_decode_string_cycle_start; \
+ } \
+ } while (0)
+
+/// Convert JSON string into VimL object
+///
+/// @param[in] buf String to convert. UTF-8 encoding is assumed.
+/// @param[in] buf_len Length of the string.
+/// @param[out] rettv Location where to save results.
+///
+/// @return OK in case of success, FAIL otherwise.
+int json_decode_string(const char *const buf, const size_t buf_len,
+ typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const char *p = buf;
+ const char *const e = buf + buf_len;
+ while (p < e && (*p == ' ' || *p == TAB || *p == NL || *p == CAR)) {
+ p++;
+ }
+ if (p == e) {
+ EMSG(_("E474: Attempt to decode a blank string"));
+ return FAIL;
+ }
+ vimconv_T conv = { .vc_type = CONV_NONE };
+ convert_setup(&conv, (char_u *) "utf-8", p_enc);
+ conv.vc_fail = true;
+ int ret = OK;
+ ValuesStack stack;
+ kv_init(stack);
+ ContainerStack container_stack;
+ kv_init(container_stack);
+ rettv->v_type = VAR_UNKNOWN;
+ bool didcomma = false;
+ bool didcolon = false;
+ bool next_map_special = false;
+ for (; p < e; p++) {
+json_decode_string_cycle_start:
+ assert(*p == '{' || next_map_special == false);
+ switch (*p) {
+ case '}':
+ case ']': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: No container to close: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (*p == '}' && last_container.container.v_type != VAR_DICT) {
+ emsgf(_("E474: Closing list with curly bracket: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (*p == ']' && last_container.container.v_type != VAR_LIST) {
+ emsgf(_("E474: Closing dictionary with square bracket: %.*s"),
+ LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcomma) {
+ emsgf(_("E474: Trailing comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Expected value after colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.stack_index != kv_size(stack) - 1) {
+ assert(last_container.stack_index < kv_size(stack) - 1);
+ emsgf(_("E474: Expected value: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ if (kv_size(stack) == 1) {
+ p++;
+ (void) kv_pop(container_stack);
+ goto json_decode_string_after_cycle;
+ } else {
+ if (json_decoder_pop(kv_pop(stack), &stack, &container_stack, &p,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ goto json_decode_string_fail;
+ }
+ assert(!next_map_special);
+ break;
+ }
+ }
+ case ',': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: Comma not inside container: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (didcomma) {
+ emsgf(_("E474: Duplicate comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Comma after colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.container.v_type == VAR_DICT
+ && last_container.stack_index != kv_size(stack) - 1) {
+ emsgf(_("E474: Using comma in place of colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.special_val == NULL
+ ? (last_container.container.v_type == VAR_DICT
+ ? (DICT_LEN(last_container.container.vval.v_dict) == 0)
+ : (last_container.container.vval.v_list->lv_len == 0))
+ : (last_container.special_val->lv_len == 0)) {
+ emsgf(_("E474: Leading comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ didcomma = true;
+ continue;
+ }
+ case ':': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: Colon not inside container: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (last_container.container.v_type != VAR_DICT) {
+ emsgf(_("E474: Using colon not in dictionary: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.stack_index != kv_size(stack) - 2) {
+ emsgf(_("E474: Unexpected colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcomma) {
+ emsgf(_("E474: Colon after comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Duplicate colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ didcolon = true;
+ continue;
+ }
+ case ' ':
+ case TAB:
+ case NL:
+ case CAR: {
+ continue;
+ }
+ case 'n': {
+ if ((p + 3) >= e || strncmp(p + 1, "ull", 3) != 0) {
+ emsgf(_("E474: Expected null: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 3;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarNull },
+ }), false);
+ break;
+ }
+ case 't': {
+ if ((p + 3) >= e || strncmp(p + 1, "rue", 3) != 0) {
+ emsgf(_("E474: Expected true: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 3;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarTrue },
+ }), false);
+ break;
+ }
+ case 'f': {
+ if ((p + 4) >= e || strncmp(p + 1, "alse", 4) != 0) {
+ emsgf(_("E474: Expected false: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 4;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarFalse },
+ }), false);
+ break;
+ }
+ case '"': {
+ if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ // Error message was already given
+ goto json_decode_string_fail;
+ }
+ if (next_map_special) {
+ goto json_decode_string_cycle_start;
+ }
+ break;
+ }
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ if (parse_json_number(buf, buf_len, &p, &stack, &container_stack,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ // Error message was already given
+ goto json_decode_string_fail;
+ }
+ if (next_map_special) {
+ goto json_decode_string_cycle_start;
+ }
+ break;
+ }
+ case '[': {
+ list_T *list = list_alloc();
+ list->lv_refcount++;
+ typval_T tv = {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ };
+ kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) {
+ .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = NULL,
+ }));
+ kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon));
+ break;
+ }
+ case '{': {
+ typval_T tv;
+ list_T *val_list = NULL;
+ if (next_map_special) {
+ next_map_special = false;
+ val_list = list_alloc();
+ val_list->lv_refcount++;
+ create_special_dict(&tv, kMPMap, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = val_list },
+ }));
+ } else {
+ dict_T *dict = dict_alloc();
+ dict->dv_refcount++;
+ tv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+ }
+ kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) {
+ .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = val_list,
+ }));
+ kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon));
+ break;
+ }
+ default: {
+ emsgf(_("E474: Unidentified byte: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ }
+ didcomma = false;
+ didcolon = false;
+ if (kv_size(container_stack) == 0) {
+ p++;
+ break;
+ }
+ }
+json_decode_string_after_cycle:
+ for (; p < e; p++) {
+ switch (*p) {
+ case NL:
+ case ' ':
+ case TAB:
+ case CAR: {
+ break;
+ }
+ default: {
+ emsgf(_("E474: Trailing characters: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ }
+ }
+ if (kv_size(stack) == 1 && kv_size(container_stack) == 0) {
+ *rettv = kv_pop(stack).val;
+ goto json_decode_string_ret;
+ }
+ emsgf(_("E474: Unexpected end of input: %.*s"), (int) buf_len, buf);
+json_decode_string_fail:
+ ret = FAIL;
+ while (kv_size(stack)) {
+ clear_tv(&(kv_pop(stack).val));
+ }
+json_decode_string_ret:
+ kv_destroy(stack);
+ kv_destroy(container_stack);
+ return ret;
+}
+
+#undef LENP
+#undef POP
+
+#undef OBJ
+
+#undef DICT_LEN
+
+/// Convert msgpack object to a VimL one
+int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ switch (mobj.type) {
+ case MSGPACK_OBJECT_NIL: {
+ *rettv = (typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarNull },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_BOOLEAN: {
+ *rettv = (typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = {
+ .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse
+ },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_POSITIVE_INTEGER: {
+ if (mobj.via.u64 <= VARNUMBER_MAX) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_number = (varnumber_T) mobj.via.u64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPInteger, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = mobj.via.u64;
+ list_append_number(list, 1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
+ if (mobj.via.i64 >= VARNUMBER_MIN) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_number = (varnumber_T) mobj.via.i64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPInteger, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = -((uint64_t) mobj.via.i64);
+ list_append_number(list, -1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_FLOAT: {
+ *rettv = (typval_T) {
+ .v_type = VAR_FLOAT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_float = mobj.via.f64 },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_STR: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPString, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_BIN: {
+ if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
+ *rettv = (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
+ };
+ break;
+ }
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPBinary, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_ARRAY: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ };
+ for (size_t i = 0; i < mobj.via.array.size; i++) {
+ listitem_T *const li = listitem_alloc();
+ li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(list, li);
+ if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_MAP: {
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
+ || mobj.via.map.ptr[i].key.via.str.size == 0
+ || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
+ mobj.via.map.ptr[i].key.via.str.size) != NULL) {
+ goto msgpack_to_vim_generic_map;
+ }
+ }
+ dict_T *const dict = dict_alloc();
+ dict->dv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
+ + mobj.via.map.ptr[i].key.via.str.size);
+ memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr,
+ mobj.via.map.ptr[i].key.via.str.size);
+ di->di_tv.v_type = VAR_UNKNOWN;
+ if (dict_add(dict, di) == FAIL) {
+ // Duplicate key: fallback to generic map
+ clear_tv(rettv);
+ xfree(di);
+ goto msgpack_to_vim_generic_map;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+msgpack_to_vim_generic_map: {}
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPMap, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(list, kv_pair);
+ listitem_T *const key_li = listitem_alloc();
+ key_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, key_li);
+ listitem_T *const val_li = listitem_alloc();
+ val_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, val_li);
+ if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_EXT: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ list_append_number(list, mobj.via.ext.type);
+ list_T *const ext_val_list = list_alloc();
+ list_append_list(list, ext_val_list);
+ create_special_dict(rettv, kMPExt, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) ext_val_list, mobj.via.ext.ptr,
+ mobj.via.ext.size) == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ }
+ return OK;
+}
diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h
new file mode 100644
index 0000000000..5c25a64f7a
--- /dev/null
+++ b/src/nvim/eval/decode.h
@@ -0,0 +1,13 @@
+#ifndef NVIM_EVAL_DECODE_H
+#define NVIM_EVAL_DECODE_H
+
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/decode.h.generated.h"
+#endif
+#endif // NVIM_EVAL_DECODE_H
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
new file mode 100644
index 0000000000..c651a50be9
--- /dev/null
+++ b/src/nvim/eval/encode.c
@@ -0,0 +1,1296 @@
+/// @file encode.c
+///
+/// File containing functions for encoding and decoding VimL values.
+///
+/// Split out from eval.c.
+
+#include <msgpack.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <math.h>
+
+#include "nvim/eval/encode.h"
+#include "nvim/buffer_defs.h" // vimconv_T
+#include "nvim/eval.h"
+#include "nvim/eval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/mbyte.h"
+#include "nvim/message.h"
+#include "nvim/memory.h"
+#include "nvim/charset.h" // vim_isprintc()
+#include "nvim/macros.h"
+#include "nvim/ascii.h"
+#include "nvim/vim.h" // For _()
+#include "nvim/lib/kvec.h"
+
+#define ga_concat(a, b) ga_concat(a, (char_u *)b)
+#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
+#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
+#define utf_char2len(b) ((size_t)utf_char2len(b))
+#define string_convert(a, b, c) \
+ ((char *)string_convert((vimconv_T *)a, (char_u *)b, c))
+#define convert_setup(vcp, from, to) \
+ (convert_setup(vcp, (char_u *)from, (char_u *)to))
+
+/// Structure representing current VimL to messagepack conversion state
+typedef struct {
+ enum {
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
+ kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
+ } type;
+ union {
+ struct {
+ dict_T *dict; ///< Currently converted dictionary.
+ hashitem_T *hi; ///< Currently converted dictionary item.
+ size_t todo; ///< Amount of items left to process.
+ } d; ///< State of dictionary conversion.
+ struct {
+ list_T *list; ///< Currently converted list.
+ listitem_T *li; ///< Currently converted list item.
+ } l; ///< State of list or generic mapping conversion.
+ } data; ///< Data to convert.
+} MPConvStackVal;
+
+/// Stack used to convert VimL values to messagepack.
+typedef kvec_t(MPConvStackVal) MPConvStack;
+
+const char *const encode_special_var_names[] = {
+ [kSpecialVarNull] = "null",
+ [kSpecialVarTrue] = "true",
+ [kSpecialVarFalse] = "false",
+};
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/encode.c.generated.h"
+#endif
+
+/// Msgpack callback for writing to readfile()-style list
+int encode_list_write(void *data, const char *buf, size_t len)
+{
+ if (len == 0) {
+ return 0;
+ }
+ list_T *const list = (list_T *) data;
+ const char *const end = buf + len;
+ const char *line_end = buf;
+ listitem_T *li = list->lv_last;
+
+ // Continue the last list element
+ if (li != NULL) {
+ line_end = xmemscan(buf, NL, len);
+ if (line_end != buf) {
+ const size_t line_length = (size_t)(line_end - buf);
+ char *str = (char *)li->li_tv.vval.v_string;
+ const size_t li_len = (str == NULL ? 0 : strlen(str));
+ li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1);
+ str = (char *)li->li_tv.vval.v_string + li_len;
+ memcpy(str, buf, line_length);
+ str[line_length] = 0;
+ memchrsub(str, NUL, NL, line_length);
+ }
+ line_end++;
+ }
+
+ while (line_end < end) {
+ const char *line_start = line_end;
+ line_end = xmemscan(line_start, NL, (size_t) (end - line_start));
+ char *str = NULL;
+ if (line_end != line_start) {
+ const size_t line_length = (size_t)(line_end - line_start);
+ str = xmemdupz(line_start, line_length);
+ memchrsub(str, NUL, NL, line_length);
+ }
+ list_append_allocated_string(list, str);
+ line_end++;
+ }
+ if (line_end == end) {
+ list_append_allocated_string(list, NULL);
+ }
+ return 0;
+}
+
+/// Abort conversion to string after a recursion error.
+static bool did_echo_string_emsg = false;
+
+/// Show a error message when converting to msgpack value
+///
+/// @param[in] msg Error message to dump. Must contain exactly two %s that
+/// will be replaced with what was being dumped: first with
+/// something like “F†or “function argumentâ€, second with path
+/// to the failed value.
+/// @param[in] mpstack Path to the failed value.
+/// @param[in] objname Dumped object name.
+///
+/// @return FAIL.
+static int conv_error(const char *const msg, const MPConvStack *const mpstack,
+ const char *const objname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T msg_ga;
+ ga_init(&msg_ga, (int)sizeof(char), 80);
+ char *const key_msg = _("key %s");
+ char *const key_pair_msg = _("key %s at index %i from special map");
+ char *const idx_msg = _("index %i");
+ for (size_t i = 0; i < kv_size(*mpstack); i++) {
+ if (i != 0) {
+ ga_concat(&msg_ga, ", ");
+ }
+ MPConvStackVal v = kv_A(*mpstack, i);
+ switch (v.type) {
+ case kMPConvDict: {
+ typval_T key_tv = {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (v.data.d.hi == NULL
+ ? v.data.d.dict->dv_hashtab.ht_array
+ : (v.data.d.hi - 1))->hi_key },
+ };
+ char *const key = encode_tv2string(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ break;
+ }
+ case kMPConvPairs:
+ case kMPConvList: {
+ int idx = 0;
+ const listitem_T *li;
+ for (li = v.data.l.list->lv_first;
+ li != NULL && li->li_next != v.data.l.li;
+ li = li->li_next) {
+ idx++;
+ }
+ if (v.type == kMPConvList
+ || li == NULL
+ || (li->li_tv.v_type != VAR_LIST
+ && li->li_tv.vval.v_list->lv_len <= 0)) {
+ vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
+ ga_concat(&msg_ga, IObuff);
+ } else {
+ typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
+ char *const key = encode_tv2echo(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ }
+ break;
+ }
+ }
+ }
+ EMSG3(msg, objname, (kv_size(*mpstack) == 0
+ ? _("itself")
+ : (char *) msg_ga.ga_data));
+ ga_clear(&msg_ga);
+ return FAIL;
+}
+
+/// Convert readfile()-style list to a char * buffer with length
+///
+/// @param[in] list Converted list.
+/// @param[out] ret_len Resulting buffer length.
+/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
+/// zero.
+///
+/// @return true in case of success, false in case of failure.
+bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
+ char **const ret_buf)
+ FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ size_t len = 0;
+ if (list != NULL) {
+ for (const listitem_T *li = list->lv_first;
+ li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_STRING) {
+ return false;
+ }
+ len++;
+ if (li->li_tv.vval.v_string != 0) {
+ len += STRLEN(li->li_tv.vval.v_string);
+ }
+ }
+ if (len) {
+ len--;
+ }
+ }
+ *ret_len = len;
+ if (len == 0) {
+ *ret_buf = NULL;
+ return true;
+ }
+ ListReaderState lrstate = encode_init_lrstate(list);
+ char *const buf = xmalloc(len);
+ size_t read_bytes;
+ if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
+ assert(false);
+ }
+ assert(len == read_bytes);
+ *ret_buf = buf;
+ return true;
+}
+
+/// Read bytes from list
+///
+/// @param[in,out] state Structure describing position in list from which
+/// reading should start. Is updated to reflect position
+/// at which reading ended.
+/// @param[out] buf Buffer to write to.
+/// @param[in] nbuf Buffer length.
+/// @param[out] read_bytes Is set to amount of bytes read.
+///
+/// @return OK when reading was finished, FAIL in case of error (i.e. list item
+/// was not a string), NOTDONE if reading was successfull, but there are
+/// more bytes to read.
+int encode_read_from_list(ListReaderState *const state, char *const buf,
+ const size_t nbuf, size_t *const read_bytes)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char *const buf_end = buf + nbuf;
+ char *p = buf;
+ while (p < buf_end) {
+ for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
+ const char ch = (char) state->li->li_tv.vval.v_string[state->offset++];
+ *p++ = (char) ((char) ch == (char) NL ? (char) NUL : (char) ch);
+ }
+ if (p < buf_end) {
+ state->li = state->li->li_next;
+ if (state->li == NULL) {
+ *read_bytes = (size_t) (p - buf);
+ return OK;
+ }
+ *p++ = NL;
+ if (state->li->li_tv.v_type != VAR_STRING) {
+ *read_bytes = (size_t) (p - buf);
+ return FAIL;
+ }
+ state->offset = 0;
+ state->li_length = (state->li->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(state->li->li_tv.vval.v_string));
+ }
+ }
+ *read_bytes = nbuf;
+ return (state->offset < state->li_length || state->li->li_next != NULL
+ ? NOTDONE
+ : OK);
+}
+
+/// Code for checking whether container references itself
+///
+/// @param[in,out] val Container to check.
+/// @param copyID_attr Name of the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
+ do { \
+ if ((val)->copyID_attr == copyID) { \
+ CONV_RECURSE((val), conv_type); \
+ } \
+ (val)->copyID_attr = copyID; \
+ } while (0)
+
+#define TV_STRLEN(tv) \
+ (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string))
+
+/// Define functions which convert VimL value to something else
+///
+/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
+/// tv)` which returns OK or FAIL and helper functions.
+///
+/// @param firstargtype Type of the first argument. It will be used to return
+/// the results.
+/// @param firstargname Name of the first argument.
+/// @param name Name of the target converter.
+#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
+static int name##_convert_one_value(firstargtype firstargname, \
+ MPConvStack *const mpstack, \
+ typval_T *const tv, \
+ const int copyID, \
+ const char *const objname) \
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
+{ \
+ switch (tv->v_type) { \
+ case VAR_STRING: { \
+ CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \
+ break; \
+ } \
+ case VAR_NUMBER: { \
+ CONV_NUMBER(tv->vval.v_number); \
+ break; \
+ } \
+ case VAR_FLOAT: { \
+ CONV_FLOAT(tv->vval.v_float); \
+ break; \
+ } \
+ case VAR_FUNC: { \
+ CONV_FUNC(tv->vval.v_string); \
+ break; \
+ } \
+ case VAR_LIST: { \
+ if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
+ CONV_EMPTY_LIST(); \
+ break; \
+ } \
+ CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \
+ CONV_LIST_START(tv->vval.v_list); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvList, \
+ .data = { \
+ .l = { \
+ .list = tv->vval.v_list, \
+ .li = tv->vval.v_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case VAR_SPECIAL: { \
+ switch (tv->vval.v_special) { \
+ case kSpecialVarNull: { \
+ CONV_NIL(); \
+ break; \
+ } \
+ case kSpecialVarTrue: \
+ case kSpecialVarFalse: { \
+ CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
+ break; \
+ } \
+ } \
+ break; \
+ } \
+ case VAR_DICT: { \
+ if (tv->vval.v_dict == NULL \
+ || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
+ CONV_EMPTY_DICT(); \
+ break; \
+ } \
+ const dictitem_T *type_di; \
+ const dictitem_T *val_di; \
+ if (CONV_ALLOW_SPECIAL \
+ && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
+ && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
+ (char_u *) "_TYPE", -1)) != NULL \
+ && type_di->di_tv.v_type == VAR_LIST \
+ && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
+ (char_u *) "_VAL", -1)) != NULL) { \
+ size_t i; \
+ for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
+ if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
+ break; \
+ } \
+ } \
+ if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ switch ((MessagePackType) i) { \
+ case kMPNil: { \
+ CONV_NIL(); \
+ break; \
+ } \
+ case kMPBoolean: { \
+ if (val_di->di_tv.v_type != VAR_NUMBER) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_BOOL(val_di->di_tv.vval.v_number); \
+ break; \
+ } \
+ case kMPInteger: { \
+ const list_T *val_list; \
+ varnumber_T sign; \
+ varnumber_T highest_bits; \
+ varnumber_T high_bits; \
+ varnumber_T low_bits; \
+ /* List of 4 integers; first is signed (should be 1 or -1, but */ \
+ /* this is not checked), second is unsigned and have at most */ \
+ /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
+ /* bits is not checked), other unsigned and have at most 31 */ \
+ /* non-zero bits (number of bits is not checked).*/ \
+ if (val_di->di_tv.v_type != VAR_LIST \
+ || (val_list = val_di->di_tv.vval.v_list) == NULL \
+ || val_list->lv_len != 4 \
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
+ || (highest_bits = \
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
+ || (high_bits = \
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
+ | (uint64_t) (((uint64_t) high_bits) << 31) \
+ | (uint64_t) low_bits); \
+ if (sign > 0) { \
+ CONV_UNSIGNED_NUMBER(number); \
+ } else { \
+ CONV_NUMBER(-number); \
+ } \
+ break; \
+ } \
+ case kMPFloat: { \
+ if (val_di->di_tv.v_type != VAR_FLOAT) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_FLOAT(val_di->di_tv.vval.v_float); \
+ break; \
+ } \
+ case kMPString: \
+ case kMPBinary: { \
+ const bool is_string = ((MessagePackType) i == kMPString); \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ size_t len; \
+ char *buf; \
+ if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
+ &buf)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ if (is_string) { \
+ CONV_STR_STRING(buf, len); \
+ } else { \
+ CONV_STRING(buf, len); \
+ } \
+ xfree(buf); \
+ break; \
+ } \
+ case kMPArray: { \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \
+ kMPConvList); \
+ CONV_LIST_START(val_di->di_tv.vval.v_list); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvList, \
+ .data = { \
+ .l = { \
+ .list = val_di->di_tv.vval.v_list, \
+ .li = val_di->di_tv.vval.v_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case kMPMap: { \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ list_T *const val_list = val_di->di_tv.vval.v_list; \
+ if (val_list == NULL || val_list->lv_len == 0) { \
+ CONV_EMPTY_DICT(); \
+ break; \
+ } \
+ for (const listitem_T *li = val_list->lv_first; li != NULL; \
+ li = li->li_next) { \
+ if (li->li_tv.v_type != VAR_LIST \
+ || li->li_tv.vval.v_list->lv_len != 2) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ } \
+ CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \
+ CONV_DICT_START(val_list->lv_len); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvPairs, \
+ .data = { \
+ .l = { \
+ .list = val_list, \
+ .li = val_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case kMPExt: { \
+ const list_T *val_list; \
+ varnumber_T type; \
+ if (val_di->di_tv.v_type != VAR_LIST \
+ || (val_list = val_di->di_tv.vval.v_list) == NULL \
+ || val_list->lv_len != 2 \
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
+ || type < INT8_MIN \
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ size_t len; \
+ char *buf; \
+ if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
+ &len, &buf)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_EXT_STRING(buf, len, type); \
+ xfree(buf); \
+ break; \
+ } \
+ } \
+ break; \
+ } \
+name##_convert_one_value_regular_dict: \
+ CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \
+ CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvDict, \
+ .data = { \
+ .d = { \
+ .dict = tv->vval.v_dict, \
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case VAR_UNKNOWN: { \
+ EMSG2(_(e_intern2), #name "_convert_one_value()"); \
+ return FAIL; \
+ } \
+ } \
+ return OK; \
+} \
+\
+scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
+ const char *const objname) \
+ FUNC_ATTR_WARN_UNUSED_RESULT \
+{ \
+ const int copyID = get_copyID(); \
+ MPConvStack mpstack; \
+ kv_init(mpstack); \
+ if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
+ == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ while (kv_size(mpstack)) { \
+ MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
+ typval_T *cur_tv = NULL; \
+ switch (cur_mpsv->type) { \
+ case kMPConvDict: { \
+ if (!cur_mpsv->data.d.todo) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
+ CONV_DICT_END(); \
+ continue; \
+ } else if (cur_mpsv->data.d.todo \
+ != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
+ CONV_DICT_BETWEEN_ITEMS(); \
+ } \
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
+ cur_mpsv->data.d.hi++; \
+ } \
+ dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
+ cur_mpsv->data.d.todo--; \
+ cur_mpsv->data.d.hi++; \
+ CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \
+ CONV_DICT_AFTER_KEY(); \
+ cur_tv = &di->di_tv; \
+ break; \
+ } \
+ case kMPConvList: { \
+ if (cur_mpsv->data.l.li == NULL) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
+ CONV_LIST_END(cur_mpsv->data.l.list); \
+ continue; \
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
+ CONV_LIST_BETWEEN_ITEMS(); \
+ } \
+ cur_tv = &cur_mpsv->data.l.li->li_tv; \
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
+ break; \
+ } \
+ case kMPConvPairs: { \
+ if (cur_mpsv->data.l.li == NULL) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
+ CONV_DICT_END(); \
+ continue; \
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
+ CONV_DICT_BETWEEN_ITEMS(); \
+ } \
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
+ CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \
+ if (name##_convert_one_value(firstargname, &mpstack, \
+ &kv_pair->lv_first->li_tv, copyID, \
+ objname) == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ CONV_DICT_AFTER_KEY(); \
+ cur_tv = &kv_pair->lv_last->li_tv; \
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
+ break; \
+ } \
+ } \
+ assert(cur_tv != NULL); \
+ if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
+ objname) == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ } \
+ kv_destroy(mpstack); \
+ return OK; \
+encode_vim_to_##name##_error_ret: \
+ kv_destroy(mpstack); \
+ return FAIL; \
+}
+
+#define CONV_STRING(buf, len) \
+ do { \
+ const char *const buf_ = (const char *) buf; \
+ if (buf == NULL) { \
+ ga_concat(gap, "''"); \
+ } else { \
+ const size_t len_ = (len); \
+ ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \
+ ga_append(gap, '\''); \
+ for (size_t i = 0; i < len_; i++) { \
+ if (buf_[i] == '\'') { \
+ ga_append(gap, '\''); \
+ } \
+ ga_append(gap, buf_[i]); \
+ } \
+ ga_append(gap, '\''); \
+ } \
+ } while (0)
+
+#define CONV_STR_STRING(buf, len) \
+ CONV_STRING(buf, len)
+
+#define CONV_EXT_STRING(buf, len, type)
+
+#define CONV_NUMBER(num) \
+ do { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
+ ga_concat(gap, numbuf); \
+ } while (0)
+
+#define CONV_FLOAT(flt) \
+ do { \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ ga_concat(gap, (char_u *) "str2float('nan')"); \
+ break; \
+ } \
+ case FP_INFINITE: { \
+ if (flt_ < 0) { \
+ ga_append(gap, '-'); \
+ } \
+ ga_concat(gap, (char_u *) "str2float('inf')"); \
+ break; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ } \
+ } \
+ } while (0)
+
+#define CONV_FUNC(fun) \
+ do { \
+ ga_concat(gap, "function("); \
+ CONV_STRING(fun, STRLEN(fun)); \
+ ga_append(gap, ')'); \
+ } while (0)
+
+#define CONV_EMPTY_LIST() \
+ ga_concat(gap, "[]")
+
+#define CONV_LIST_START(lst) \
+ ga_append(gap, '[')
+
+#define CONV_EMPTY_DICT() \
+ ga_concat(gap, "{}")
+
+#define CONV_NIL() \
+ ga_concat(gap, "v:null")
+
+#define CONV_BOOL(num) \
+ ga_concat(gap, ((num)? "v:true": "v:false"))
+
+#define CONV_UNSIGNED_NUMBER(num)
+
+#define CONV_DICT_START(len) \
+ ga_append(gap, '{')
+
+#define CONV_DICT_END() \
+ ga_append(gap, '}')
+
+#define CONV_DICT_AFTER_KEY() \
+ ga_concat(gap, ": ")
+
+#define CONV_DICT_BETWEEN_ITEMS() \
+ ga_concat(gap, ", ")
+
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
+
+#define CONV_LIST_END(lst) \
+ ga_append(gap, ']')
+
+#define CONV_LIST_BETWEEN_ITEMS() \
+ CONV_DICT_BETWEEN_ITEMS()
+
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ if (!did_echo_string_emsg) { \
+ /* Only give this message once for a recursive call to avoid */ \
+ /* flooding the user with errors. */ \
+ did_echo_string_emsg = true; \
+ EMSG(_("E724: unable to correctly dump variable " \
+ "with self-referencing container")); \
+ } \
+ char ebuf[NUMBUFLEN + 7]; \
+ size_t backref = 0; \
+ for (; backref < kv_size(*mpstack); backref++) { \
+ const MPConvStackVal mpval = kv_A(*mpstack, backref); \
+ if (mpval.type == conv_type) { \
+ if (conv_type == kMPConvDict) { \
+ if ((void *) mpval.data.d.dict == (void *) (val)) { \
+ break; \
+ } \
+ } else if (conv_type == kMPConvList) { \
+ if ((void *) mpval.data.l.list == (void *) (val)) { \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \
+ ga_concat(gap, &ebuf[0]); \
+ return OK; \
+ } while (0)
+
+#define CONV_ALLOW_SPECIAL false
+
+DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
+
+#undef CONV_RECURSE
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ char ebuf[NUMBUFLEN + 7]; \
+ size_t backref = 0; \
+ for (; backref < kv_size(*mpstack); backref++) { \
+ const MPConvStackVal mpval = kv_A(*mpstack, backref); \
+ if (mpval.type == conv_type) { \
+ if (conv_type == kMPConvDict) { \
+ if ((void *) mpval.data.d.dict == (void *) val) { \
+ break; \
+ } \
+ } else if (conv_type == kMPConvList) { \
+ if ((void *) mpval.data.l.list == (void *) val) { \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (conv_type == kMPConvDict) { \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \
+ } else { \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \
+ } \
+ ga_concat(gap, &ebuf[0]); \
+ return OK; \
+ } while (0)
+
+DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
+
+#undef CONV_RECURSE
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ if (!did_echo_string_emsg) { \
+ /* Only give this message once for a recursive call to avoid */ \
+ /* flooding the user with errors. */ \
+ did_echo_string_emsg = true; \
+ EMSG(_("E724: unable to correctly dump variable " \
+ "with self-referencing container")); \
+ } \
+ return OK; \
+ } while (0)
+
+#undef CONV_ALLOW_SPECIAL
+#define CONV_ALLOW_SPECIAL true
+
+#undef CONV_NIL
+#define CONV_NIL() \
+ ga_concat(gap, "null")
+
+#undef CONV_BOOL
+#define CONV_BOOL(num) \
+ ga_concat(gap, ((num)? "true": "false"))
+
+#undef CONV_UNSIGNED_NUMBER
+#define CONV_UNSIGNED_NUMBER(num) \
+ do { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
+ ga_concat(gap, numbuf); \
+ } while (0)
+
+#undef CONV_FLOAT
+#define CONV_FLOAT(flt) \
+ do { \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ EMSG(_("E474: Unable to represent NaN value in JSON")); \
+ return FAIL; \
+ } \
+ case FP_INFINITE: { \
+ EMSG(_("E474: Unable to represent infinity in JSON")); \
+ return FAIL; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ break; \
+ } \
+ } \
+ } while (0)
+
+/// Last used p_enc value
+///
+/// Generic pointer: it is not used as a string, only pointer comparisons are
+/// performed. Must not be freed.
+static const void *last_p_enc = NULL;
+
+/// Conversion setup for converting from last_p_enc to UTF-8
+static vimconv_T p_enc_conv = {
+ .vc_type = CONV_NONE,
+};
+
+/// Escape sequences used in JSON
+static const char escapes[][3] = {
+ [BS] = "\\b",
+ [TAB] = "\\t",
+ [NL] = "\\n",
+ [CAR] = "\\r",
+ ['"'] = "\\\"",
+ ['\\'] = "\\\\",
+ [FF] = "\\f",
+};
+
+static const char xdigits[] = "0123456789ABCDEF";
+
+/// Convert given string to JSON string
+///
+/// @param[out] gap Garray where result will be saved.
+/// @param[in] buf Converted string.
+/// @param[in] len Converted string length.
+///
+/// @return OK in case of success, FAIL otherwise.
+static inline int convert_to_json_string(garray_T *const gap,
+ const char *const buf,
+ const size_t len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *utf_buf = buf;
+ if (utf_buf == NULL) {
+ ga_concat(gap, "\"\"");
+ } else {
+ size_t utf_len = len;
+ char *tofree = NULL;
+ if (last_p_enc != (const void *) p_enc) {
+ p_enc_conv.vc_type = CONV_NONE;
+ convert_setup(&p_enc_conv, p_enc, "utf-8");
+ p_enc_conv.vc_fail = true;
+ last_p_enc = p_enc;
+ }
+ if (p_enc_conv.vc_type != CONV_NONE) {
+ tofree = string_convert(&p_enc_conv, buf, &utf_len);
+ if (tofree == NULL) {
+ emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"),
+ utf_len, utf_buf);
+ return FAIL;
+ }
+ utf_buf = tofree;
+ }
+ size_t str_len = 0;
+ // Encode character as \u0000 if
+ // 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F).
+ // 2. &encoding is not UTF-8 and code point is above 0x7F.
+ // 3. &encoding is UTF-8 and code point is not printable according to
+ // utf_printable().
+ // This is done to make it possible to :echo values when &encoding is not
+ // UTF-8.
+#define ENCODE_RAW(p_enc_conv, ch) \
+ (ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \
+ ? utf_printable(ch) \
+ : ch < 0x7F))
+ for (size_t i = 0; i < utf_len;) {
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i));
+ assert(shift > 0);
+ i += shift;
+ switch (ch) {
+ case BS:
+ case TAB:
+ case NL:
+ case FF:
+ case CAR:
+ case '"':
+ case '\\': {
+ str_len += 2;
+ break;
+ }
+ default: {
+ if (ch > 0x7F && shift == 1) {
+ emsgf(_("E474: String \"%.*s\" contains byte that does not start "
+ "any UTF-8 character"),
+ utf_len - (i - shift), utf_buf + i - shift);
+ xfree(tofree);
+ return FAIL;
+ } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END)
+ || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) {
+ emsgf(_("E474: UTF-8 string contains code point which belongs "
+ "to a surrogate pair: %.*s"),
+ utf_len - (i - shift), utf_buf + i - shift);
+ xfree(tofree);
+ return FAIL;
+ } else if (ENCODE_RAW(p_enc_conv, ch)) {
+ str_len += shift;
+ } else {
+ str_len += ((sizeof("\\u1234") - 1)
+ * (size_t) (1 + (ch >= SURROGATE_FIRST_CHAR)));
+ }
+ break;
+ }
+ }
+ }
+ ga_append(gap, '"');
+ ga_grow(gap, (int) str_len);
+ for (size_t i = 0; i < utf_len;) {
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0? 1: utf_char2len(ch));
+ assert(shift > 0);
+ // Is false on invalid unicode, but this should already be handled.
+ assert(ch == 0 || shift == utf_ptr2len(utf_buf + i));
+ switch (ch) {
+ case BS:
+ case TAB:
+ case NL:
+ case FF:
+ case CAR:
+ case '"':
+ case '\\': {
+ ga_concat_len(gap, escapes[ch], 2);
+ break;
+ }
+ default: {
+ if (ENCODE_RAW(p_enc_conv, ch)) {
+ ga_concat_len(gap, utf_buf + i, shift);
+ } else if (ch < SURROGATE_FIRST_CHAR) {
+ ga_concat_len(gap, ((const char[]) {
+ '\\', 'u',
+ xdigits[(ch >> (4 * 3)) & 0xF],
+ xdigits[(ch >> (4 * 2)) & 0xF],
+ xdigits[(ch >> (4 * 1)) & 0xF],
+ xdigits[(ch >> (4 * 0)) & 0xF],
+ }), sizeof("\\u1234") - 1);
+ } else {
+ const int tmp = ch - SURROGATE_FIRST_CHAR;
+ const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1));
+ const int lo = SURROGATE_LO_END + ((tmp >> 0) & ((1 << 10) - 1));
+ ga_concat_len(gap, ((const char[]) {
+ '\\', 'u',
+ xdigits[(hi >> (4 * 3)) & 0xF],
+ xdigits[(hi >> (4 * 2)) & 0xF],
+ xdigits[(hi >> (4 * 1)) & 0xF],
+ xdigits[(hi >> (4 * 0)) & 0xF],
+ '\\', 'u',
+ xdigits[(lo >> (4 * 3)) & 0xF],
+ xdigits[(lo >> (4 * 2)) & 0xF],
+ xdigits[(lo >> (4 * 1)) & 0xF],
+ xdigits[(lo >> (4 * 0)) & 0xF],
+ }), (sizeof("\\u1234") - 1) * 2);
+ }
+ break;
+ }
+ }
+ i += shift;
+ }
+ ga_append(gap, '"');
+ xfree(tofree);
+ }
+ return OK;
+}
+
+#undef CONV_STRING
+#define CONV_STRING(buf, len) \
+ do { \
+ if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
+ return FAIL; \
+ } \
+ } while (0)
+
+#undef CONV_EXT_STRING
+#define CONV_EXT_STRING(buf, len, type) \
+ do { \
+ xfree(buf); \
+ EMSG(_("E474: Unable to convert EXT string to JSON")); \
+ return FAIL; \
+ } while (0)
+
+#undef CONV_FUNC
+#define CONV_FUNC(fun) \
+ return conv_error(_("E474: Error while dumping %s, %s: " \
+ "attempt to dump function reference"), \
+ mpstack, objname)
+
+/// Check whether given key can be used in json_encode()
+///
+/// @param[in] tv Key to check.
+static inline bool check_json_key(const typval_T *const tv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (tv->v_type == VAR_STRING) {
+ return true;
+ }
+ if (tv->v_type != VAR_DICT) {
+ return false;
+ }
+ const dict_T *const spdict = tv->vval.v_dict;
+ if (spdict->dv_hashtab.ht_used != 2) {
+ return false;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL
+ || type_di->di_tv.v_type != VAR_LIST
+ || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString]
+ && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary])
+ || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL
+ || val_di->di_tv.v_type != VAR_LIST) {
+ return false;
+ }
+ if (val_di->di_tv.vval.v_list == NULL) {
+ return true;
+ }
+ for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first;
+ li != NULL; li = li->li_next) {
+ if (li->li_tv.v_type != VAR_STRING) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \
+ do { \
+ if (!check_json_key(&kv_pair->lv_first->li_tv)) { \
+ EMSG(_("E474: Invalid key in special dictionary")); \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ } while (0)
+
+DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
+
+#undef CONV_STRING
+#undef CONV_STR_STRING
+#undef CONV_EXT_STRING
+#undef CONV_NUMBER
+#undef CONV_FLOAT
+#undef CONV_FUNC
+#undef CONV_EMPTY_LIST
+#undef CONV_LIST_START
+#undef CONV_EMPTY_DICT
+#undef CONV_NIL
+#undef CONV_BOOL
+#undef CONV_UNSIGNED_NUMBER
+#undef CONV_DICT_START
+#undef CONV_DICT_END
+#undef CONV_DICT_AFTER_KEY
+#undef CONV_DICT_BETWEEN_ITEMS
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#undef CONV_LIST_END
+#undef CONV_LIST_BETWEEN_ITEMS
+#undef CONV_RECURSE
+#undef CONV_ALLOW_SPECIAL
+
+/// Return a string with the string representation of a variable.
+/// Puts quotes around strings, so that they can be parsed back by eval().
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2string(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
+ did_echo_string_emsg = false;
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+/// Return a string with the string representation of a variable.
+/// Does not put quotes around strings, as ":echo" displays values.
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2echo(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
+ if (tv->vval.v_string != NULL) {
+ ga_concat(&ga, tv->vval.v_string);
+ }
+ } else {
+ encode_vim_to_echo(&ga, tv, ":echo argument");
+ }
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+/// Return a string with the string representation of a variable.
+/// Puts quotes around strings, so that they can be parsed back by eval().
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2json(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ did_echo_string_emsg = false;
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+#define CONV_STRING(buf, len) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_bin(packer, 0); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_bin(packer, len_); \
+ msgpack_pack_bin_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_STR_STRING(buf, len) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_str(packer, 0); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_str(packer, len_); \
+ msgpack_pack_str_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_EXT_STRING(buf, len, type) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_ext(packer, 0, (int8_t) type); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_ext(packer, len_, (int8_t) type); \
+ msgpack_pack_ext_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_NUMBER(num) \
+ msgpack_pack_int64(packer, (int64_t) (num))
+
+#define CONV_FLOAT(flt) \
+ msgpack_pack_double(packer, (double) (flt))
+
+#define CONV_FUNC(fun) \
+ return conv_error(_("E951: Error while dumping %s, %s: " \
+ "attempt to dump function reference"), \
+ mpstack, objname)
+
+#define CONV_EMPTY_LIST() \
+ msgpack_pack_array(packer, 0)
+
+#define CONV_LIST_START(lst) \
+ msgpack_pack_array(packer, (size_t) (lst)->lv_len)
+
+#define CONV_EMPTY_DICT() \
+ msgpack_pack_map(packer, 0)
+
+#define CONV_NIL() \
+ msgpack_pack_nil(packer)
+
+#define CONV_BOOL(num) \
+ do { \
+ if ((num)) { \
+ msgpack_pack_true(packer); \
+ } else { \
+ msgpack_pack_false(packer); \
+ } \
+ } while (0)
+
+#define CONV_UNSIGNED_NUMBER(num) \
+ msgpack_pack_uint64(packer, (num))
+
+#define CONV_DICT_START(len) \
+ msgpack_pack_map(packer, (size_t) (len))
+
+#define CONV_DICT_END()
+
+#define CONV_DICT_AFTER_KEY()
+
+#define CONV_DICT_BETWEEN_ITEMS()
+
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
+
+#define CONV_LIST_END(lst)
+
+#define CONV_LIST_BETWEEN_ITEMS()
+
+#define CONV_RECURSE(val, conv_type) \
+ return conv_error(_("E952: Unable to dump %s: " \
+ "container references itself in %s"), \
+ mpstack, objname)
+
+#define CONV_ALLOW_SPECIAL true
+
+DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
+
+#undef CONV_STRING
+#undef CONV_STR_STRING
+#undef CONV_EXT_STRING
+#undef CONV_NUMBER
+#undef CONV_FLOAT
+#undef CONV_FUNC
+#undef CONV_EMPTY_LIST
+#undef CONV_LIST_START
+#undef CONV_EMPTY_DICT
+#undef CONV_NIL
+#undef CONV_BOOL
+#undef CONV_UNSIGNED_NUMBER
+#undef CONV_DICT_START
+#undef CONV_DICT_END
+#undef CONV_DICT_AFTER_KEY
+#undef CONV_DICT_BETWEEN_ITEMS
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#undef CONV_LIST_END
+#undef CONV_LIST_BETWEEN_ITEMS
+#undef CONV_RECURSE
+#undef CONV_ALLOW_SPECIAL
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
new file mode 100644
index 0000000000..9bc665253b
--- /dev/null
+++ b/src/nvim/eval/encode.h
@@ -0,0 +1,75 @@
+#ifndef NVIM_EVAL_ENCODE_H
+#define NVIM_EVAL_ENCODE_H
+
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval.h"
+#include "nvim/garray.h"
+#include "nvim/vim.h" // For STRLEN
+
+/// Convert VimL value to msgpack string
+///
+/// @param[out] packer Packer to save results in.
+/// @param[in] tv Dumped value.
+/// @param[in] objname Object name, used for error message.
+///
+/// @return OK in case of success, FAIL otherwise.
+int encode_vim_to_msgpack(msgpack_packer *const packer,
+ typval_T *const tv,
+ const char *const objname);
+
+/// Convert VimL value to :echo output
+///
+/// @param[out] packer Packer to save results in.
+/// @param[in] tv Dumped value.
+/// @param[in] objname Object name, used for error message.
+///
+/// @return OK in case of success, FAIL otherwise.
+int encode_vim_to_echo(garray_T *const packer,
+ typval_T *const tv,
+ const char *const objname);
+
+/// Structure defining state for read_from_list()
+typedef struct {
+ const listitem_T *li; ///< Item currently read.
+ size_t offset; ///< Byte offset inside the read item.
+ size_t li_length; ///< Length of the string inside the read item.
+} ListReaderState;
+
+/// Initialize ListReaderState structure
+static inline ListReaderState encode_init_lrstate(const list_T *const list)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return (ListReaderState) {
+ .li = list->lv_first,
+ .offset = 0,
+ .li_length = (list->lv_first->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(list->lv_first->li_tv.vval.v_string)),
+ };
+}
+
+/// Array mapping values from SpecialVarValue enum to names
+extern const char *const encode_special_var_names[];
+
+/// First codepoint in high surrogates block
+#define SURROGATE_HI_START 0xD800
+
+/// Last codepoint in high surrogates block
+#define SURROGATE_HI_END 0xDBFF
+
+/// First codepoint in low surrogates block
+#define SURROGATE_LO_START 0xDC00
+
+/// Last codepoint in low surrogates block
+#define SURROGATE_LO_END 0xDFFF
+
+/// First character that needs to be encoded as surrogate pair
+#define SURROGATE_FIRST_CHAR 0x10000
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/encode.h.generated.h"
+#endif
+#endif // NVIM_EVAL_ENCODE_H
diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h
index cdad1f3197..8ffc0c98ce 100644
--- a/src/nvim/eval_defs.h
+++ b/src/nvim/eval_defs.h
@@ -16,39 +16,52 @@ typedef double float_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
-/*
- * Structure to hold an internal variable without a name.
- */
+/// Special variable values
+typedef enum {
+ kSpecialVarFalse, ///< v:false
+ kSpecialVarTrue, ///< v:true
+ kSpecialVarNull, ///< v:null
+} SpecialVarValue;
+
+/// Variable lock status for typval_T.v_lock
+typedef enum {
+ VAR_UNLOCKED = 0, ///< Not locked.
+ VAR_LOCKED, ///< User lock, can be unlocked.
+ VAR_FIXED, ///< Locked forever.
+} VarLockStatus;
+
+/// VimL variable types, for use in typval_T.v_type
+typedef enum {
+ VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
+ VAR_NUMBER, ///< Number, .v_number is used.
+ VAR_STRING, ///< String, .v_string is used.
+ VAR_FUNC, ///< Function referene, .v_string is used for function name.
+ VAR_LIST, ///< List, .v_list is used.
+ VAR_DICT, ///< Dictionary, .v_dict is used.
+ VAR_FLOAT, ///< Floating-point value, .v_float is used.
+ VAR_SPECIAL, ///< Special value (true, false, null), .v_special
+ ///< is used.
+} VarType;
+
+/// Structure that holds an internal variable value
typedef struct {
- char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */
- char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */
+ VarType v_type; ///< Variable type.
+ VarLockStatus v_lock; ///< Variable lock status.
union {
- varnumber_T v_number; /* number value */
- float_T v_float; /* floating number value */
- char_u *v_string; /* string value (can be NULL!) */
- list_T *v_list; /* list value (can be NULL!) */
- dict_T *v_dict; /* dict value (can be NULL!) */
- } vval;
+ varnumber_T v_number; ///< Number, for VAR_NUMBER.
+ SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
+ float_T v_float; ///< Floating-point number, for VAR_FLOAT.
+ char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
+ list_T *v_list; ///< List for VAR_LIST, can be NULL.
+ dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
+ } vval; ///< Actual value.
} typval_T;
-/* Values for "v_type". */
-#define VAR_UNKNOWN 0
-#define VAR_NUMBER 1 /* "v_number" is used */
-#define VAR_STRING 2 /* "v_string" is used */
-#define VAR_FUNC 3 /* "v_string" is function name */
-#define VAR_LIST 4 /* "v_list" is used */
-#define VAR_DICT 5 /* "v_dict" is used */
-#define VAR_FLOAT 6 /* "v_float" is used */
-
/* Values for "dv_scope". */
#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */
#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not
allowed to mask existing functions */
-/* Values for "v_lock". */
-#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */
-#define VAR_FIXED 2 /* locked forever */
-
/*
* Structure to hold an item of a list: an internal variable without a name.
*/
@@ -107,19 +120,18 @@ typedef struct dictitem_S dictitem_T;
#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable
#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated
-/*
- * Structure to hold info about a Dictionary.
- */
+/// Structure representing a Dictionary
struct dictvar_S {
- char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */
- char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */
- int dv_refcount; /* reference count */
- int dv_copyID; /* ID used by deepcopy() */
- hashtab_T dv_hashtab; /* hashtab that refers to the items */
- dict_T *dv_copydict; /* copied dict used by deepcopy() */
- dict_T *dv_used_next; /* next dict in used dicts list */
- dict_T *dv_used_prev; /* previous dict in used dicts list */
- QUEUE watchers; // dictionary key watchers set by user code
+ VarLockStatus dv_lock; ///< Whole dictionary lock status.
+ char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if
+ ///< dictionary represents a scope (i.e. g:, l: …).
+ int dv_refcount; ///< Reference count.
+ int dv_copyID; ///< ID used when recursivery traversing a value.
+ hashtab_T dv_hashtab; ///< Hashtab containing all items.
+ dict_T *dv_copydict; ///< Copied dict used by deepcopy().
+ dict_T *dv_used_next; ///< Next dictionary in used dictionaries list.
+ dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list.
+ QUEUE watchers; ///< Dictionary key watchers set by user code.
};
// structure used for explicit stack while garbage collecting hash tables
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 5ea5beb478..d020bc8f20 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3,6 +3,7 @@
*/
#include <assert.h>
+#include <float.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
@@ -50,7 +51,6 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -274,17 +274,26 @@ static int linelen(int *has_tab)
static char_u *sortbuf1;
static char_u *sortbuf2;
-static int sort_ic; /* ignore case */
-static int sort_nr; /* sort on number */
-static int sort_rx; /* sort on regex instead of skipping it */
+static int sort_ic; ///< ignore case
+static int sort_nr; ///< sort on number
+static int sort_rx; ///< sort on regex instead of skipping it
+static int sort_flt; ///< sort on floating number
-static int sort_abort; /* flag to indicate if sorting has been interrupted */
+static int sort_abort; ///< flag to indicate if sorting has been interrupted
-/* Struct to store info to be sorted. */
+/// Struct to store info to be sorted.
typedef struct {
- linenr_T lnum; /* line number */
- long start_col_nr; /* starting column number or number */
- long end_col_nr; /* ending column number */
+ linenr_T lnum; ///< line number
+ long start_col_nr; ///< starting column number or number
+ long end_col_nr; ///< ending column number
+ union {
+ struct {
+ long start_col_nr; ///< starting column number
+ long end_col_nr; ///< ending column number
+ } line;
+ long value; ///< value if sorting by integer
+ float_T value_flt; ///< value if sorting by float
+ } st_u;
} sorti_T;
@@ -303,21 +312,26 @@ static int sort_compare(const void *s1, const void *s2)
if (got_int)
sort_abort = TRUE;
- /* When sorting numbers "start_col_nr" is the number, not the column
- * number. */
- if (sort_nr)
- result = l1.start_col_nr == l2.start_col_nr ? 0
- : l1.start_col_nr > l2.start_col_nr ? 1 : -1;
- else {
- /* We need to copy one line into "sortbuf1", because there is no
- * guarantee that the first pointer becomes invalid when obtaining the
- * second one. */
- STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr,
- l1.end_col_nr - l1.start_col_nr + 1);
- sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0;
- STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr,
- l2.end_col_nr - l2.start_col_nr + 1);
- sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0;
+ // When sorting numbers "start_col_nr" is the number, not the column
+ // number.
+ if (sort_nr) {
+ result = l1.st_u.value == l2.st_u.value
+ ? 0 : l1.st_u.value > l2.st_u.value
+ ? 1 : -1;
+ } else if (sort_flt) {
+ result = l1.st_u.value_flt == l2.st_u.value_flt
+ ? 0 : l1.st_u.value_flt > l2.st_u.value_flt
+ ? 1 : -1;
+ } else {
+ // We need to copy one line into "sortbuf1", because there is no
+ // guarantee that the first pointer becomes invalid when obtaining the
+ // second one.
+ STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr,
+ l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1);
+ sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0;
+ STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr,
+ l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1);
+ sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0;
result = sort_ic ? STRICMP(sortbuf1, sortbuf2)
: STRCMP(sortbuf1, sortbuf2);
@@ -361,7 +375,7 @@ void ex_sort(exarg_T *eap)
regmatch.regprog = NULL;
sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
- sort_abort = sort_ic = sort_rx = sort_nr = 0;
+ sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0;
size_t format_found = 0;
for (p = eap->arg; *p != NUL; ++p) {
@@ -371,7 +385,10 @@ void ex_sort(exarg_T *eap)
} else if (*p == 'r') {
sort_rx = true;
} else if (*p == 'n') {
- sort_nr = 2;
+ sort_nr = 1;
+ format_found++;
+ } else if (*p == 'f') {
+ sort_flt = 1;
format_found++;
} else if (*p == 'b') {
sort_what = STR2NR_BIN + STR2NR_FORCE;
@@ -424,7 +441,8 @@ void ex_sort(exarg_T *eap)
goto sortend;
}
- // From here on "sort_nr" is used as a flag for any number sorting.
+ // From here on "sort_nr" is used as a flag for any integer number
+ // sorting.
sort_nr += sort_what;
// Make an array with all line numbers. This avoids having to copy all
@@ -453,7 +471,7 @@ void ex_sort(exarg_T *eap)
end_col = 0;
}
- if (sort_nr) {
+ if (sort_nr || sort_flt) {
// Make sure vim_str2nr doesn't read any digits past the end
// of the match, by temporarily terminating the string there
s2 = s + end_col;
@@ -461,29 +479,42 @@ void ex_sort(exarg_T *eap)
*s2 = NUL;
// Sorting on number: Store the number itself.
p = s + start_col;
- if (sort_what & STR2NR_HEX) {
- s = skiptohex(p);
- } else if (sort_what & STR2NR_BIN) {
- s = (char_u*) skiptobin((char*) p);
- } else {
- s = skiptodigit(p);
- }
- if (s > p && s[-1] == '-') {
- // include preceding negative sign
- s--;
- }
- if (*s == NUL) {
- // empty line should sort before any number
- nrs[lnum - eap->line1].start_col_nr = -MAXLNUM;
+ if (sort_nr) {
+ if (sort_what & STR2NR_HEX) {
+ s = skiptohex(p);
+ } else if (sort_what & STR2NR_BIN) {
+ s = (char_u *)skiptobin((char *)p);
+ } else {
+ s = skiptodigit(p);
+ }
+ if (s > p && s[-1] == '-') {
+ s--; // include preceding negative sign
+ }
+ if (*s == NUL) {
+ // empty line should sort before any number
+ nrs[lnum - eap->line1].st_u.value = -MAXLNUM;
+ } else {
+ vim_str2nr(s, NULL, NULL, sort_what,
+ &nrs[lnum - eap->line1].st_u.value, NULL, 0);
+ }
} else {
- vim_str2nr(s, NULL, NULL, sort_what,
- &nrs[lnum - eap->line1].start_col_nr, NULL, 0);
+ s = skipwhite(p);
+ if (*s == '+') {
+ s = skipwhite(s + 1);
+ }
+
+ if (*s == NUL) {
+ // empty line should sort before any number
+ nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX;
+ } else {
+ nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL);
+ }
}
*s2 = c;
} else {
// Store the column to sort at.
- nrs[lnum - eap->line1].start_col_nr = start_col;
- nrs[lnum - eap->line1].end_col_nr = end_col;
+ nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col;
+ nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col;
}
nrs[lnum - eap->line1].lnum = lnum;
@@ -1326,15 +1357,17 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#endif
size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL.
-
+
len += is_fish_shell ? sizeof("begin; ""; end") - 1
: sizeof("("")") - 1;
- if (itmp != NULL)
+ if (itmp != NULL) {
len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1;
- if (otmp != NULL)
+ }
+ if (otmp != NULL) {
len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "),
- char_u *buf = xmalloc(len);
+ }
+ char *const buf = xmalloc(len);
#if defined(UNIX)
// Put delimiters around the command (for concatenated commands) when
@@ -1342,19 +1375,19 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
if (itmp != NULL || otmp != NULL) {
char *fmt = is_fish_shell ? "begin; %s; end"
: "(%s)";
- vim_snprintf((char *)buf, len, fmt, (char *)cmd);
+ vim_snprintf(buf, len, fmt, (char *)cmd);
} else {
- STRCPY(buf, cmd);
+ strncpy(buf, (char *) cmd, len);
}
if (itmp != NULL) {
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
+ strncat(buf, " < ", len);
+ strncat(buf, (char *) itmp, len);
}
#else
// For shells that don't understand braces around commands, at least allow
// the use of commands in a pipe.
- STRCPY(buf, cmd);
+ strncpy(buf, cmd, len);
if (itmp != NULL) {
char_u *p;
@@ -1362,55 +1395,56 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
// Don't do this when 'shellquote' is not empty, otherwise the
// redirection would be inside the quotes.
if (*p_shq == NUL) {
- p = vim_strchr(buf, '|');
- if (p != NULL)
+ p = strchr(buf, '|');
+ if (p != NULL) {
*p = NUL;
+ }
}
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
+ strncat(buf, " < ", len);
+ strncat(buf, (char *) itmp, len);
if (*p_shq == NUL) {
- p = vim_strchr(cmd, '|');
+ p = strchr(cmd, '|');
if (p != NULL) {
- STRCAT(buf, " "); // Insert a space before the '|' for DOS
- STRCAT(buf, p);
+ strncat(buf, " ", len); // Insert a space before the '|' for DOS
+ strncat(buf, p, len);
}
}
}
#endif
if (otmp != NULL) {
- append_redir(buf, len, p_srr, otmp);
+ append_redir(buf, len, (char *) p_srr, (char *) otmp);
}
- return buf;
+ return (char_u *) buf;
}
-/*
- * Append output redirection for file "fname" to the end of string buffer
- * "buf[buflen]"
- * Works with the 'shellredir' and 'shellpipe' options.
- * The caller should make sure that there is enough room:
- * STRLEN(opt) + STRLEN(fname) + 3
- */
-void append_redir(char_u *buf, size_t buflen, char_u *opt, char_u *fname)
+/// Append output redirection for the given file to the end of the buffer
+///
+/// @param[out] buf Buffer to append to.
+/// @param[in] buflen Buffer length.
+/// @param[in] opt Separator or format string to append: will append
+/// `printf(' ' . opt, fname)` if `%s` is found in `opt` or
+/// a space, opt, a space and then fname if `%s` is not found
+/// there.
+/// @param[in] fname File name to append.
+void append_redir(char *const buf, const size_t buflen,
+ const char *const opt, const char *const fname)
{
- char_u *p;
- char_u *end;
-
- end = buf + STRLEN(buf);
- /* find "%s" */
- for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p) {
- if (p[1] == 's') /* found %s */
+ char *const end = buf + strlen(buf);
+ // find "%s"
+ const char *p = opt;
+ for (; (p = strchr(p, '%')) != NULL; p++) {
+ if (p[1] == 's') { // found %s
break;
- if (p[1] == '%') /* skip %% */
- ++p;
+ } else if (p[1] == '%') { // skip %%
+ p++;
+ }
}
if (p != NULL) {
- *end = ' '; /* not really needed? Not with sh, ksh or bash */
- vim_snprintf((char *)end + 1, (size_t)(buflen - (end + 1 - buf)),
- (char *)opt, (char *)fname);
- } else
- vim_snprintf((char *)end, (size_t)(buflen - (end - buf)),
- " %s %s",
- (char *)opt, (char *)fname);
+ *end = ' '; // not really needed? Not with sh, ksh or bash
+ vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname);
+ } else {
+ vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname);
+ }
}
void print_line_no_prefix(linenr_T lnum, int use_number, int list)
@@ -2093,15 +2127,13 @@ do_ecmd (
if ((command != NULL || newlnum > (linenr_T)0)
&& *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
- char_u *p;
-
- /* Set v:swapcommand for the SwapExists autocommands. */
- size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
- p = xmalloc(len);
+ // Set v:swapcommand for the SwapExists autocommands.
+ const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
+ char *const p = xmalloc(len);
if (command != NULL) {
- vim_snprintf((char *)p, len, ":%s\r", command);
+ vim_snprintf(p, len, ":%s\r", command);
} else {
- vim_snprintf((char *)p, len, "%" PRId64 "G", (int64_t)newlnum);
+ vim_snprintf(p, len, "%" PRId64 "G", (int64_t)newlnum);
}
set_vim_var_string(VV_SWAPCOMMAND, p, -1);
did_set_swapcommand = TRUE;
@@ -5016,7 +5048,9 @@ helptags_one (
/*
* Sort the tags.
*/
- sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ if (ga.ga_data != NULL) {
+ sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ }
/*
* Check for duplicates.
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 6c8835b5c5..04fd88cc8d 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2575,6 +2575,18 @@ return {
func='ex_copymove',
},
{
+ command='tcd',
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_cd',
+ },
+ {
+ command='tchdir',
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_cd',
+ },
+ {
command='tNext',
flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR),
addr_type=ADDR_LINES,
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 71ea170e1c..a0a0b9d3a5 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2373,8 +2373,9 @@ static FILE *fopen_noinh_readbin(char *filename)
# ifdef HAVE_FD_CLOEXEC
{
int fdflags = fcntl(fd_tmp, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
+ }
}
# endif
@@ -3168,27 +3169,27 @@ static char_u *get_mess_env(void)
*/
void set_lang_var(void)
{
- char_u *loc;
+ const char *loc;
# ifdef HAVE_GET_LOCALE_VAL
- loc = (char_u *)get_locale_val(LC_CTYPE);
+ loc = get_locale_val(LC_CTYPE);
# else
- /* setlocale() not supported: use the default value */
- loc = (char_u *)"C";
+ // setlocale() not supported: use the default value
+ loc = "C";
# endif
set_vim_var_string(VV_CTYPE, loc, -1);
/* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
* back to LC_CTYPE if it's empty. */
# ifdef HAVE_WORKING_LIBINTL
- loc = get_mess_env();
+ loc = (char *) get_mess_env();
# else
- loc = (char_u *)get_locale_val(LC_MESSAGES);
+ loc = get_locale_val(LC_MESSAGES);
# endif
set_vim_var_string(VV_LANG, loc, -1);
# ifdef HAVE_GET_LOCALE_VAL
- loc = (char_u *)get_locale_val(LC_TIME);
+ loc = get_locale_val(LC_TIME);
# endif
set_vim_var_string(VV_LC_TIME, loc, -1);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index ef108ec783..648a3a8487 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2958,8 +2958,11 @@ set_one_cmd_context (
case CMD_chdir:
case CMD_lcd:
case CMD_lchdir:
- if (xp->xp_context == EXPAND_FILES)
+ case CMD_tcd:
+ case CMD_tchdir:
+ if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_DIRECTORIES;
+ }
break;
case CMD_help:
xp->xp_context = EXPAND_HELP;
@@ -6815,36 +6818,55 @@ void free_cd_dir(void)
#endif
-/*
- * Deal with the side effects of changing the current directory.
- * When "local" is TRUE then this was after an ":lcd" command.
- */
-void post_chdir(int local)
+/// Deal with the side effects of changing the current directory.
+///
+/// @param scope Scope of the function call (global, tab or window).
+void post_chdir(CdScope scope)
{
+ // The local directory of the current window is always overwritten.
xfree(curwin->w_localdir);
curwin->w_localdir = NULL;
- if (local) {
- /* If still in global directory, need to remember current
- * directory as global directory. */
- if (globaldir == NULL && prev_dir != NULL)
+
+ // Overwrite the local directory of the current tab page for `cd` and `tcd`
+ if (scope >= kCdScopeTab) {
+ xfree(curtab->localdir);
+ curtab->localdir = NULL;
+ }
+
+ if (scope < kCdScopeGlobal) {
+ // If still in global directory, need to remember current directory as
+ // global directory.
+ if (globaldir == NULL && prev_dir != NULL) {
globaldir = vim_strsave(prev_dir);
- /* Remember this local directory for the window. */
- if (os_dirname(NameBuff, MAXPATHL) == OK)
- curwin->w_localdir = vim_strsave(NameBuff);
- } else {
- /* We are now in the global directory, no need to remember its
- * name. */
+ }
+ }
+
+ switch (scope) {
+ case kCdScopeGlobal:
+ // We are now in the global directory, no need to remember its name.
xfree(globaldir);
globaldir = NULL;
+ break;
+ case kCdScopeTab:
+ // Remember this local directory for the tab page.
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
+ curtab->localdir = vim_strsave(NameBuff);
+ }
+ break;
+ case kCdScopeWindow:
+ // Remember this local directory for the window.
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
+ curwin->w_localdir = vim_strsave(NameBuff);
+ }
+ break;
}
shorten_fnames(TRUE);
}
-/*
- * ":cd", ":lcd", ":chdir" and ":lchdir".
- */
+
+/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
void ex_cd(exarg_T *eap)
{
char_u *new_dir;
@@ -6885,10 +6907,25 @@ void ex_cd(exarg_T *eap)
new_dir = NameBuff;
}
#endif
- if (new_dir == NULL || vim_chdir(new_dir))
+ if (vim_chdir(new_dir)) {
EMSG(_(e_failed));
- else {
- post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir);
+ } else {
+ CdScope scope = kCdScopeGlobal; // Depends on command invoked
+
+ switch (eap->cmdidx) {
+ case CMD_tcd:
+ case CMD_tchdir:
+ scope = kCdScopeTab;
+ break;
+ case CMD_lcd:
+ case CMD_lchdir:
+ scope = kCdScopeWindow;
+ break;
+ default:
+ break;
+ }
+
+ post_chdir(scope);
/* Echo the new current directory if the command was typed. */
if (KeyTyped || p_verbose >= 5)
@@ -7422,10 +7459,10 @@ static int mksession_nl = FALSE; /* use NL only in put_eol() */
static void ex_mkrc(exarg_T *eap)
{
FILE *fd;
- int failed = FALSE;
- int view_session = FALSE;
- int using_vdir = FALSE; /* using 'viewdir'? */
- char_u *viewFile = NULL;
+ int failed = false;
+ int view_session = false;
+ int using_vdir = false; // using 'viewdir'?
+ char *viewFile = NULL;
unsigned *flagp;
if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) {
@@ -7436,32 +7473,34 @@ static void ex_mkrc(exarg_T *eap)
* short file name when 'acd' is set, that is checked later. */
did_lcd = FALSE;
- char_u *fname;
- /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */
+ char *fname;
+ // ":mkview" or ":mkview 9": generate file name with 'viewdir'
if (eap->cmdidx == CMD_mkview
&& (*eap->arg == NUL
|| (ascii_isdigit(*eap->arg) && eap->arg[1] == NUL))) {
- eap->forceit = TRUE;
- fname = (char_u *)get_view_file(*eap->arg);
- if (fname == NULL)
+ eap->forceit = true;
+ fname = get_view_file(*eap->arg);
+ if (fname == NULL) {
return;
+ }
viewFile = fname;
- using_vdir = TRUE;
- } else if (*eap->arg != NUL)
- fname = eap->arg;
- else if (eap->cmdidx == CMD_mkvimrc)
- fname = (char_u *)VIMRC_FILE;
- else if (eap->cmdidx == CMD_mksession)
- fname = (char_u *)SESSION_FILE;
- else
- fname = (char_u *)EXRC_FILE;
+ using_vdir = true;
+ } else if (*eap->arg != NUL) {
+ fname = (char *) eap->arg;
+ } else if (eap->cmdidx == CMD_mkvimrc) {
+ fname = VIMRC_FILE;
+ } else if (eap->cmdidx == CMD_mksession) {
+ fname = SESSION_FILE;
+ } else {
+ fname = EXRC_FILE;
+ }
/* When using 'viewdir' may have to create the directory. */
if (using_vdir && !os_isdir(p_vdir)) {
vim_mkdir_emsg(p_vdir, 0755);
}
- fd = open_exfile(fname, eap->forceit, WRITEBIN);
+ fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN);
if (fd != NULL) {
if (eap->cmdidx == CMD_mkview)
flagp = &vop_flags;
@@ -7505,8 +7544,9 @@ static void ex_mkrc(exarg_T *eap)
|| os_chdir((char *)dirnow) != 0)
*dirnow = NUL;
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
- if (vim_chdirfile(fname) == OK)
- shorten_fnames(TRUE);
+ if (vim_chdirfile((char_u *) fname) == OK) {
+ shorten_fnames(true);
+ }
} else if (*dirnow != NUL
&& (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
if (os_chdir((char *)globaldir) == 0)
@@ -7551,15 +7591,14 @@ static void ex_mkrc(exarg_T *eap)
failed |= fclose(fd);
- if (failed)
+ if (failed) {
EMSG(_(e_write));
- else if (eap->cmdidx == CMD_mksession) {
- /* successful session write - set this_session var */
- char_u *tbuf;
-
- tbuf = xmalloc(MAXPATHL);
- if (vim_FullName((char *)fname, (char *)tbuf, MAXPATHL, FALSE) == OK)
+ } else if (eap->cmdidx == CMD_mksession) {
+ // successful session write - set this_session var
+ char *const tbuf = xmalloc(MAXPATHL);
+ if (vim_FullName(fname, tbuf, MAXPATHL, false) == OK) {
set_vim_var_string(VV_THIS_SESSION, tbuf, -1);
+ }
xfree(tbuf);
}
#ifdef MKSESSION_NL
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index a5a4edbbbf..7af3ee233c 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -19,6 +19,18 @@
#define EXMODE_NORMAL 1
#define EXMODE_VIM 2
+/// The scope of a command.
+///
+/// The lower a number, the deeper the scope.
+typedef enum {
+ kCdScopeWindow, ///< Affects one window.
+ kCdScopeTab, ///< Affects one tab page.
+ kCdScopeGlobal, ///< Affects the entire instance of NeoVim.
+} CdScope;
+
+/// Last `:cd` scope defined.
+#define MAX_CD_SCOPE kCdScopeGlobal
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.h.generated.h"
#endif
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 6fc74c7ad0..41ad96a378 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -569,17 +569,19 @@ static void catch_exception(except_T *excp)
{
excp->caught = caught_stack;
caught_stack = excp;
- set_vim_var_string(VV_EXCEPTION, excp->value, -1);
+ set_vim_var_string(VV_EXCEPTION, (char *) excp->value, -1);
if (*excp->throw_name != NUL) {
- if (excp->throw_lnum != 0)
+ if (excp->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64),
- excp->throw_name, (int64_t)excp->throw_lnum);
- else
+ excp->throw_name, (int64_t)excp->throw_lnum);
+ } else {
vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name);
- set_vim_var_string(VV_THROWPOINT, IObuff, -1);
- } else
- /* throw_name not set on an exception from a command that was typed. */
+ }
+ set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1);
+ } else {
+ // throw_name not set on an exception from a command that was typed.
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ }
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
@@ -614,20 +616,22 @@ static void finish_exception(except_T *excp)
EMSG(_(e_internal));
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
- set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
+ set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1);
if (*caught_stack->throw_name != NUL) {
- if (caught_stack->throw_lnum != 0)
+ if (caught_stack->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE,
- _("%s, line %" PRId64), caught_stack->throw_name,
- (int64_t)caught_stack->throw_lnum);
- else
+ _("%s, line %" PRId64), caught_stack->throw_name,
+ (int64_t)caught_stack->throw_lnum);
+ } else {
vim_snprintf((char *)IObuff, IOSIZE, "%s",
- caught_stack->throw_name);
- set_vim_var_string(VV_THROWPOINT, IObuff, -1);
- } else
- /* throw_name not set on an exception from a command that was
- * typed. */
+ caught_stack->throw_name);
+ }
+ set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1);
+ } else {
+ // throw_name not set on an exception from a command that was
+ // typed.
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ }
} else {
set_vim_var_string(VV_EXCEPTION, NULL, -1);
set_vim_var_string(VV_THROWPOINT, NULL, -1);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 383cd47dbe..c7870b9f69 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -45,7 +45,6 @@
#include "nvim/search.h"
#include "nvim/sha256.h"
#include "nvim/strings.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/types.h"
#include "nvim/undo.h"
@@ -2139,9 +2138,10 @@ readfile_charconvert (
else {
close(*fdp); /* close the input file, ignore errors */
*fdp = -1;
- if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc,
- fname, tmpname) == FAIL)
+ if (eval_charconvert((char *) fenc, enc_utf8 ? "utf-8" : (char *) p_enc,
+ (char *) fname, (char *) tmpname) == FAIL) {
errmsg = (char_u *)_("Conversion with 'charconvert' failed");
+ }
if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) {
errmsg = (char_u *)_("can't read output of 'charconvert'");
}
@@ -3435,9 +3435,9 @@ restore_backup:
* with 'charconvert' to (overwrite) the output file.
*/
if (end != 0) {
- if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
- wfname, fname) == FAIL) {
- write_info.bw_conv_error = TRUE;
+ if (eval_charconvert(enc_utf8 ? "utf-8" : (char *) p_enc, (char *) fenc,
+ (char *) wfname, (char *) fname) == FAIL) {
+ write_info.bw_conv_error = true;
end = 0;
}
}
@@ -4740,7 +4740,6 @@ buf_check_timestamp (
{
int retval = 0;
char_u *path;
- char_u *tbuf;
char *mesg = NULL;
char *mesg2 = "";
int helpmesg = FALSE;
@@ -4810,19 +4809,17 @@ buf_check_timestamp (
else
reason = "time";
- /*
- * Only give the warning if there are no FileChangedShell
- * autocommands.
- * Avoid being called recursively by setting "busy".
- */
- busy = TRUE;
- set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1);
- set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1);
- ++allbuf_lock;
+ // Only give the warning if there are no FileChangedShell
+ // autocommands.
+ // Avoid being called recursively by setting "busy".
+ busy = true;
+ set_vim_var_string(VV_FCS_REASON, reason, -1);
+ set_vim_var_string(VV_FCS_CHOICE, "", -1);
+ allbuf_lock++;
n = apply_autocmds(EVENT_FILECHANGEDSHELL,
- buf->b_fname, buf->b_fname, FALSE, buf);
- --allbuf_lock;
- busy = FALSE;
+ buf->b_fname, buf->b_fname, false, buf);
+ allbuf_lock--;
+ busy = false;
if (n) {
if (!buf_valid(buf))
EMSG(_("E246: FileChangedShell autocommand deleted buffer"));
@@ -4876,35 +4873,39 @@ buf_check_timestamp (
if (mesg != NULL) {
path = home_replace_save(buf, buf->b_fname);
- if (!helpmesg)
+ if (!helpmesg) {
mesg2 = "";
- tbuf = xmalloc(STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2);
- sprintf((char *)tbuf, mesg, path);
- /* Set warningmsg here, before the unimportant and output-specific
- * mesg2 has been appended. */
+ }
+ const size_t tbuf_len = STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2;
+ char *const tbuf = xmalloc(tbuf_len);
+ snprintf(tbuf, tbuf_len, mesg, path);
+ // Set warningmsg here, before the unimportant and output-specific
+ // mesg2 has been appended.
set_vim_var_string(VV_WARNINGMSG, tbuf, -1);
if (can_reload) {
if (*mesg2 != NUL) {
- STRCAT(tbuf, "\n");
- STRCAT(tbuf, mesg2);
+ strncat(tbuf, "\n", tbuf_len);
+ strncat(tbuf, mesg2, tbuf_len);
+ }
+ if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf,
+ (char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) {
+ reload = true;
}
- if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf,
- (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2)
- reload = TRUE;
} else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) {
if (*mesg2 != NUL) {
- STRCAT(tbuf, "; ");
- STRCAT(tbuf, mesg2);
+ strncat(tbuf, "; ", tbuf_len);
+ strncat(tbuf, mesg2, tbuf_len);
}
EMSG(tbuf);
retval = 2;
} else {
if (!autocmd_busy) {
msg_start();
- msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST);
- if (*mesg2 != NUL)
+ msg_puts_attr((char_u *) tbuf, hl_attr(HLF_E) + MSG_HIST);
+ if (*mesg2 != NUL) {
msg_puts_attr((char_u *)mesg2,
hl_attr(HLF_W) + MSG_HIST);
+ }
msg_clr_eos();
(void)msg_end();
if (emsg_silent == 0) {
@@ -5114,6 +5115,147 @@ void forward_slash(char_u *fname)
}
#endif
+/// Name of Vim's own temp dir. Ends in a slash.
+static char_u *vim_tempdir = NULL;
+
+/// Create a directory for private use by this instance of Neovim.
+/// This is done once, and the same directory is used for all temp files.
+/// This method avoids security problems because of symlink attacks et al.
+/// It's also a bit faster, because we only need to check for an existing
+/// file when creating the directory and not for each temp file.
+static void vim_maketempdir(void)
+{
+ static const char *temp_dirs[] = TEMP_DIR_NAMES;
+ // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
+ char_u template[TEMP_FILE_PATH_MAXLEN];
+ char_u path[TEMP_FILE_PATH_MAXLEN];
+ for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
+ // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
+ expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
+ if (!os_isdir(template)) { // directory doesn't exist
+ continue;
+ }
+
+ add_pathsep((char *)template);
+ // Concatenate with temporary directory name pattern
+ STRCAT(template, "nvimXXXXXX");
+
+ if (os_mkdtemp((const char *)template, (char *)path) != 0) {
+ continue;
+ }
+
+ if (vim_settempdir((char *)path)) {
+ // Successfully created and set temporary directory so stop trying.
+ break;
+ } else {
+ // Couldn't set `vim_tempdir` to `path` so remove created directory.
+ os_rmdir((char *)path);
+ }
+ }
+}
+
+/// Delete "name" and everything in it, recursively.
+/// @param name The path which should be deleted.
+/// @return 0 for success, -1 if some file was not deleted.
+int delete_recursive(char_u *name)
+{
+ int result = 0;
+
+ if (os_isrealdir(name)) {
+ snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT
+
+ char_u **files;
+ int file_count;
+ char_u *exp = vim_strsave(NameBuff);
+ if (gen_expand_wildcards(1, &exp, &file_count, &files,
+ EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS
+ | EW_DODOT | EW_EMPTYOK) == OK) {
+ for (int i = 0; i < file_count; i++) {
+ if (delete_recursive(files[i]) != 0) {
+ result = -1;
+ }
+ }
+ FreeWild(file_count, files);
+ } else {
+ result = -1;
+ }
+
+ xfree(exp);
+ os_rmdir((char *)name);
+ } else {
+ result = os_remove((char *)name) == 0 ? 0 : -1;
+ }
+
+ return result;
+}
+
+/// Delete the temp directory and all files it contains.
+void vim_deltempdir(void)
+{
+ if (vim_tempdir != NULL) {
+ // remove the trailing path separator
+ path_tail(vim_tempdir)[-1] = NUL;
+ delete_recursive(vim_tempdir);
+ xfree(vim_tempdir);
+ vim_tempdir = NULL;
+ }
+}
+
+/// Get the name of temp directory. This directory would be created on the first
+/// call to this function.
+char_u *vim_gettempdir(void)
+{
+ if (vim_tempdir == NULL) {
+ vim_maketempdir();
+ }
+
+ return vim_tempdir;
+}
+
+/// Set Neovim own temporary directory name to `tempdir`. This directory should
+/// be already created. Expand this name to a full path and put it in
+/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
+///
+/// @param tempdir must be no longer than MAXPATHL.
+///
+/// @return false if we run out of memory.
+static bool vim_settempdir(char *tempdir)
+{
+ char *buf = verbose_try_malloc(MAXPATHL + 2);
+ if (!buf) {
+ return false;
+ }
+ vim_FullName(tempdir, buf, MAXPATHL, false);
+ add_pathsep(buf);
+ vim_tempdir = (char_u *)xstrdup(buf);
+ xfree(buf);
+ return true;
+}
+
+/// Return a unique name that can be used for a temp file.
+///
+/// @note The temp file is NOT created.
+///
+/// @return pointer to the temp file name or NULL if Neovim can't create
+/// temporary directory for its own temporary files.
+char_u *vim_tempname(void)
+{
+ // Temp filename counter.
+ static uint32_t temp_count;
+
+ char_u *tempdir = vim_gettempdir();
+ if (!tempdir) {
+ return NULL;
+ }
+
+ // There is no need to check if the file exists, because we own the directory
+ // and nobody else creates a file in it.
+ char_u template[TEMP_FILE_PATH_MAXLEN];
+ snprintf((char *)template, TEMP_FILE_PATH_MAXLEN,
+ "%s%" PRIu32, tempdir, temp_count++);
+ return vim_strsave(template);
+}
+
/*
* Code for automatic commands.
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 70ab4ced75..ac3cf959c8 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1699,14 +1699,14 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
did_emsg = FALSE;
if (*wp->w_p_fdt != NUL) {
- char_u dashes[MAX_LEVEL + 2];
+ char dashes[MAX_LEVEL + 2];
win_T *save_curwin;
int level;
char_u *p;
- /* Set "v:foldstart" and "v:foldend". */
- set_vim_var_nr(VV_FOLDSTART, lnum);
- set_vim_var_nr(VV_FOLDEND, lnume);
+ // Set "v:foldstart" and "v:foldend".
+ set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum);
+ set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume);
/* Set "v:folddashes" to a string of "level" dashes. */
/* Set "v:foldlevel" to "level". */
@@ -1716,7 +1716,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
memset(dashes, '-', (size_t)level);
dashes[level] = NUL;
set_vim_var_string(VV_FOLDDASHES, dashes, -1);
- set_vim_var_nr(VV_FOLDLEVEL, (long)level);
+ set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T) level);
/* skip evaluating foldtext on errors */
if (!got_fdt_error) {
@@ -2110,10 +2110,11 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
*/
if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
&& flp->lvl > 0) {
- foldFind(gap, startlnum - 1, &fp);
+ (void)foldFind(gap, startlnum - 1, &fp);
if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len
- || fp->fd_top >= startlnum)
+ || fp->fd_top >= startlnum) {
fp = NULL;
+ }
}
/*
@@ -2167,13 +2168,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
}
}
if (lvl < level + i) {
- foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2);
- if (fp2 != NULL)
+ (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2);
+ if (fp2 != NULL) {
bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top;
- } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level)
- finish = TRUE;
- else
+ }
+ } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) {
+ finish = true;
+ } else {
break;
+ }
}
/* At the start of the first nested fold and at the end of the current
@@ -2676,7 +2679,7 @@ static void foldlevelExpr(fline_T *flp)
win = curwin;
curwin = flp->wp;
curbuf = flp->wp->w_buffer;
- set_vim_var_nr(VV_LNUM, lnum);
+ set_vim_var_nr(VV_LNUM, (varnumber_T) lnum);
flp->start = 0;
flp->had_end = flp->end;
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index e6cbd9332b..98cec69b54 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -188,12 +188,23 @@ void ga_concat(garray_T *gap, const char_u *restrict s)
return;
}
- int len = (int)strlen((char *) s);
+ ga_concat_len(gap, (const char *restrict) s, strlen((char *) s));
+}
+
+/// Concatenate a string to a growarray which contains characters
+///
+/// @param[out] gap Growarray to modify.
+/// @param[in] s String to concatenate.
+/// @param[in] len String length.
+void ga_concat_len(garray_T *const gap, const char *restrict s,
+ const size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
if (len) {
- ga_grow(gap, len);
+ ga_grow(gap, (int) len);
char *data = gap->ga_data;
- memcpy(data + gap->ga_len, s, (size_t)len);
- gap->ga_len += len;
+ memcpy(data + gap->ga_len, s, len);
+ gap->ga_len += (int) len;
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 40b5718071..49d1de21d9 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -4,17 +4,7 @@
#include <stdbool.h>
#include <inttypes.h>
-// EXTERN is only defined in main.c. That's where global variables are
-// actually defined and initialized.
-#ifndef EXTERN
-# define EXTERN extern
-# define INIT(...)
-#else
-# ifndef INIT
-# define INIT(...) __VA_ARGS__
-# endif
-#endif
-
+#include "nvim/macros.h"
#include "nvim/ex_eval.h"
#include "nvim/iconv.h"
#include "nvim/mbyte.h"
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index ab8959239b..6ce8954fef 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -32,7 +32,6 @@
#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/version.h"
-#include "nvim/tempfile.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -2780,11 +2779,13 @@ void mch_print_end(prt_settings_T *psettings)
}
prt_message((char_u *)_("Sending to printer..."));
- /* Not printing to a file: use 'printexpr' to print the file. */
- if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
+ // Not printing to a file: use 'printexpr' to print the file.
+ if (eval_printexpr((char *) prt_ps_file_name, (char *) psettings->arguments)
+ == FAIL) {
EMSG(_("E365: Failed to print PostScript file"));
- else
+ } else {
prt_message((char_u *)_("Print job sent."));
+ }
}
mch_print_cleanup();
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index a143490356..2f9ec0b3ff 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -27,7 +27,6 @@
#include "nvim/quickfix.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -1063,8 +1062,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose,
if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
*qfpos == '-', cmdline) > 0) {
if (postponed_split != 0) {
- win_split(postponed_split > 0 ? postponed_split : 0,
- postponed_split_flags);
+ (void)win_split(postponed_split > 0 ? postponed_split : 0,
+ postponed_split_flags);
RESET_BINDING(curwin);
postponed_split = 0;
}
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index d3008185dc..f197669a97 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -529,7 +529,7 @@ int get_expr_indent(void)
save_pos = curwin->w_cursor;
save_curswant = curwin->w_curswant;
save_set_curswant = curwin->w_set_curswant;
- set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum);
+ set_vim_var_nr(VV_LNUM, (varnumber_T) curwin->w_cursor.lnum);
if (use_sandbox) {
sandbox++;
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 53ecf232c6..b41ef0cc9f 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -60,6 +60,7 @@ int main() {
#define kv_pop(v) ((v).items[--(v).size])
#define kv_size(v) ((v).size)
#define kv_max(v) ((v).capacity)
+#define kv_last(v) kv_A(v, kv_size(v) - 1)
#define kv_resize(type, v, s) ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity))
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 26ab5a7de7..5f69fa2f6a 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -1,6 +1,17 @@
#ifndef NVIM_MACROS_H
#define NVIM_MACROS_H
+// EXTERN is only defined in main.c. That's where global variables are
+// actually defined and initialized.
+#ifndef EXTERN
+# define EXTERN extern
+# define INIT(...)
+#else
+# ifndef INIT
+# define INIT(...) __VA_ARGS__
+# endif
+#endif
+
#ifndef MIN
# define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 23ced5ebe5..71a972e8f6 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -238,8 +238,8 @@ int main(int argc, char **argv)
check_and_set_isatty(&params);
// Get the name with which Nvim was invoked, with and without path.
- set_vim_var_string(VV_PROGPATH, (char_u *)argv[0], -1);
- set_vim_var_string(VV_PROGNAME, path_tail((char_u *)argv[0]), -1);
+ set_vim_var_string(VV_PROGPATH, argv[0], -1);
+ set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1);
event_init();
/*
@@ -1141,10 +1141,11 @@ scripterror:
/* If there is a "+123" or "-c" command, set v:swapcommand to the first
* one. */
if (parmp->n_commands > 0) {
- p = xmalloc(STRLEN(parmp->commands[0]) + 3);
- sprintf((char *)p, ":%s\r", parmp->commands[0]);
- set_vim_var_string(VV_SWAPCOMMAND, p, -1);
- xfree(p);
+ const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3;
+ char *const swcmd = xmalloc(swcmd_len);
+ snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]);
+ set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
+ xfree(swcmd);
}
TIME_MSG("parsing arguments");
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index b568279d7d..f82f88a88f 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -65,7 +65,6 @@
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/version.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
@@ -3194,7 +3193,7 @@ attention_message (
*/
static int do_swapexists(buf_T *buf, char_u *fname)
{
- set_vim_var_string(VV_SWAPNAME, fname, -1);
+ set_vim_var_string(VV_SWAPNAME, (char *) fname, -1);
set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
/* Trigger SwapExists autocommands with <afile> set to the file being
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 1dd71baaa4..97b098c6d2 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -61,14 +61,8 @@ static int confirm_msg_used = FALSE; /* displaying confirm_msg */
static char_u *confirm_msg = NULL; /* ":confirm" message */
static char_u *confirm_msg_tail; /* tail of confirm_msg */
-struct msg_hist {
- struct msg_hist *next;
- char_u *msg;
- int attr;
-};
-
-static struct msg_hist *first_msg_hist = NULL;
-static struct msg_hist *last_msg_hist = NULL;
+MessageHistoryEntry *first_msg_hist = NULL;
+MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
static FILE *verbose_fd = NULL;
@@ -149,10 +143,11 @@ msg_attr_keep (
{
static int entered = 0;
int retval;
- char_u *buf = NULL;
+ char_u *buf = NULL;
- if (attr == 0)
- set_vim_var_string(VV_STATUSMSG, s, -1);
+ if (attr == 0) {
+ set_vim_var_string(VV_STATUSMSG, (char *) s, -1);
+ }
/*
* It is possible that displaying a messages causes a problem (e.g.,
@@ -503,8 +498,8 @@ int emsg(char_u *s)
return TRUE;
}
- /* set "v:errmsg", also when using ":silent! cmd" */
- set_vim_var_string(VV_ERRMSG, s, -1);
+ // set "v:errmsg", also when using ":silent! cmd"
+ set_vim_var_string(VV_ERRMSG, (char *) s, -1);
/*
* When using ":silent! cmd" ignore error messages.
@@ -563,49 +558,23 @@ int emsg(char_u *s)
return msg_attr(s, attr);
}
-/*
- * Print an error message with one "%s" and one string argument.
- */
-int emsg2(char_u *s, char_u *a1)
-{
- return emsg3(s, a1, NULL);
-}
-
void emsg_invreg(int name)
{
EMSG2(_("E354: Invalid register name: '%s'"), transchar(name));
}
-/// Print an error message with one or two "%s" and one or two string arguments.
-int emsg3(char_u *s, char_u *a1, char_u *a2)
-{
- if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
- }
-
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, a1, a2);
- return emsg(IObuff);
-}
-
-/// Print an error message with one "%" PRId64 and one (int64_t) argument.
-int emsgn(char_u *s, int64_t n)
+/// Print an error message with unknown number of arguments
+bool emsgf(const char *const fmt, ...)
{
if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
+ return true;
}
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n);
- return emsg(IObuff);
-}
-
-/// Print an error message with one "%" PRIu64 and one (uint64_t) argument.
-int emsgu(char_u *s, uint64_t n)
-{
- if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
- }
+ va_list ap;
+ va_start(ap, fmt);
+ vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL);
+ va_end(ap);
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n);
return emsg(IObuff);
}
@@ -1787,25 +1756,24 @@ static void msg_scroll_up(void)
static void inc_msg_scrolled(void)
{
if (*get_vim_var_str(VV_SCROLLSTART) == NUL) {
- char_u *p = sourcing_name;
- char_u *tofree = NULL;
- int len;
-
- /* v:scrollstart is empty, set it to the script/function name and line
- * number */
- if (p == NULL)
- p = (char_u *)_("Unknown");
- else {
- len = (int)STRLEN(p) + 40;
+ char *p = (char *) sourcing_name;
+ char *tofree = NULL;
+
+ // v:scrollstart is empty, set it to the script/function name and line
+ // number
+ if (p == NULL) {
+ p = _("Unknown");
+ } else {
+ size_t len = strlen(p) + 40;
tofree = xmalloc(len);
- vim_snprintf((char *)tofree, len, _("%s line %" PRId64),
- p, (int64_t)sourcing_lnum);
+ vim_snprintf(tofree, len, _("%s line %" PRId64),
+ p, (int64_t) sourcing_lnum);
p = tofree;
}
set_vim_var_string(VV_SCROLLSTART, p, -1);
xfree(tofree);
}
- ++msg_scrolled;
+ msg_scrolled++;
}
static msgchunk_T *last_msgchunk = NULL; /* last displayed text */
@@ -2572,7 +2540,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
/* Don't want a hit-enter prompt here. */
++no_wait_return;
- set_vim_var_string(VV_WARNINGMSG, message, -1);
+ set_vim_var_string(VV_WARNINGMSG, (char *) message, -1);
xfree(keep_msg);
keep_msg = NULL;
if (hl)
@@ -3086,7 +3054,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
return str_l;
}
-int vim_snprintf(char *str, size_t str_m, char *fmt, ...)
+int vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
{
va_list ap;
int str_l;
@@ -3097,11 +3065,12 @@ int vim_snprintf(char *str, size_t str_m, char *fmt, ...)
return str_l;
}
-int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
+int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
+ typval_T *tvs)
{
size_t str_l = 0;
bool str_avail = str_l < str_m;
- char *p = fmt;
+ const char *p = fmt;
int arg_idx = 1;
if (!p) {
@@ -3135,7 +3104,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
char tmp[TMP_LEN];
// string address in case of string argument
- char *str_arg;
+ const char *str_arg;
// natural field width of arg without padding and sign
size_t str_arg_l;
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 019c7bfb73..d3a16fff93 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdarg.h>
#include "nvim/eval_defs.h" // for typval_T
+#include "nvim/ex_cmds_defs.h" // for exarg_T
/*
* Types of dialogs passed to do_dialog().
@@ -24,6 +25,56 @@
#define VIM_ALL 5
#define VIM_DISCARDALL 6
+/// Show plain message
+#define MSG(s) msg((char_u *)(s))
+
+/// Show message highlighted according to the attr
+#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr))
+
+/// Display error message
+///
+/// Sets error flag in process, can be transformed into an exception.
+#define EMSG(s) emsg((char_u *)(s))
+
+/// Like #EMSG, but for messages with one "%s" inside
+#define EMSG2(s, p) emsgf((const char *) (s), (p))
+
+/// Like #EMSG, but for messages with two "%s" inside
+#define EMSG3(s, p, q) emsgf((const char *) (s), (p), (q))
+
+/// Like #EMSG, but for messages with one "%" PRId64 inside
+#define EMSGN(s, n) emsgf((const char *) (s), (int64_t)(n))
+
+/// Like #EMSG, but for messages with one "%" PRIu64 inside
+#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n))
+
+/// Display message at the recorded position
+#define MSG_PUTS(s) msg_puts((char_u *)(s))
+
+/// Display message at the recorded position, highlighted
+#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a))
+
+/// Like #MSG_PUTS, but highlight like title
+#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s))
+
+/// Like #MSG_PUTS, but if middle part of too long messages it will be replaced
+#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0)
+
+/// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced
+#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a))
+
+/// Message history for `:messages`
+typedef struct msg_hist {
+ struct msg_hist *next; ///< Next message.
+ char_u *msg; ///< Message text.
+ int attr; ///< Message highlighting.
+} MessageHistoryEntry;
+
+/// First message
+extern MessageHistoryEntry *first_msg_hist;
+/// Last message
+extern MessageHistoryEntry *last_msg_hist;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index db303fd54a..43e0dd0c1a 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -43,7 +43,6 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -86,38 +85,32 @@ open_line (
int second_line_indent
)
{
- char_u *saved_line; /* copy of the original line */
- char_u *next_line = NULL; /* copy of the next line */
- char_u *p_extra = NULL; /* what goes to next line */
- int less_cols = 0; /* less columns for mark in new line */
- int less_cols_off = 0; /* columns to skip for mark adjust */
- pos_T old_cursor; /* old cursor position */
- int newcol = 0; /* new cursor column */
- int newindent = 0; /* auto-indent of the new line */
- int n;
- int trunc_line = FALSE; /* truncate current line afterwards */
- int retval = FALSE; /* return value, default is FAIL */
- int extra_len = 0; /* length of p_extra string */
- int lead_len; /* length of comment leader */
- char_u *lead_flags; /* position in 'comments' for comment leader */
- char_u *leader = NULL; /* copy of comment leader */
- char_u *allocated = NULL; /* allocated memory */
- char_u *p;
- int saved_char = NUL; /* init for GCC */
- pos_T *pos;
- int do_si = (!p_paste && curbuf->b_p_si
- && !curbuf->b_p_cin
- );
- int no_si = FALSE; /* reset did_si afterwards */
- int first_char = NUL; /* init for GCC */
+ char_u *next_line = NULL; // copy of the next line
+ char_u *p_extra = NULL; // what goes to next line
+ colnr_T less_cols = 0; // less columns for mark in new line
+ colnr_T less_cols_off = 0; // columns to skip for mark adjust
+ pos_T old_cursor; // old cursor position
+ colnr_T newcol = 0; // new cursor column
+ int newindent = 0; // auto-indent of the new line
+ bool trunc_line = false; // truncate current line afterwards
+ bool retval = false; // return value, default is false
+ int extra_len = 0; // length of p_extra string
+ int lead_len; // length of comment leader
+ char_u *lead_flags; // position in 'comments' for comment leader
+ char_u *leader = NULL; // copy of comment leader
+ char_u *allocated = NULL; // allocated memory
+ char_u *p;
+ char_u saved_char = NUL; // init for GCC
+ pos_T *pos;
+ bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin);
+ bool no_si = false; // reset did_si afterwards
+ int first_char = NUL; // init for GCC
int vreplace_mode;
- int did_append; /* appended a new line */
- int saved_pi = curbuf->b_p_pi; /* copy of preserveindent setting */
+ bool did_append; // appended a new line
+ int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting
- /*
- * make a copy of the current line so we can mess with it
- */
- saved_line = vim_strsave(get_cursor_line_ptr());
+ // make a copy of the current line so we can mess with it
+ char_u *saved_line = vim_strsave(get_cursor_line_ptr());
if (State & VREPLACE_FLAG) {
/*
@@ -204,7 +197,7 @@ open_line (
char_u *ptr;
char_u last_char;
- old_cursor = curwin->w_cursor;
+ pos_T old_cursor = curwin->w_cursor;
ptr = saved_line;
if (flags & OPENLINE_DO_COM)
lead_len = get_leader_len(ptr, NULL, FALSE, TRUE);
@@ -401,7 +394,7 @@ open_line (
end_comment_pending = -1; /* means we want to set it */
++p;
}
- n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) /* we can set it now */
end_comment_pending = lead_end[n - 1];
@@ -498,10 +491,11 @@ open_line (
}
}
if (lead_len > 0) {
- /* allocate buffer (may concatenate p_extra later) */
- leader = xmalloc(lead_len + lead_repl_len + extra_space + extra_len
- + (second_line_indent > 0 ? second_line_indent : 0) + 1);
- allocated = leader; /* remember to free it later */
+ // allocate buffer (may concatenate p_extra later)
+ leader = xmalloc((size_t)(lead_len + lead_repl_len + extra_space
+ + extra_len + (second_line_indent > 0
+ ? second_line_indent : 0) + 1));
+ allocated = leader; // remember to free it later
STRLCPY(leader, saved_line, lead_len + 1);
@@ -598,9 +592,8 @@ open_line (
if (!ascii_iswhite(*p)) {
/* Don't put a space before a TAB. */
if (p + 1 < leader + lead_len && p[1] == TAB) {
- --lead_len;
- memmove(p, p + 1,
- (leader + lead_len) - p);
+ lead_len--;
+ memmove(p, p + 1, (size_t)(leader + lead_len - p));
} else {
int l = (*mb_ptr2len)(p);
@@ -611,8 +604,7 @@ open_line (
--l;
*p++ = ' ';
}
- memmove(p + 1, p + l,
- (leader + lead_len) - p);
+ memmove(p + 1, p + l, (size_t)(leader + lead_len - p));
lead_len -= l - 1;
}
*p = ' ';
@@ -813,9 +805,11 @@ open_line (
* In REPLACE mode, for each character in the new indent, there must
* be a NUL on the replace stack, for when it is deleted with BS
*/
- if (REPLACE_NORMAL(State))
- for (n = 0; n < (int)curwin->w_cursor.col; ++n)
+ if (REPLACE_NORMAL(State)) {
+ for (colnr_T n = 0; n < curwin->w_cursor.col; n++) {
replace_push(NUL);
+ }
+ }
newcol += curwin->w_cursor.col;
if (no_si)
did_si = FALSE;
@@ -1281,11 +1275,13 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
*/
width = wp->w_width - win_col_off(wp);
- if (width <= 0)
- return 32000;
- if (col <= (unsigned int)width)
+ if (width <= 0) {
+ return 32000; // bigger than the number of lines of the screen
+ }
+ if (col <= (unsigned int)width) {
return 1;
- col -= width;
+ }
+ col -= (unsigned int)width;
width += win_col_off2(wp);
assert(col <= INT_MAX && (int)col < INT_MAX - (width -1));
return ((int)col + (width - 1)) / width + 1;
@@ -1297,15 +1293,9 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
*/
int plines_win_col(win_T *wp, linenr_T lnum, long column)
{
- long col;
- char_u *s;
- int lines = 0;
- int width;
- char_u *line;
-
- /* Check for filler lines above this buffer line. When folded the result
- * is one line anyway. */
- lines = diff_check_fill(wp, lnum);
+ // Check for filler lines above this buffer line. When folded the result
+ // is one line anyway.
+ int lines = diff_check_fill(wp, lnum);
if (!wp->w_p_wrap)
return lines + 1;
@@ -1313,30 +1303,29 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
if (wp->w_width == 0)
return lines + 1;
- line = s = ml_get_buf(wp->w_buffer, lnum, FALSE);
+ char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *s = line;
- col = 0;
+ colnr_T col = 0;
while (*s != NUL && --column >= 0) {
- col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL);
+ col += win_lbr_chartabsize(wp, line, s, col, NULL);
mb_ptr_adv(s);
}
- /*
- * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- * INSERT mode, then col must be adjusted so that it represents the last
- * screen position of the TAB. This only fixes an error when the TAB wraps
- * from one screen line to the next (when 'columns' is not a multiple of
- * 'ts') -- webb.
- */
- if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1))
- col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1;
+ // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
+ // INSERT mode, then col must be adjusted so that it represents the last
+ // screen position of the TAB. This only fixes an error when the TAB wraps
+ // from one screen line to the next (when 'columns' is not a multiple of
+ // 'ts') -- webb.
+ if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) {
+ col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
+ }
- /*
- * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
- */
- width = wp->w_width - win_col_off(wp);
- if (width <= 0)
+ // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
+ int width = wp->w_width - win_col_off(wp);
+ if (width <= 0) {
return 9999;
+ }
lines += 1;
if (col > width)
@@ -1349,11 +1338,9 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
int count = 0;
while (first <= last) {
- int x;
-
- /* Check if there are any really folded lines, but also included lines
- * that are maybe folded. */
- x = foldedCount(wp, first, NULL);
+ // Check if there are any really folded lines, but also included lines
+ // that are maybe folded.
+ linenr_T x = foldedCount(wp, first, NULL);
if (x > 0) {
++count; /* count 1 for "+-- folded" line */
first += x;
@@ -1374,117 +1361,99 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
*/
void ins_bytes(char_u *p)
{
- ins_bytes_len(p, (int)STRLEN(p));
+ ins_bytes_len(p, STRLEN(p));
}
-/*
- * Insert string "p" with length "len" at the cursor position.
- * Handles Replace mode and multi-byte characters.
- */
-void ins_bytes_len(char_u *p, int len)
+/// Insert string "p" with length "len" at the cursor position.
+/// Handles Replace mode and multi-byte characters.
+void ins_bytes_len(char_u *p, size_t len)
{
- int i;
- int n;
-
- if (has_mbyte)
- for (i = 0; i < len; i += n) {
- if (enc_utf8)
- /* avoid reading past p[len] */
- n = utfc_ptr2len_len(p + i, len - i);
- else
- n = (*mb_ptr2len)(p + i);
+ if (has_mbyte) {
+ size_t n;
+ for (size_t i = 0; i < len; i += n) {
+ if (enc_utf8) {
+ // avoid reading past p[len]
+ n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i));
+ } else {
+ n = (size_t)(*mb_ptr2len)(p + i);
+ }
ins_char_bytes(p + i, n);
}
- else
- for (i = 0; i < len; ++i)
+ } else {
+ for (size_t i = 0; i < len; i++) {
ins_char(p[i]);
+ }
+ }
}
-/*
- * Insert or replace a single character at the cursor position.
- * When in REPLACE or VREPLACE mode, replace any existing character.
- * Caller must have prepared for undo.
- * For multi-byte characters we get the whole character, the caller must
- * convert bytes to a character.
- */
+/// Insert or replace a single character at the cursor position.
+/// When in REPLACE or VREPLACE mode, replace any existing character.
+/// Caller must have prepared for undo.
+/// For multi-byte characters we get the whole character, the caller must
+/// convert bytes to a character.
void ins_char(int c)
{
char_u buf[MB_MAXBYTES + 1];
- int n;
+ size_t n = (size_t)(*mb_char2bytes)(c, buf);
- n = (*mb_char2bytes)(c, buf);
-
- /* When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
- * Happens for CTRL-Vu9900. */
- if (buf[0] == 0)
+ // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
+ // Happens for CTRL-Vu9900.
+ if (buf[0] == 0) {
buf[0] = '\n';
-
+ }
ins_char_bytes(buf, n);
}
-void ins_char_bytes(char_u *buf, int charlen)
+void ins_char_bytes(char_u *buf, size_t charlen)
{
- int c = buf[0];
- int newlen; /* nr of bytes inserted */
- int oldlen; /* nr of bytes deleted (0 when not replacing) */
- char_u *p;
- char_u *newp;
- char_u *oldp;
- int linelen; /* length of old line including NUL */
- colnr_T col;
- linenr_T lnum = curwin->w_cursor.lnum;
- int i;
-
- /* Break tabs if needed. */
- if (virtual_active() && curwin->w_cursor.coladd > 0)
+ // Break tabs if needed.
+ if (virtual_active() && curwin->w_cursor.coladd > 0) {
coladvance_force(getviscol());
+ }
- col = curwin->w_cursor.col;
- oldp = ml_get(lnum);
- linelen = (int)STRLEN(oldp) + 1;
+ int c = buf[0];
+ size_t col = (size_t)curwin->w_cursor.col;
+ linenr_T lnum = curwin->w_cursor.lnum;
+ char_u *oldp = ml_get(lnum);
+ size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL
- /* The lengths default to the values for when not replacing. */
- oldlen = 0;
- newlen = charlen;
+ // The lengths default to the values for when not replacing.
+ size_t oldlen = 0; // nr of bytes inserted
+ size_t newlen = charlen; // nr of bytes deleted (0 when not replacing)
if (State & REPLACE_FLAG) {
if (State & VREPLACE_FLAG) {
- colnr_T new_vcol = 0; /* init for GCC */
+ // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag.
+ // Returns the old value of list, so when finished,
+ // curwin->w_p_list should be set back to this.
+ int old_list = curwin->w_p_list;
+ if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
+ curwin->w_p_list = false;
+ }
+ // In virtual replace mode each character may replace one or more
+ // characters (zero if it's a TAB). Count the number of bytes to
+ // be deleted to make room for the new character, counting screen
+ // cells. May result in adding spaces to fill a gap.
colnr_T vcol;
- int old_list;
-
- /*
- * Disable 'list' temporarily, unless 'cpo' contains the 'L' flag.
- * Returns the old value of list, so when finished,
- * curwin->w_p_list should be set back to this.
- */
- old_list = curwin->w_p_list;
- if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL)
- curwin->w_p_list = FALSE;
-
- /*
- * In virtual replace mode each character may replace one or more
- * characters (zero if it's a TAB). Count the number of bytes to
- * be deleted to make room for the new character, counting screen
- * cells. May result in adding spaces to fill a gap.
- */
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
- new_vcol = vcol + chartabsize(buf, vcol);
+ colnr_T new_vcol = vcol + chartabsize(buf, vcol);
while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
vcol += chartabsize(oldp + col + oldlen, vcol);
- /* Don't need to remove a TAB that takes us to the right
- * position. */
- if (vcol > new_vcol && oldp[col + oldlen] == TAB)
+ // Don't need to remove a TAB that takes us to the right
+ // position.
+ if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
break;
- oldlen += (*mb_ptr2len)(oldp + col + oldlen);
- /* Deleted a bit too much, insert spaces. */
- if (vcol > new_vcol)
- newlen += vcol - new_vcol;
+ }
+ oldlen += (size_t)(*mb_ptr2len)(oldp + col + oldlen);
+ // Deleted a bit too much, insert spaces.
+ if (vcol > new_vcol) {
+ newlen += (size_t)(vcol - new_vcol);
+ }
}
curwin->w_p_list = old_list;
} else if (oldp[col] != NUL) {
- /* normal replace */
- oldlen = (*mb_ptr2len)(oldp + col);
+ // normal replace
+ oldlen = (size_t)(*mb_ptr2len)(oldp + col);
}
@@ -1493,38 +1462,39 @@ void ins_char_bytes(char_u *buf, int charlen)
* done the other way around, so that the first byte is popped off
* first (it tells the byte length of the character). */
replace_push(NUL);
- for (i = 0; i < oldlen; ++i) {
- if (has_mbyte)
- i += replace_push_mb(oldp + col + i) - 1;
- else
+ for (size_t i = 0; i < oldlen; i++) {
+ if (has_mbyte) {
+ i += (size_t)replace_push_mb(oldp + col + i) - 1;
+ } else {
replace_push(oldp[col + i]);
+ }
}
}
- newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen));
+ char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen));
- /* Copy bytes before the cursor. */
- if (col > 0)
+ // Copy bytes before the cursor.
+ if (col > 0) {
memmove(newp, oldp, (size_t)col);
+ }
- /* Copy bytes after the changed character(s). */
- p = newp + col;
- memmove(p + newlen, oldp + col + oldlen,
- (size_t)(linelen - col - oldlen));
+ // Copy bytes after the changed character(s).
+ char_u *p = newp + col;
+ memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen));
- /* Insert or overwrite the new character. */
+ // Insert or overwrite the new character.
memmove(p, buf, charlen);
- i = charlen;
- /* Fill with spaces when necessary. */
- while (i < newlen)
- p[i++] = ' ';
+ // Fill with spaces when necessary.
+ for (size_t i = charlen; i < newlen; i++) {
+ p[i] = ' ';
+ }
/* Replace the line in the buffer. */
ml_replace(lnum, newp, FALSE);
- /* mark the buffer as changed and prepare for displaying */
- changed_bytes(lnum, col);
+ // mark the buffer as changed and prepare for displaying
+ changed_bytes(lnum, (colnr_T)col);
/*
* If we're in Insert or Replace mode and 'showmatch' is set, then briefly
@@ -1541,8 +1511,8 @@ void ins_char_bytes(char_u *buf, int charlen)
}
if (!p_ri || (State & REPLACE_FLAG)) {
- /* Normal insert: move cursor right */
- curwin->w_cursor.col += charlen;
+ // Normal insert: move cursor right
+ curwin->w_cursor.col += (int)charlen;
}
/*
* TODO: should try to update w_row here, to avoid recomputing it later.
@@ -1595,7 +1565,7 @@ int del_char(int fixpos)
return FAIL;
return del_chars(1L, fixpos);
}
- return del_bytes(1L, fixpos, TRUE);
+ return del_bytes(1, fixpos, true);
}
/*
@@ -1603,7 +1573,7 @@ int del_char(int fixpos)
*/
int del_chars(long count, int fixpos)
{
- long bytes = 0;
+ int bytes = 0;
long i;
char_u *p;
int l;
@@ -1617,30 +1587,22 @@ int del_chars(long count, int fixpos)
return del_bytes(bytes, fixpos, TRUE);
}
-/*
- * Delete "count" bytes under the cursor.
- * If "fixpos" is TRUE, don't leave the cursor on the NUL after the line.
- * Caller must have prepared for undo.
- *
- * return FAIL for failure, OK otherwise
- */
-int
-del_bytes (
- long count,
- int fixpos_arg,
- int use_delcombine /* 'delcombine' option applies */
-)
+/// Delete "count" bytes under the cursor.
+/// If "fixpos" is true, don't leave the cursor on the NUL after the line.
+/// Caller must have prepared for undo.
+///
+/// @param count number of bytes to be deleted
+/// @param fixpos_arg leave the cursor on the NUL after the line
+/// @param use_delcombine 'delcombine' option applies
+///
+/// @return FAIL for failure, OK otherwise
+int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
{
- char_u *oldp, *newp;
- colnr_T oldlen;
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
- int was_alloced;
- long movelen;
- int fixpos = fixpos_arg;
-
- oldp = ml_get(lnum);
- oldlen = (int)STRLEN(oldp);
+ bool fixpos = fixpos_arg;
+ char_u *oldp = ml_get(lnum);
+ colnr_T oldlen = (colnr_T)STRLEN(oldp);
/*
* Can't do anything when the cursor is on the NUL after the line.
@@ -1664,14 +1626,12 @@ del_bytes (
count = utf_ptr2len(oldp + n);
n += count;
} while (UTF_COMPOSINGLIKE(oldp + col, oldp + n));
- fixpos = 0;
+ fixpos = false;
}
}
- /*
- * When count is too big, reduce it.
- */
- movelen = (long)oldlen - (long)col - count + 1; /* includes trailing NUL */
+ // When count is too big, reduce it.
+ int movelen = oldlen - col - count + 1; // includes trailing NUL
if (movelen <= 1) {
/*
* If we just took off the last character of a non-blank line, and
@@ -1691,15 +1651,14 @@ del_bytes (
movelen = 1;
}
- /*
- * If the old line has been allocated the deletion can be done in the
- * existing line. Otherwise a new line has to be allocated.
- */
- was_alloced = ml_line_alloced(); /* check if oldp was allocated */
- if (was_alloced)
- newp = oldp; /* use same allocated memory */
- else { /* need to allocate a new line */
- newp = xmalloc(oldlen + 1 - count);
+ // If the old line has been allocated the deletion can be done in the
+ // existing line. Otherwise a new line has to be allocated.
+ bool was_alloced = ml_line_alloced(); // check if oldp was allocated
+ char_u *newp;
+ if (was_alloced) {
+ newp = oldp; // use same allocated memory
+ } else { // need to allocate a new line
+ newp = xmalloc((size_t)(oldlen + 1 - count));
memmove(newp, oldp, (size_t)col);
}
memmove(newp + col, oldp + col + count, (size_t)movelen);
@@ -1725,12 +1684,12 @@ truncate_line (
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
- if (col == 0)
+ if (col == 0) {
newp = vim_strsave((char_u *)"");
- else
- newp = vim_strnsave(ml_get(lnum), col);
-
- ml_replace(lnum, newp, FALSE);
+ } else {
+ newp = vim_strnsave(ml_get(lnum), (size_t)col);
+ }
+ ml_replace(lnum, newp, false);
/* mark the buffer as changed and prepare for displaying */
changed_bytes(lnum, curwin->w_cursor.col);
@@ -2250,7 +2209,7 @@ change_warning (
msg_col = col;
msg_source(hl_attr(HLF_W));
MSG_PUTS_ATTR(_(w_readonly), hl_attr(HLF_W) | MSG_HIST);
- set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_readonly), -1);
+ set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
(void)msg_end();
if (msg_silent == 0 && !silent_mode) {
@@ -2360,13 +2319,13 @@ int get_keystroke(void)
* 5 chars plus NUL). And fix_input_buffer() can triple the number of
* bytes. */
maxlen = (buflen - 6 - len) / 3;
- if (buf == NULL)
- buf = xmalloc(buflen);
- else if (maxlen < 10) {
- /* Need some more space. This might happen when receiving a long
- * escape sequence. */
+ if (buf == NULL) {
+ buf = xmalloc((size_t)buflen);
+ } else if (maxlen < 10) {
+ // Need some more space. This might happen when receiving a long
+ // escape sequence.
buflen += 100;
- buf = xrealloc(buf, buflen);
+ buf = xrealloc(buf, (size_t)buflen);
maxlen = (buflen - 6 - len) / 3;
}
@@ -2729,38 +2688,33 @@ void fast_breakcheck(void)
}
}
-/*
- * Get the stdout of an external command.
- * If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not
- * NULL store the length there.
- * Returns an allocated string, or NULL for error.
- */
-char_u *
-get_cmd_output (
- char_u *cmd,
- char_u *infile, /* optional input file name */
- int flags, // can be kShellOptSilent
- size_t *ret_len
-)
+/// Get the stdout of an external command.
+/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not
+/// NULL store the length there.
+///
+/// @param cmd command to execute
+/// @param infile optional input file name
+/// @param flags can be kShellOptSilent or 0
+/// @param ret_len length of the stdout
+///
+/// @return an allocated string, or NULL for error.
+char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags,
+ size_t *ret_len)
{
- char_u *tempname;
- char_u *command;
- char_u *buffer = NULL;
- int len;
- int i = 0;
- FILE *fd;
+ char_u *buffer = NULL;
if (check_restricted() || check_secure())
return NULL;
- /* get a name for the temp file */
- if ((tempname = vim_tempname()) == NULL) {
+ // get a name for the temp file
+ char_u *tempname = vim_tempname();
+ if (tempname == NULL) {
EMSG(_(e_notmp));
return NULL;
}
- /* Add the redirection stuff */
- command = make_filter_cmd(cmd, infile, tempname);
+ // Add the redirection stuff
+ char_u *command = make_filter_cmd(cmd, infile, tempname);
/*
* Call the shell to execute the command (errors are ignored).
@@ -2772,10 +2726,8 @@ get_cmd_output (
xfree(command);
- /*
- * read the names from the file into memory
- */
- fd = mch_fopen((char *)tempname, READBIN);
+ // read the names from the file into memory
+ FILE *fd = mch_fopen((char *)tempname, READBIN);
if (fd == NULL) {
EMSG2(_(e_notopen), tempname);
@@ -2783,11 +2735,11 @@ get_cmd_output (
}
fseek(fd, 0L, SEEK_END);
- len = ftell(fd); /* get size of temp file */
+ size_t len = (size_t)ftell(fd); // get size of temp file
fseek(fd, 0L, SEEK_SET);
buffer = xmalloc(len + 1);
- i = (int)fread((char *)buffer, (size_t)1, (size_t)len, fd);
+ size_t i = fread((char *)buffer, 1, len, fd);
fclose(fd);
os_remove((char *)tempname);
if (i != len) {
diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h
index 11891d6f7e..f0f66854d8 100644
--- a/src/nvim/misc1.h
+++ b/src/nvim/misc1.h
@@ -2,6 +2,7 @@
#define NVIM_MISC1_H
#include "nvim/vim.h"
+#include "nvim/os/shell.h"
/* flags for open_line() */
#define OPENLINE_DELSPACES 1 /* delete spaces after cursor */
diff --git a/src/nvim/misc2.c b/src/nvim/misc2.c
index 3c0a1414a6..4b64de1be0 100644
--- a/src/nvim/misc2.c
+++ b/src/nvim/misc2.c
@@ -327,9 +327,10 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
}
}
- set_vim_var_nr(VV_SHELL_ERROR, (long)retval);
- if (do_profiling == PROF_YES)
+ set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T) retval);
+ if (do_profiling == PROF_YES) {
prof_child_exit(&wait_time);
+ }
return retval;
}
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 3ae1a6a890..4611b69424 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -14,6 +14,8 @@
#include "nvim/misc1.h"
#include "nvim/cursor.h"
#include "nvim/buffer_defs.h"
+#include "nvim/memline.h"
+#include "nvim/charset.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.c.generated.h"
@@ -503,3 +505,95 @@ void set_mouse_topline(win_T *wp)
orig_topfill = wp->w_topfill;
}
+///
+/// Return length of line "lnum" for horizontal scrolling.
+///
+static colnr_T scroll_line_len(linenr_T lnum)
+{
+ colnr_T col = 0;
+ char_u *line = ml_get(lnum);
+ if (*line != NUL) {
+ for (;;) {
+ int numchar = chartabsize(line, col);
+ mb_ptr_adv(line);
+ if (*line == NUL) { // don't count the last character
+ break;
+ }
+ col += numchar;
+ }
+ }
+ return col;
+}
+
+///
+/// Find longest visible line number.
+///
+static linenr_T find_longest_lnum(void)
+{
+ linenr_T ret = 0;
+
+ // Calculate maximum for horizontal scrollbar. Check for reasonable
+ // line numbers, topline and botline can be invalid when displaying is
+ // postponed.
+ if (curwin->w_topline <= curwin->w_cursor.lnum &&
+ curwin->w_botline > curwin->w_cursor.lnum &&
+ curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) {
+ long max = 0;
+
+ // Use maximum of all visible lines. Remember the lnum of the
+ // longest line, closest to the cursor line. Used when scrolling
+ // below.
+ for (linenr_T lnum = curwin->w_topline; lnum < curwin->w_botline; lnum++) {
+ colnr_T len = scroll_line_len(lnum);
+ if (len > (colnr_T)max) {
+ max = len;
+ ret = lnum;
+ } else if (len == (colnr_T)max
+ && abs((int)(lnum - curwin->w_cursor.lnum))
+ < abs((int)(ret - curwin->w_cursor.lnum))) {
+ ret = lnum;
+ }
+ }
+ } else {
+ // Use cursor line only.
+ ret = curwin->w_cursor.lnum;
+ }
+
+ return ret;
+}
+
+///
+/// Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise.
+///
+bool mouse_scroll_horiz(int dir)
+{
+ if (curwin->w_p_wrap) {
+ return false;
+ }
+
+ int step = 6;
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
+ step = curwin->w_width;
+ }
+
+ int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
+ if (leftcol < 0) {
+ leftcol = 0;
+ }
+
+ if (curwin->w_leftcol == leftcol) {
+ return false;
+ }
+
+ curwin->w_leftcol = (colnr_T)leftcol;
+
+ // When the line of the cursor is too short, move the cursor to the
+ // longest visible line.
+ if (!virtual_active()
+ && (colnr_T)leftcol > scroll_line_len(curwin->w_cursor.lnum)) {
+ curwin->w_cursor.lnum = find_longest_lnum();
+ curwin->w_cursor.col = 0;
+ }
+
+ return leftcol_changed();
+}
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index c824bcc8f0..0149f7c7c0 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -34,6 +34,12 @@
#define MOUSE_X1 0x300 // Mouse-button X1 (6th)
#define MOUSE_X2 0x400 // Mouse-button X2
+// Direction for nv_mousescroll() and ins_mousescroll()
+#define MSCR_DOWN 0 // DOWN must be FALSE
+#define MSCR_UP 1
+#define MSCR_LEFT -1
+#define MSCR_RIGHT -2
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.h.generated.h"
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 474e25ffeb..6cc56ba3dd 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -14,7 +14,7 @@
#include "nvim/vim.h"
#include "nvim/memory.h"
#include "nvim/log.h"
-#include "nvim/tempfile.h"
+#include "nvim/fileio.h"
#include "nvim/path.h"
#include "nvim/strings.h"
@@ -59,7 +59,7 @@ static void set_vservername(garray_T *srvs)
char *default_server = (srvs->ga_len > 0)
? ((SocketWatcher **)srvs->ga_data)[0]->addr
: NULL;
- set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1);
+ set_vim_var_string(VV_SEND_SERVER, default_server, -1);
}
/// Teardown the server module
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 2895816b8f..75ee11bc9d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2598,11 +2598,10 @@ do_mouse (
end_visual.col = leftcol;
else
end_visual.col = rightcol;
- if (curwin->w_cursor.lnum <
- (start_visual.lnum + end_visual.lnum) / 2)
- end_visual.lnum = end_visual.lnum;
- else
+ if (curwin->w_cursor.lnum >=
+ (start_visual.lnum + end_visual.lnum) / 2) {
end_visual.lnum = start_visual.lnum;
+ }
/* move VIsual to the right column */
start_visual = curwin->w_cursor; /* save the cursor pos */
@@ -3244,9 +3243,9 @@ void clear_showcmd(void)
top = curwin->w_cursor.lnum;
bot = VIsual.lnum;
}
- /* Include closed folds as a whole. */
- hasFolding(top, &top, NULL);
- hasFolding(bot, NULL, &bot);
+ // Include closed folds as a whole.
+ (void)hasFolding(top, &top, NULL);
+ (void)hasFolding(bot, NULL, &bot);
lines = bot - top + 1;
if (VIsual_mode == Ctrl_V) {
@@ -3927,6 +3926,8 @@ static void nv_mousescroll(cmdarg_T *cap)
cap->count0 = 3;
nv_scroll_line(cap);
}
+ } else {
+ mouse_scroll_horiz(cap->arg);
}
curwin->w_redr_status = true;
@@ -5152,9 +5153,10 @@ static void nv_gotofile(cmdarg_T *cap)
ptr = grab_file_name(cap->count1, &lnum);
if (ptr != NULL) {
- /* do autowrite if necessary */
- if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf))
- autowrite(curbuf, false);
+ // do autowrite if necessary
+ if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) {
+ (void)autowrite(curbuf, false);
+ }
setpcmark();
(void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
P_HID(curbuf) ? ECMD_HIDE : 0, curwin);
@@ -7057,18 +7059,17 @@ static void nv_operator(cmdarg_T *cap)
*/
static void set_op_var(int optype)
{
- char_u opchars[3];
-
- if (optype == OP_NOP)
+ if (optype == OP_NOP) {
set_vim_var_string(VV_OP, NULL, 0);
- else {
+ } else {
+ char opchars[3];
int opchar0 = get_op_char(optype);
assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX);
- opchars[0] = (char_u)opchar0;
+ opchars[0] = (char) opchar0;
int opchar1 = get_extra_op_char(optype);
assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX);
- opchars[1] = (char_u)opchar1;
+ opchars[1] = (char) opchar1;
opchars[2] = NUL;
set_vim_var_string(VV_OP, opchars, -1);
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 5f48fdbf3d..601890d133 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -3989,7 +3989,7 @@ format_lines (
if (line_count < 0 && u_save_cursor() == FAIL)
break;
if (next_leader_len > 0) {
- (void)del_bytes((long)next_leader_len, FALSE, FALSE);
+ (void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
(long)-next_leader_len);
} else if (second_indent > 0) { /* the "leader" for FO_Q_SECOND */
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 7b41d256f5..b255d47c18 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1447,7 +1447,7 @@ do_set (
char_u *oldval = NULL; // previous value if *varp
char_u *newval;
char_u *origval = NULL;
- char_u *saved_origval = NULL;
+ char *saved_origval = NULL;
unsigned newlen;
int comma;
int bs;
@@ -1724,7 +1724,7 @@ do_set (
if (!starting && origval != NULL) {
// origval may be freed by
// did_set_string_option(), make a copy.
- saved_origval = vim_strsave(origval);
+ saved_origval = xstrdup((char *) origval);
}
/* Handle side effects, and set the global value for
@@ -1739,11 +1739,10 @@ do_set (
}
if (saved_origval != NULL) {
- char_u buf_type[7];
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_type[7];
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW,
- *(char_u **)varp, -1);
+ set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1);
set_vim_var_string(VV_OPTION_OLD, saved_origval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
apply_autocmds(EVENT_OPTIONSET,
@@ -2057,6 +2056,7 @@ static void didset_options(void)
(void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
(void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
(void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
+ (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
(void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
(void)spell_check_msm();
(void)spell_check_sps();
@@ -2144,6 +2144,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_ep);
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
+ check_string_option(&buf->b_p_tc);
check_string_option(&buf->b_p_dict);
check_string_option(&buf->b_p_tsr);
check_string_option(&buf->b_p_lw);
@@ -2323,7 +2324,7 @@ set_string_option (
char_u *s;
char_u **varp;
char_u *oldval;
- char_u *saved_oldval = NULL;
+ char *saved_oldval = NULL;
char_u *r = NULL;
if (options[opt_idx].var == NULL) /* don't set hidden option */
@@ -2339,7 +2340,7 @@ set_string_option (
*varp = s;
if (!starting) {
- saved_oldval = vim_strsave(oldval);
+ saved_oldval = xstrdup((char *) oldval);
}
if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL,
@@ -2348,10 +2349,10 @@ set_string_option (
// call autocommand after handling side effects
if (saved_oldval != NULL) {
- char_u buf_type[7];
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_type[7];
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, *varp, -1);
+ set_vim_var_string(VV_OPTION_NEW, (char *) (*varp), -1);
set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
apply_autocmds(EVENT_OPTIONSET,
@@ -2984,6 +2985,24 @@ did_set_string_option (
if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
errmsg = e_invarg;
}
+ } else if (gvarp == &p_tc) { // 'tagcase'
+ unsigned int *flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ p = curbuf->b_p_tc;
+ flags = &curbuf->b_tc_flags;
+ } else {
+ p = p_tc;
+ flags = &tc_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *p == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else if (*p == NUL
+ || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_cmp) { // 'casemap'
if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK)
errmsg = e_invarg;
@@ -3800,7 +3819,7 @@ set_bool_option (
msg_source(hl_attr(HLF_W));
MSG_ATTR(_(w_arabic), hl_attr(HLF_W));
- set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_arabic), -1);
+ set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
}
/* set 'delcombine' */
@@ -3847,14 +3866,14 @@ set_bool_option (
options[opt_idx].flags |= P_WAS_SET;
if (!starting) {
- char_u buf_old[2];
- char_u buf_new[2];
- char_u buf_type[7];
- vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%d",
+ char buf_old[2];
+ char buf_new[2];
+ char buf_type[7];
+ vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d",
old_value ? true: false);
- vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%d",
+ vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d",
value ? true: false);
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
@@ -4237,12 +4256,12 @@ set_num_option (
options[opt_idx].flags |= P_WAS_SET;
if (!starting && errmsg == NULL) {
- char_u buf_old[NUMBUFLEN];
- char_u buf_new[NUMBUFLEN];
- char_u buf_type[7];
- vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
- vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%ld", value);
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_old[NUMBUFLEN];
+ char buf_new[NUMBUFLEN];
+ char buf_type[7];
+ vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
+ vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
@@ -5134,6 +5153,10 @@ void unset_global_local_option(char *name, void *from)
case PV_TAGS:
clear_string_option(&buf->b_p_tags);
break;
+ case PV_TC:
+ clear_string_option(&buf->b_p_tc);
+ buf->b_tc_flags = 0;
+ break;
case PV_DEF:
clear_string_option(&buf->b_p_def);
break;
@@ -5187,6 +5210,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
case PV_PATH: return (char_u *)&(curbuf->b_p_path);
case PV_AR: return (char_u *)&(curbuf->b_p_ar);
case PV_TAGS: return (char_u *)&(curbuf->b_p_tags);
+ case PV_TC: return (char_u *)&(curbuf->b_p_tc);
case PV_DEF: return (char_u *)&(curbuf->b_p_def);
case PV_INC: return (char_u *)&(curbuf->b_p_inc);
case PV_DICT: return (char_u *)&(curbuf->b_p_dict);
@@ -5224,6 +5248,8 @@ static char_u *get_varp(vimoption_T *p)
? (char_u *)&(curbuf->b_p_ar) : p->var;
case PV_TAGS: return *curbuf->b_p_tags != NUL
? (char_u *)&(curbuf->b_p_tags) : p->var;
+ case PV_TC: return *curbuf->b_p_tc != NUL
+ ? (char_u *)&(curbuf->b_p_tc) : p->var;
case PV_BKC: return *curbuf->b_p_bkc != NUL
? (char_u *)&(curbuf->b_p_bkc) : p->var;
case PV_DEF: return *curbuf->b_p_def != NUL
@@ -5603,6 +5629,8 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_kp = empty_option;
buf->b_p_path = empty_option;
buf->b_p_tags = empty_option;
+ buf->b_p_tc = empty_option;
+ buf->b_tc_flags = 0;
buf->b_p_def = empty_option;
buf->b_p_inc = empty_option;
buf->b_p_inex = vim_strsave(p_inex);
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 8e74e5036e..87a9a7398c 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/types.h"
+#include "nvim/macros.h" // For EXTERN
// option_defs.h: definition of global variables for settable options
@@ -596,6 +597,14 @@ static char *(p_swb_values[]) =
#define SWB_NEWTAB 0x008
#define SWB_VSPLIT 0x010
EXTERN int p_tbs; ///< 'tagbsearch'
+EXTERN char_u *p_tc; ///< 'tagcase'
+EXTERN unsigned tc_flags; ///< flags from 'tagcase'
+#ifdef IN_OPTION_C
+static char *(p_tc_values[]) = { "followic", "ignore", "match", NULL };
+#endif
+#define TC_FOLLOWIC 0x01
+#define TC_IGNORE 0x02
+#define TC_MATCH 0x04
EXTERN long p_tl; ///< 'taglength'
EXTERN int p_tr; ///< 'tagrelative'
EXTERN char_u *p_tags; ///< 'tags'
@@ -736,6 +745,7 @@ enum {
, BV_SW
, BV_SWF
, BV_TAGS
+ , BV_TC
, BV_TS
, BV_TW
, BV_TX
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index df77c374ec..a743e8c605 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2332,6 +2332,13 @@ return {
defaults={if_true={vi=true}}
},
{
+ full_name='tagcase', abbreviation='tc',
+ type='string', scope={'global', 'buffer'},
+ vim=true,
+ varname='p_tc',
+ defaults={if_true={vi="followic", vim="followic"}}
+ },
+ {
full_name='taglength', abbreviation='tl',
type='number', scope={'global'},
vi_def=true,
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 34d8fde4f1..bc2d37764d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -59,6 +59,23 @@ int os_dirname(char_u *buf, size_t len)
return OK;
}
+/// Check if the given path is a directory and not a symlink to a directory.
+/// @return `true` if `name` is a directory and NOT a symlink to a directory.
+/// `false` if `name` is not a directory or if an error occurred.
+bool os_isrealdir(const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uv_fs_t request;
+ if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) {
+ return false;
+ }
+ if (S_ISLNK(request.statbuf.st_mode)) {
+ return false;
+ } else {
+ return S_ISDIR(request.statbuf.st_mode);
+ }
+}
+
/// Check if the given path is a directory or not.
///
/// @return `true` if `fname` is a directory.
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index cb9a58cc77..2a859a0f7a 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -34,7 +34,6 @@
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/types.h"
#include "nvim/os/os.h"
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 22a3f96cfa..29ff62ef77 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -604,7 +604,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
starstar = true;
// convert the file pattern to a regexp pattern
- int starts_with_dot = (*s == '.');
+ int starts_with_dot = *s == '.';
char_u *pat = file_pat_to_reg_pat(s, e, NULL, false);
if (pat == NULL) {
xfree(buf);
@@ -647,9 +647,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) {
// Find all matching entries.
char_u *name;
- scandir_next_with_dots(NULL /* initialize */);
- while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) {
- if ((name[0] != '.' || starts_with_dot)
+ scandir_next_with_dots(NULL); // initialize
+ while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) {
+ if ((name[0] != '.'
+ || starts_with_dot
+ || ((flags & EW_DODOT)
+ && name[1] != NUL && (name[1] != '.' || name[2] != NUL)))
&& ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
|| ((flags & EW_NOTWILD)
&& fnamencmp(path + (s - buf), name, e - s) == 0))) {
@@ -1220,7 +1223,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
recursive = false;
- return (ga.ga_data != NULL) ? OK : FAIL;
+ return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL;
}
@@ -1495,13 +1498,12 @@ void simplify_filename(char_u *filename)
} while (*p != NUL);
}
-static char_u *eval_includeexpr(char_u *ptr, size_t len)
+static char *eval_includeexpr(const char *const ptr, const size_t len)
{
- assert(len <= INT_MAX);
- set_vim_var_string(VV_FNAME, ptr, (int)len);
- char_u *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
- was_set_insecurely((char_u *)"includeexpr",
- OPT_LOCAL));
+ set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t) len);
+ char *res = (char *) eval_to_string_safe(
+ curbuf->b_p_inex, NULL, was_set_insecurely((char_u *)"includeexpr",
+ OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
}
@@ -1523,7 +1525,7 @@ find_file_name_in_path (
char_u *tofree = NULL;
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = eval_includeexpr(ptr, len);
+ tofree = (char_u *) eval_includeexpr((char *) ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = STRLEN(ptr);
@@ -1540,7 +1542,7 @@ find_file_name_in_path (
*/
if (file_name == NULL
&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = eval_includeexpr(ptr, len);
+ tofree = (char_u *) eval_includeexpr((char *) ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = STRLEN(ptr);
@@ -1924,7 +1926,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file,
/// If FAIL is returned, *num_file and *file are either
/// unchanged or *num_file is set to 0 and *file is set to
/// NULL or points to "".
-int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
+int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
int flags)
{
int retval;
@@ -1932,7 +1934,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
char_u *p;
int non_suf_match; /* number without matching suffix */
- retval = gen_expand_wildcards(num_pat, pat, num_file, file, flags);
+ retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags);
/* When keeping all matches, return here */
if ((flags & EW_KEEPALL) || retval == FAIL)
@@ -1944,18 +1946,20 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
if (*p_wig) {
char_u *ffname;
- /* check all files in (*file)[] */
- for (i = 0; i < *num_file; ++i) {
- ffname = (char_u *)FullName_save((char *)(*file)[i], FALSE);
- if (ffname == NULL) /* out of memory */
+ // check all filess in (*files)[]
+ for (i = 0; i < *num_files; i++) {
+ ffname = (char_u *)FullName_save((char *)(*files)[i], false);
+ if (ffname == NULL) { // out of memory
break;
- if (match_file_list(p_wig, (*file)[i], ffname)) {
- /* remove this matching file from the list */
- xfree((*file)[i]);
- for (j = i; j + 1 < *num_file; ++j)
- (*file)[j] = (*file)[j + 1];
- --*num_file;
- --i;
+ }
+ if (match_file_list(p_wig, (*files)[i], ffname)) {
+ // remove this matching files from the list
+ xfree((*files)[i]);
+ for (j = i; j + 1 < *num_files; j++) {
+ (*files)[j] = (*files)[j + 1];
+ }
+ (*num_files)--;
+ i--;
}
xfree(ffname);
}
@@ -1964,26 +1968,28 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
/*
* Move the names where 'suffixes' match to the end.
*/
- if (*num_file > 1) {
+ if (*num_files > 1) {
non_suf_match = 0;
- for (i = 0; i < *num_file; ++i) {
- if (!match_suffix((*file)[i])) {
- /*
- * Move the name without matching suffix to the front
- * of the list.
- */
- p = (*file)[i];
- for (j = i; j > non_suf_match; --j)
- (*file)[j] = (*file)[j - 1];
- (*file)[non_suf_match++] = p;
+ for (i = 0; i < *num_files; i++) {
+ if (!match_suffix((*files)[i])) {
+ //
+ // Move the name without matching suffix to the front
+ // of the list.
+ //
+ p = (*files)[i];
+ for (j = i; j > non_suf_match; j--) {
+ (*files)[j] = (*files)[j - 1];
+ }
+ (*files)[non_suf_match++] = p;
}
}
}
// Free empty array of matches
- if (*num_file == 0) {
- xfree(*file);
- *file = NULL;
+ if (*num_files == 0) {
+ xfree(*files);
+ *files = NULL;
+ return FAIL;
}
return retval;
diff --git a/src/nvim/path.h b/src/nvim/path.h
index eac367d0ac..88e5935c24 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -21,6 +21,8 @@
/* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
* is used when executing commands and EW_SILENT for interactive expanding. */
#define EW_ALLLINKS 0x1000 // also links not pointing to existing file
+#define EW_DODOT 0x4000 // also files starting with a dot
+#define EW_EMPTYOK 0x8000 // no matches is not an error
/// Return value for the comparison of two files. Also @see path_full_compare.
typedef enum file_comparison {
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 1a5ef991dc..8a9c86e88d 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -4276,7 +4276,8 @@ msgstr ""
"&Abrir para lectura únicamente\n"
"&Editar de todas formas\n"
"&Recuperar\n"
-"&Borrar&Salir\n"
+"&Borrar\n"
+"&Salir\n"
"&Abortar"
#.
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index 729697eee3..171e155689 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -11,10 +11,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: vim 7.4b\n"
+"Project-Id-Version: vim 7.4\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2013-08-03 17:14+0200\n"
+"POT-Creation-Date: 2015-08-11 20:58+0200\n"
+"PO-Revision-Date: 2015-08-11 22:02+0200\n"
"Last-Translator: Vlad Sandrini <vlad.gently@gmail.com>\n"
"Language-Team: Italian Antonio Colombo <azc100@gmail."
"com> Vlad Sandrini <vlad.gently@gmail."
@@ -104,11 +104,6 @@ msgstr "E84: Nessun buffer risulta modificato"
msgid "E85: There is no listed buffer"
msgstr "E85: Non c'è alcun buffer elencato"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Non esiste il buffer %<PRId64>"
-
#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Non posso oltrepassare l'ultimo buffer"
@@ -128,7 +123,7 @@ msgstr ""
#. wrap around (may cause duplicates)
#: ../buffer.c:1423
msgid "W14: Warning: List of file names overflow"
-msgstr "W14: Attenzione: Superato limite della lista dei nomi di file"
+msgstr "W14: Avviso: Superato limite della lista dei nomi di file"
#: ../buffer.c:1555 ../quickfix.c:3361
#, c-format
@@ -148,7 +143,7 @@ msgstr "E94: Nessun buffer corrispondente a %s"
#: ../buffer.c:2161
#, c-format
msgid "line %<PRId64>"
-msgstr "linea %<PRId64>"
+msgstr "riga %<PRId64>"
#: ../buffer.c:2233
msgid "E95: Buffer with this name already exists"
@@ -181,17 +176,17 @@ msgstr "[in sola lettura]"
#: ../buffer.c:2524
#, c-format
msgid "1 line --%d%%--"
-msgstr "1 linea --%d%%--"
+msgstr "1 riga --%d%%--"
#: ../buffer.c:2526
#, c-format
msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> linee --%d%%--"
+msgstr "%<PRId64> righe --%d%%--"
#: ../buffer.c:2530
#, c-format
msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
-msgstr "linea %<PRId64> di %<PRId64> --%d%%-- col "
+msgstr "riga %<PRId64> di %<PRId64> --%d%%-- col "
#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
msgid "[No Name]"
@@ -250,7 +245,7 @@ msgstr "Segni per %s:"
#: ../buffer.c:4543
#, c-format
msgid " line=%<PRId64> id=%d name=%s"
-msgstr " linea=%<PRId64> id=%d, nome=%s"
+msgstr " riga=%<PRId64> id=%d, nome=%s"
#: ../cursor_shape.c:68
msgid "E545: Missing colon"
@@ -346,11 +341,11 @@ msgstr " modalità ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
#: ../edit.c:85
msgid " Whole line completion (^L^N^P)"
-msgstr " Completamento Linea Intera (^L^N^P)"
+msgstr " Completamento riga intera (^L^N^P)"
#: ../edit.c:86
msgid " File name completion (^F^N^P)"
-msgstr " Completamento nomi File (^F^N^P)"
+msgstr " Completamento nomi file (^F^N^P)"
#: ../edit.c:87
msgid " Tag completion (^]^N^P)"
@@ -374,7 +369,7 @@ msgstr " Completamento Thesaurus (^T^N^P)"
#: ../edit.c:93
msgid " Command-line completion (^V^N^P)"
-msgstr " Completamento linea comandi (^V^N^P)"
+msgstr " Completamento riga comandi (^V^N^P)"
#: ../edit.c:94
msgid " User defined completion (^U^N^P)"
@@ -452,7 +447,7 @@ msgstr "Ritorno all'originale"
#: ../edit.c:4621
msgid "Word from other line"
-msgstr "Parola da un'altra linea"
+msgstr "Parola da un'altra riga"
#: ../edit.c:4624
msgid "The only match"
@@ -680,6 +675,11 @@ msgstr "E696: Manca virgola nella Lista: %s"
msgid "E697: Missing end of List ']': %s"
msgstr "E697: Manca ']' a fine Lista: %s"
+#: ../eval.c:5750
+#, c-format
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!"
+
#: ../eval.c:6475
#, c-format
msgid "E720: Missing colon in Dictionary: %s"
@@ -754,15 +754,15 @@ msgstr "E785: complete() può essere usata solo in modalità inserimento"
msgid "&Ok"
msgstr "&OK"
-#: ../eval.c:8676
-#, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E737: Chiave già esistente: %s"
-
#: ../eval.c:8692
msgid "extend() argument"
msgstr "argomento di extend()"
+#: ../eval.c:9345
+#, c-format
+msgid "E737: Key already exists: %s"
+msgstr "E737: Chiave già esistente: %s"
+
#: ../eval.c:8915
msgid "map() argument"
msgstr "argomento di map()"
@@ -774,7 +774,7 @@ msgstr "argomento di filter()"
#: ../eval.c:9229
#, c-format
msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld linee: "
+msgstr "+-%s%3ld righe: "
#: ../eval.c:9291
#, c-format
@@ -1043,21 +1043,21 @@ msgstr "> %d, Esa %08x, Ottale %o"
#: ../ex_cmds.c:684
msgid "E134: Move lines into themselves"
-msgstr "E134: Movimento di linee verso se stesse"
+msgstr "E134: Movimento di righe verso se stesse"
#: ../ex_cmds.c:747
msgid "1 line moved"
-msgstr "1 linea mossa"
+msgstr "1 riga mossa"
#: ../ex_cmds.c:749
#, c-format
msgid "%<PRId64> lines moved"
-msgstr "%<PRId64> linee mosse"
+msgstr "%<PRId64> righe mosse"
#: ../ex_cmds.c:1175
#, c-format
msgid "%<PRId64> lines filtered"
-msgstr "%<PRId64> linee filtrate"
+msgstr "%<PRId64> righe filtrate"
#: ../ex_cmds.c:1194
msgid "E135: *Filter* Autocommands must not change current buffer"
@@ -1070,7 +1070,7 @@ msgstr "[Non salvato dopo l'ultima modifica]\n"
#: ../ex_cmds.c:1424
#, c-format
msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s nella linea: "
+msgstr "%sviminfo: %s nella riga: "
#: ../ex_cmds.c:1431
msgid "E136: viminfo: Too many errors, skipping rest of file"
@@ -1239,12 +1239,12 @@ msgstr "%<PRId64> sostituzioni"
#: ../ex_cmds.c:4392
msgid " on 1 line"
-msgstr " in 1 linea"
+msgstr " in 1 riga"
#: ../ex_cmds.c:4395
#, c-format
msgid " on %<PRId64> lines"
-msgstr " in %<PRId64> linee"
+msgstr " in %<PRId64> righe"
#: ../ex_cmds.c:4438
msgid "E147: Cannot do :global recursive"
@@ -1257,7 +1257,7 @@ msgstr "E148: Manca espressione regolare nel comando 'global'"
#: ../ex_cmds.c:4508
#, c-format
msgid "Pattern found in every line: %s"
-msgstr "Espressione trovata su ogni linea: %s"
+msgstr "Espressione trovata su ogni riga: %s"
#: ../ex_cmds.c:4510
#, c-format
@@ -1355,6 +1355,11 @@ msgstr "E158: Nome buffer non valido: %s"
msgid "E157: Invalid sign ID: %<PRId64>"
msgstr "E157: ID 'sign' non valido: %<PRId64>"
+#: ../ex_cmds.c:5517
+#, c-format
+msgid "E885: Not possible to change sign %s"
+msgstr "E885: Impossibile cambiare segno %s"
+
#: ../ex_cmds.c:6066
msgid " (not supported)"
msgstr " (non supportata)"
@@ -1370,7 +1375,7 @@ msgstr "Entro modalità Debug. Batti \"cont\" per continuare."
#: ../ex_cmds2.c:143 ../ex_docmd.c:759
#, c-format
msgid "line %<PRId64>: %s"
-msgstr "linea %<PRId64>: %s"
+msgstr "riga %<PRId64>: %s"
#: ../ex_cmds2.c:145
#, c-format
@@ -1380,7 +1385,7 @@ msgstr "com: %s"
#: ../ex_cmds2.c:322
#, c-format
msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "Pausa in \"%s%s\" linea %<PRId64>"
+msgstr "Pausa in \"%s%s\" riga %<PRId64>"
#: ../ex_cmds2.c:581
#, c-format
@@ -1394,7 +1399,7 @@ msgstr "Nessun 'breakpoint' definito"
#: ../ex_cmds2.c:617
#, c-format
msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s linea %<PRId64>"
+msgstr "%3d %s %s riga %<PRId64>"
#: ../ex_cmds2.c:942
msgid "E750: First use \":profile start {fname}\""
@@ -1417,7 +1422,7 @@ msgstr "E162: Buffer \"%s\" non salvato dopo modifica"
#: ../ex_cmds2.c:1480
msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr ""
-"Attenzione: Entrato in altro buffer inaspettatamente (controllare "
+"Avviso: Entrato in altro buffer inaspettatamente (controllare "
"autocomandi)"
#: ../ex_cmds2.c:1826
@@ -1465,7 +1470,7 @@ msgstr "non riesco ad eseguire \"%s\""
#: ../ex_cmds2.c:2520
#, c-format
msgid "line %<PRId64>: could not source \"%s\""
-msgstr "linea %<PRId64>: non riesco ad eseguire \"%s\""
+msgstr "riga %<PRId64>: non riesco ad eseguire \"%s\""
#: ../ex_cmds2.c:2535
#, c-format
@@ -1475,7 +1480,7 @@ msgstr "eseguo \"%s\""
#: ../ex_cmds2.c:2537
#, c-format
msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "linea %<PRId64>: eseguo \"%s\""
+msgstr "riga %<PRId64>: eseguo \"%s\""
#: ../ex_cmds2.c:2693
#, c-format
@@ -1504,7 +1509,7 @@ msgstr "gestore di errore"
#: ../ex_cmds2.c:3020
msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: Attenzione: Separatore di linea errato, forse manca ^M"
+msgstr "W15: Avviso: Separatore di riga errato, forse manca ^M"
#: ../ex_cmds2.c:3139
msgid "E167: :scriptencoding used outside of a sourced file"
@@ -1606,10 +1611,10 @@ msgstr "E174: Il comando esiste già: aggiungi ! per sostituirlo"
#: ../ex_docmd.c:4432
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-" Nome Arg. Inter Completo Definizione"
+" Nome Arg. Indir. Completo Definizione"
#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
@@ -1635,6 +1640,10 @@ msgstr "E178: Valore predefinito del contatore non valido"
msgid "E179: argument required for -complete"
msgstr "E179: argomento necessario per -complete"
+#: ../ex_docmd.c:4923
+msgid "E179: argument required for -addr"
+msgstr "E179: argomento necessario per -addr"
+
#: ../ex_docmd.c:4635
#, c-format
msgid "E181: Invalid attribute: %s"
@@ -1658,6 +1667,11 @@ msgstr "E841: Nome riservato, non usabile in un comando definito dall'utente"
msgid "E184: No such user-defined command: %s"
msgstr "E184: Comando definito dall'utente %s inesistente"
+#: ../ex_docmd.c:5516
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: Tipo di indirizzo non valido: %s"
+
#: ../ex_docmd.c:5219
#, c-format
msgid "E180: Invalid complete value: %s"
@@ -1666,7 +1680,7 @@ msgstr "E180: Valore %s non valido per 'complete'"
#: ../ex_docmd.c:5225
msgid "E468: Completion argument only allowed for custom completion"
msgstr ""
-"E468: Argomento di completamento permesso solo per completamento "
+"E468: Argomento di completamento consentito solo per completamento "
"personalizzato"
#: ../ex_docmd.c:5231
@@ -1815,7 +1829,7 @@ msgstr "Eccezione scartata: %s"
#: ../ex_eval.c:588 ../ex_eval.c:634
#, c-format
msgid "%s, line %<PRId64>"
-msgstr "%s, linea %<PRId64>"
+msgstr "%s, riga %<PRId64>"
#. always scroll up, don't overwrite
#: ../ex_eval.c:608
@@ -1961,7 +1975,7 @@ msgstr ""
#: ../ex_getln.c:5047
msgid "Command Line"
-msgstr "Linea di Comando"
+msgstr "Riga di Comando"
#: ../ex_getln.c:5048
msgid "Search String"
@@ -1973,7 +1987,7 @@ msgstr "Espressione"
#: ../ex_getln.c:5050
msgid "Input Line"
-msgstr "Linea di Input"
+msgstr "Riga di Input"
#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
@@ -2091,7 +2105,7 @@ msgstr "[manca CR]"
#: ../fileio.c:1819
msgid "[long lines split]"
-msgstr "[linee lunghe divise]"
+msgstr "[righe lunghe divise]"
#: ../fileio.c:1823 ../fileio.c:3512
msgid "[NOT converted]"
@@ -2104,12 +2118,12 @@ msgstr "[convertito]"
#: ../fileio.c:1831
#, c-format
msgid "[CONVERSION ERROR in line %<PRId64>]"
-msgstr "[ERRORE DI CONVERSIONE alla linea %<PRId64>]"
+msgstr "[ERRORE DI CONVERSIONE alla riga %<PRId64>]"
#: ../fileio.c:1835
#, c-format
msgid "[ILLEGAL BYTE in line %<PRId64>]"
-msgstr "[BYTE NON VALIDO alla linea %<PRId64>]"
+msgstr "[BYTE NON VALIDO alla riga %<PRId64>]"
#: ../fileio.c:1838
msgid "[READ ERRORS]"
@@ -2137,7 +2151,7 @@ msgstr "E203: Buffer in scrittura cancellato o scaricato dagli autocomandi"
#: ../fileio.c:2486
msgid "E204: Autocommand changed number of lines in unexpected way"
-msgstr "E204: L'autocomando ha modificato numero linee in maniera imprevista"
+msgstr "E204: L'autocomando ha modificato numero righe in maniera imprevista"
#: ../fileio.c:2548 ../fileio.c:2565
msgid "is not a file or writable device"
@@ -2213,7 +2227,7 @@ msgid ""
"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
"override)"
msgstr ""
-"E513: errore in scrittura, conversione fallita alla linea %<PRId64> (rendere "
+"E513: errore in scrittura, conversione fallita alla riga %<PRId64> (rendere "
"'fenc' nullo per eseguire comunque)"
#: ../fileio.c:3448
@@ -2227,7 +2241,7 @@ msgstr " ERRORE DI CONVERSIONE"
#: ../fileio.c:3509
#, c-format
msgid " in line %<PRId64>;"
-msgstr " alla linea %<PRId64>;"
+msgstr " alla riga %<PRId64>;"
#: ../fileio.c:3519
msgid "[Device]"
@@ -2271,7 +2285,7 @@ msgid ""
"WARNING: Original file may be lost or damaged\n"
msgstr ""
"\n"
-"ATTENZIONE: Il file originale può essere perso o danneggiato\n"
+"AVVISO: Il file originale può essere perso o danneggiato\n"
#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
@@ -2303,12 +2317,12 @@ msgstr "[in formato UNIX]"
#: ../fileio.c:3831
msgid "1 line, "
-msgstr "1 linea, "
+msgstr "1 riga, "
#: ../fileio.c:3833
#, c-format
msgid "%<PRId64> lines, "
-msgstr "%<PRId64> linee,"
+msgstr "%<PRId64> righe,"
#: ../fileio.c:3836
msgid "1 character"
@@ -2325,14 +2339,14 @@ msgstr "[noeol]"
#: ../fileio.c:3849
msgid "[Incomplete last line]"
-msgstr "[Manca carattere di fine linea]"
+msgstr "[Manca carattere di fine riga]"
#. don't overwrite messages here
#. must give this prompt
#. don't use emsg() here, don't want to flush the buffers
#: ../fileio.c:3865
msgid "WARNING: The file has been changed since reading it!!!"
-msgstr "ATTENZIONE: File modificato dopo essere stato letto dall'Editor!!!"
+msgstr "AVVISO: File modificato dopo essere stato letto dall'Editor!!!"
#: ../fileio.c:3867
msgid "Do you really want to write to it"
@@ -2368,7 +2382,7 @@ msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
"well"
msgstr ""
-"W12: Attenzione: File \"%s\" modificato su disco ed anche nel buffer di Vim"
+"W12: Avviso: File \"%s\" modificato su disco ed anche nel buffer di Vim"
#: ../fileio.c:4907
msgid "See \":help W12\" for more info."
@@ -2377,7 +2391,7 @@ msgstr "Vedere \":help W12\" per ulteriori informazioni."
#: ../fileio.c:4910
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
-msgstr "W11: Attenzione: File \"%s\" modificato dopo l'apertura"
+msgstr "W11: Avviso: File \"%s\" modificato dopo l'apertura"
#: ../fileio.c:4911
msgid "See \":help W11\" for more info."
@@ -2386,7 +2400,7 @@ msgstr "Vedere \":help W11\" per ulteriori informazioni."
#: ../fileio.c:4914
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
-msgstr "W16: Attenzione: Modo File \"%s\" modificato dopo l'apertura"
+msgstr "W16: Avviso: Modo File \"%s\" modificato dopo l'apertura"
#: ../fileio.c:4915
msgid "See \":help W16\" for more info."
@@ -2395,11 +2409,11 @@ msgstr "Vedere \":help W16\" per ulteriori informazioni."
#: ../fileio.c:4927
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
-msgstr "W13: Attenzione: Il file \"%s\" risulta creato dopo l'apertura"
+msgstr "W13: Avviso: Il file \"%s\" risulta creato dopo l'apertura"
#: ../fileio.c:4947
msgid "Warning"
-msgstr "Attenzione"
+msgstr "Avviso"
#: ../fileio.c:4948
msgid ""
@@ -2513,7 +2527,7 @@ msgstr "E351: Non posso cancellare piegatura con il 'foldmethod' in uso"
#: ../fold.c:1784
#, c-format
msgid "+--%3ld lines folded "
-msgstr "+--%3ld linee piegate"
+msgstr "+--%3ld righe piegate"
#. buffer has already been read
#: ../getchar.c:273
@@ -2679,7 +2693,7 @@ msgstr "E364: Chiamata a libreria fallita per \"%s()\""
#: ../globals.h:1026
msgid "E19: Mark has invalid line number"
-msgstr "E19: 'Mark' con numero linea non valido"
+msgstr "E19: 'Mark' con numero riga non valido"
#: ../globals.h:1027
msgid "E20: Mark not set"
@@ -2720,7 +2734,7 @@ msgstr "E29: Ancora nessun testo inserito"
#: ../globals.h:1038
msgid "E30: No previous command line"
-msgstr "E30: Nessuna linea comandi precedente"
+msgstr "E30: Nessuna riga comandi precedente"
#: ../globals.h:1039
msgid "E31: No such mapping"
@@ -2947,6 +2961,11 @@ msgstr "E363: l'espressione usa troppa memoria rispetto a 'maxmempattern'"
msgid "E749: empty buffer"
msgstr "E749: buffer vuoto"
+#: ../globals.h:1226
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: Non esiste il buffer %<PRId64>"
+
#: ../globals.h:1108
msgid "E682: Invalid search pattern or delimiter"
msgstr "E682: Espressione o delimitatore di ricerca non validi"
@@ -3260,11 +3279,11 @@ msgid ""
" # line"
msgstr ""
"\n"
-" # linea"
+" # riga"
#: ../if_cscope.c:1713
msgid "filename / context / line\n"
-msgstr "nomefile / contest / linea\n"
+msgstr "nomefile / contest / riga\n"
#: ../if_cscope.c:1809
#, c-format
@@ -3326,16 +3345,16 @@ msgstr "Non posso aprire come script output: \""
#: ../main.c:1622
msgid "Vim: Warning: Output is not to a terminal\n"
-msgstr "Vim: Attenzione: Output non diretto a un terminale\n"
+msgstr "Vim: Avviso: Output non diretto a un terminale\n"
#: ../main.c:1624
msgid "Vim: Warning: Input is not from a terminal\n"
-msgstr "Vim: Attenzione: Input non proveniente da un terminale\n"
+msgstr "Vim: Avviso: Input non proveniente da un terminale\n"
#. just in case..
#: ../main.c:1891
msgid "pre-vimrc command line"
-msgstr "linea comandi prima di vimrc"
+msgstr "riga comandi prima di vimrc"
#: ../main.c:1964
#, c-format
@@ -3528,7 +3547,7 @@ msgstr "+\t\t\tPosizionati alla fine del file"
#: ../main.c:2231
msgid "+<lnum>\t\tStart at line <lnum>"
-msgstr "+<lnum>\t\tPosizionati alla linea <lnum>"
+msgstr "+<lnum>\t\tPosizionati alla riga <lnum>"
#: ../main.c:2232
msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
@@ -3589,7 +3608,7 @@ msgid ""
"mark line col file/text"
msgstr ""
"\n"
-"mark linea col.file/testo"
+"mark riga col.file/testo"
#. Highlight title
#: ../mark.c:789
@@ -3598,7 +3617,7 @@ msgid ""
" jump line col file/text"
msgstr ""
"\n"
-" salt.linea col.file/testo"
+" salt.riga col.file/testo"
#. Highlight title
#: ../mark.c:831
@@ -3607,7 +3626,7 @@ msgid ""
"change line col text"
msgstr ""
"\n"
-"modif linea col testo"
+"modif riga col testo"
#: ../mark.c:1238
msgid ""
@@ -3767,7 +3786,7 @@ msgstr "File originale \"%s\""
#: ../memline.c:995
msgid "E308: Warning: Original file may have been changed"
msgstr ""
-"E308: Attenzione: il file originale può essere stato modificato nel frattempo"
+"E308: Avviso: il file originale può essere stato modificato nel frattempo"
#: ../memline.c:1061
#, c-format
@@ -3776,7 +3795,7 @@ msgstr "E309: Impossibile leggere blocco 1 da %s"
#: ../memline.c:1065
msgid "???MANY LINES MISSING"
-msgstr "???MOLTE LINEE MANCANTI"
+msgstr "???MOLTE RIGHE MANCANTI"
#: ../memline.c:1076
msgid "???LINE COUNT WRONG"
@@ -3788,7 +3807,7 @@ msgstr "???BLOCCO VUOTO"
#: ../memline.c:1103
msgid "???LINES MISSING"
-msgstr "???LINEE MANCANTI"
+msgstr "???RIGHE MANCANTI"
#: ../memline.c:1128
#, c-format
@@ -3801,12 +3820,12 @@ msgstr "???BLOCCO MANCANTE"
#: ../memline.c:1147
msgid "??? from here until ???END lines may be messed up"
-msgstr "??? da qui fino a ???END le linee possono essere fuori ordine"
+msgstr "??? da qui fino a ???END le righe possono essere fuori ordine"
#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr ""
-"??? da qui fino a ???END linee possono essere state inserite/cancellate"
+"??? da qui fino a ???END righe possono essere state inserite/cancellate"
#: ../memline.c:1181
msgid "???END"
@@ -3819,7 +3838,7 @@ msgstr "E311: Recupero Interrotto"
#: ../memline.c:1243
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
-msgstr "E312: Errori durante recupero; controlla linee che iniziano con ???"
+msgstr "E312: Errori durante recupero; controlla righe che iniziano con ???"
#: ../memline.c:1245
msgid "See \":help E312\" for more information."
@@ -3980,12 +3999,12 @@ msgstr "E314: Preservazione fallita"
#: ../memline.c:1819
#, c-format
msgid "E315: ml_get: invalid lnum: %<PRId64>"
-msgstr "E315: ml_get: numero linea non valido: %<PRId64>"
+msgstr "E315: ml_get: numero riga non valido: %<PRId64>"
#: ../memline.c:1851
#, c-format
msgid "E316: ml_get: cannot find line %<PRId64>"
-msgstr "E316: ml_get: non riesco a trovare la linea %<PRId64>"
+msgstr "E316: ml_get: non riesco a trovare la riga %<PRId64>"
#: ../memline.c:2236
msgid "E317: pointer block id wrong 3"
@@ -4010,7 +4029,7 @@ msgstr "cancellato blocco 1?"
#: ../memline.c:2707
#, c-format
msgid "E320: Cannot find line %<PRId64>"
-msgstr "E320: Non riesco a trovare la linea %<PRId64>"
+msgstr "E320: Non riesco a trovare la riga %<PRId64>"
#: ../memline.c:2916
msgid "E317: pointer block id wrong"
@@ -4023,12 +4042,12 @@ msgstr "pe_line_count a zero"
#: ../memline.c:2955
#, c-format
msgid "E322: line number out of range: %<PRId64> past the end"
-msgstr "E322: numero linea non ammissibile: %<PRId64> dopo la fine"
+msgstr "E322: numero riga non ammissibile: %<PRId64> dopo la fine"
#: ../memline.c:2959
#, c-format
msgid "E323: line count wrong in block %<PRId64>"
-msgstr "E323: contatore linee errato nel blocco %<PRId64>"
+msgstr "E323: contatore righe errato nel blocco %<PRId64>"
#: ../memline.c:2999
msgid "Stack size increases"
@@ -4242,7 +4261,7 @@ msgstr "Errore/i eseguendo %s:"
#: ../message.c:445
#, c-format
msgid "line %4ld:"
-msgstr "linea %4ld:"
+msgstr "riga %4ld:"
#: ../message.c:617
#, c-format
@@ -4264,7 +4283,7 @@ msgstr "Premi INVIO o un comando per proseguire"
#: ../message.c:1843
#, c-format
msgid "%s line %<PRId64>"
-msgstr "%s linea %<PRId64>"
+msgstr "%s riga %<PRId64>"
#: ../message.c:2392
msgid "-- More --"
@@ -4272,7 +4291,7 @@ msgstr "-- Ancora --"
#: ../message.c:2398
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr " SPAZIO/d/j: schermo/pagina/linea giù, b/u/k: su, q: abbandona "
+msgstr " SPAZIO/d/j: schermo/pagina/riga giù, b/u/k: su, q: abbandona "
#: ../message.c:3021 ../message.c:3031
msgid "Question"
@@ -4324,7 +4343,7 @@ msgstr "E767: Troppi argomenti per printf()"
#: ../misc1.c:2256
msgid "W10: Warning: Changing a readonly file"
-msgstr "W10: Attenzione: Modifica a un file in sola-lettura"
+msgstr "W10: Avviso: Modifica a un file in sola-lettura"
#: ../misc1.c:2537
msgid "Type number and <Enter> or click with mouse (empty cancels): "
@@ -4337,21 +4356,21 @@ msgstr "Inserire numero e <Invio> (vuoto per annullare): "
#: ../misc1.c:2585
msgid "1 more line"
-msgstr "1 linea in più"
+msgstr "1 riga in più"
#: ../misc1.c:2588
msgid "1 line less"
-msgstr "1 linea in meno"
+msgstr "1 riga in meno"
#: ../misc1.c:2593
#, c-format
msgid "%<PRId64> more lines"
-msgstr "%<PRId64> linee in più"
+msgstr "%<PRId64> righe in più"
#: ../misc1.c:2596
#, c-format
msgid "%<PRId64> fewer lines"
-msgstr "%<PRId64> linee in meno"
+msgstr "%<PRId64> righe in meno"
#: ../misc1.c:2599
msgid " (Interrupted)"
@@ -4376,7 +4395,7 @@ msgstr "E774: opzione 'operatorfunc' non impostata"
#: ../normal.c:2637
msgid "Warning: terminal cannot highlight"
-msgstr "Attenzione: il terminale non è in grado di evidenziare"
+msgstr "Avviso: il terminale non è in grado di evidenziare"
#: ../normal.c:2807
msgid "E348: No string under cursor"
@@ -4405,36 +4424,36 @@ msgstr "Batti :quit<Invio> per uscire da Vim"
#: ../ops.c:248
#, c-format
msgid "1 line %sed 1 time"
-msgstr "1 linea %sa 1 volta"
+msgstr "1 riga %sa 1 volta"
#: ../ops.c:250
#, c-format
msgid "1 line %sed %d times"
-msgstr "1 linea %sa %d volte"
+msgstr "1 riga %sa %d volte"
#: ../ops.c:253
#, c-format
msgid "%<PRId64> lines %sed 1 time"
-msgstr "%<PRId64> linee %se 1 volta"
+msgstr "%<PRId64> righe %se 1 volta"
#: ../ops.c:256
#, c-format
msgid "%<PRId64> lines %sed %d times"
-msgstr "%<PRId64> linee %se %d volte"
+msgstr "%<PRId64> righe %se %d volte"
#: ../ops.c:592
#, c-format
msgid "%<PRId64> lines to indent... "
-msgstr "%<PRId64> linee da rientrare... "
+msgstr "%<PRId64> righe da rientrare... "
#: ../ops.c:634
msgid "1 line indented "
-msgstr "1 linea rientrata "
+msgstr "1 riga rientrata "
#: ../ops.c:636
#, c-format
msgid "%<PRId64> lines indented "
-msgstr "%<PRId64> linee rientrate "
+msgstr "%<PRId64> righe rientrate "
#: ../ops.c:938
msgid "E748: No previously used register"
@@ -4447,30 +4466,30 @@ msgstr "non riesco a salvare in un registro; cancello comunque"
#: ../ops.c:1929
msgid "1 line changed"
-msgstr "1 linea cambiata"
+msgstr "1 riga cambiata"
#: ../ops.c:1931
#, c-format
msgid "%<PRId64> lines changed"
-msgstr "%<PRId64> linee cambiate"
+msgstr "%<PRId64> righe cambiate"
#: ../ops.c:2521
msgid "block of 1 line yanked"
-msgstr "blocco di 1 linea messo in registro"
+msgstr "blocco di 1 riga messo in registro"
#: ../ops.c:2523
msgid "1 line yanked"
-msgstr "1 linea messa in registro"
+msgstr "1 riga messa in registro"
#: ../ops.c:2525
#, c-format
msgid "block of %<PRId64> lines yanked"
-msgstr "blocco di %<PRId64> linee messo in registro"
+msgstr "blocco di %<PRId64> righe messo in registro"
#: ../ops.c:2528
#, c-format
msgid "%<PRId64> lines yanked"
-msgstr "%<PRId64> linee messe in registro"
+msgstr "%<PRId64> righe messe in registro"
#: ../ops.c:2710
#, c-format
@@ -4503,6 +4522,13 @@ msgstr ""
msgid "E574: Unknown register type %d"
msgstr "E574: Tipo di registro sconosciuto: %d"
+#: ../ops.c:4897
+msgid ""
+"E883: search pattern and expression register may not contain two or more "
+"lines"
+msgstr "E883: espressione di ricerca e registro dell'espressione non possono "
+"contenere due o più righe"
+
#: ../ops.c:5089
#, c-format
msgid "%<PRId64> Cols; "
@@ -4514,7 +4540,7 @@ msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Bytes"
msgstr ""
-"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; "
+"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; "
"%<PRId64> di %<PRId64> Caratt."
#: ../ops.c:5105
@@ -4523,7 +4549,7 @@ msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes"
msgstr ""
-"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; "
+"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; "
"%<PRId64> di %<PRId64> Caratt.; %<PRId64> di %<PRId64> Byte"
#: ../ops.c:5123
@@ -4532,7 +4558,7 @@ msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
"%<PRId64> of %<PRId64>"
msgstr ""
-"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
+"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
"Caratt. %<PRId64> di %<PRId64>"
#: ../ops.c:5133
@@ -4541,7 +4567,7 @@ msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>"
msgstr ""
-"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
+"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
"Caratt. %<PRId64> di %<PRId64>; Byte %<PRId64> di %<PRId64>"
#: ../ops.c:5146
@@ -4587,6 +4613,11 @@ msgstr "E522: Non trovato in 'termcap'"
msgid "E539: Illegal character <%s>"
msgstr "E539: Carattere non ammesso <%s>"
+#: ../option.c:2253
+#, c-format
+msgid "For option %s"
+msgstr "Per opzione %s"
+
#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: Non posso assegnare a 'term' il valore 'stringa nulla'"
@@ -4665,7 +4696,7 @@ msgstr "W17: Arabo richiede UTF-8, esegui ':set encoding=utf-8'"
#: ../option.c:5623
#, c-format
msgid "E593: Need at least %d lines"
-msgstr "E593: Servono almeno %d linee"
+msgstr "E593: Servono almeno %d righe"
#: ../option.c:5631
#, c-format
@@ -4822,7 +4853,7 @@ msgstr "(%d di %d)%s%s: "
#: ../quickfix.c:1676
msgid " (line deleted)"
-msgstr " (linea cancellata)"
+msgstr " (riga cancellata)"
#: ../quickfix.c:1863
msgid "E380: At bottom of quickfix stack"
@@ -4973,6 +5004,10 @@ msgstr "E554: Errore sintattico in %s{...}"
msgid "External submatches:\n"
msgstr "Sotto-corrispondenze esterne:\n"
+#: ../regexp.c:2470
+msgid "E888: (NFA regexp) cannot repeat %s"
+msgstr "E888: (NFA regexp) non riesco a ripetere %s"
+
#: ../regexp.c:7022
msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
@@ -4981,6 +5016,10 @@ msgstr ""
"E864: \\%#= può essere seguito solo da 0, 1 o 2. Sarà usato il motore "
"automatico "
+#: ../regexp.c:7039
+msgid "Switching to backtracking RE engine for pattern: "
+msgstr "Passo alla ricerca di RE col vecchio metodo: "
+
#: ../regexp_nfa.c:239
msgid "E865: (NFA) Regexp end encountered prematurely"
msgstr "E865: (NFA) Fine prematura dell'espressione regolare"
@@ -5113,7 +5152,7 @@ msgstr " VISUALE"
#: ../screen.c:7470
msgid " VISUAL LINE"
-msgstr " VISUALE LINEA"
+msgstr " VISUALE RIGA"
#: ../screen.c:7471
msgid " VISUAL BLOCK"
@@ -5125,7 +5164,7 @@ msgstr " SELEZIONA"
#: ../screen.c:7473
msgid " SELECT LINE"
-msgstr " SELEZIONA LINEA"
+msgstr " SELEZIONA RIGA"
#: ../screen.c:7474
msgid " SELECT BLOCK"
@@ -5191,7 +5230,7 @@ msgstr "Cerco nel file incluso: %s"
#: ../search.c:4405
msgid "E387: Match is on current line"
-msgstr "E387: Corrispondenza nella linea corrente"
+msgstr "E387: Corrispondenza nella riga corrente"
#: ../search.c:4517
msgid "All included files were found"
@@ -5235,12 +5274,12 @@ msgstr "E758: File ortografico troncato"
#: ../spell.c:953
#, c-format
msgid "Trailing text in %s line %d: %s"
-msgstr "Testo in eccesso in %s linea %d: %s"
+msgstr "Testo in eccesso in %s riga %d: %s"
#: ../spell.c:954
#, c-format
msgid "Affix name too long in %s line %d: %s"
-msgstr "Nome affisso troppo lungo in %s linea %d: %s"
+msgstr "Nome affisso troppo lungo in %s riga %d: %s"
#: ../spell.c:955
msgid "E761: Format error in affix file FOL, LOW or UPP"
@@ -5261,7 +5300,7 @@ msgstr "E756: Controllo ortografico non abilitato"
#: ../spell.c:2249
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr "Attenzione: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\""
+msgstr "Avviso: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\""
#: ../spell.c:2473
#, c-format
@@ -5287,7 +5326,7 @@ msgstr "E770: Sezione non supportata nel file ortografico"
#: ../spell.c:3762
#, c-format
msgid "Warning: region %s not supported"
-msgstr "Attenzione: regione %s non supportata"
+msgstr "Avviso: regione %s non supportata"
#: ../spell.c:4550
#, c-format
@@ -5297,7 +5336,7 @@ msgstr "Lettura file affissi %s ..."
#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
#, c-format
msgid "Conversion failure for word in %s line %d: %s"
-msgstr "Conversione fallita per una parola in %s linea %d: %s"
+msgstr "Conversione fallita per una parola in %s riga %d: %s"
#: ../spell.c:4630 ../spell.c:6170
#, c-format
@@ -5307,12 +5346,12 @@ msgstr "Conversione in %s non supportata: da %s a %s"
#: ../spell.c:4642
#, c-format
msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr "Valore di FLAG non valido in %s linea %d: %s"
+msgstr "Valore di FLAG non valido in %s riga %d: %s"
#: ../spell.c:4655
#, c-format
msgid "FLAG after using flags in %s line %d: %s"
-msgstr "FLAG dopo l'uso di flags in %s linea %d: %s"
+msgstr "FLAG dopo l'uso di flags in %s riga %d: %s"
#: ../spell.c:4723
#, c-format
@@ -5321,7 +5360,7 @@ msgid ""
"%d"
msgstr ""
"Definire COMPOUNDFORBIDFLAG dopo l'elemento PFX potrebbe dare risultati "
-"errati in %s linea %d"
+"errati in %s riga %d"
#: ../spell.c:4731
#, c-format
@@ -5330,43 +5369,43 @@ msgid ""
"%d"
msgstr ""
"Definire COMPOUNDPERMITFLAG dopo l'elemento PFX potrebbe dare risultati "
-"errati in %s linea %d"
+"errati in %s riga %d"
#: ../spell.c:4747
#, c-format
msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDRULES in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDRULES in %s riga %d: %s"
#: ../spell.c:4771
#, c-format
msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDWORDMAX in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDWORDMAX in %s riga %d: %s"
#: ../spell.c:4777
#, c-format
msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDMIN in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDMIN in %s riga %d: %s"
#: ../spell.c:4783
#, c-format
msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDSYLMAX in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDSYLMAX in %s riga %d: %s"
#: ../spell.c:4795
#, c-format
msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s linea %d: %s"
+msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s riga %d: %s"
#: ../spell.c:4847
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
msgstr ""
-"Flag combinazione diverso in blocco affissi continuo in %s linea %d: %s"
+"Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s"
#: ../spell.c:4850
#, c-format
msgid "Duplicate affix in %s line %d: %s"
-msgstr "Affisso duplicato in %s linea %d: %s"
+msgstr "Affisso duplicato in %s riga %d: %s"
#: ../spell.c:4871
#, c-format
@@ -5375,42 +5414,42 @@ msgid ""
"line %d: %s"
msgstr ""
"Affisso usato anche per BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST "
-"in %s linea %d: %s"
+"in %s riga %d: %s"
#: ../spell.c:4893
#, c-format
msgid "Expected Y or N in %s line %d: %s"
-msgstr "Y o N deve essere presente in %s linea %d: %s"
+msgstr "Y o N deve essere presente in %s riga %d: %s"
#: ../spell.c:4968
#, c-format
msgid "Broken condition in %s line %d: %s"
-msgstr "Condizione non rispettata in %s linea %d: %s"
+msgstr "Condizione non rispettata in %s riga %d: %s"
#: ../spell.c:5091
#, c-format
msgid "Expected REP(SAL) count in %s line %d"
-msgstr "Contatore REP(SAL) necessario in %s linea %d"
+msgstr "Contatore REP(SAL) necessario in %s riga %d"
#: ../spell.c:5120
#, c-format
msgid "Expected MAP count in %s line %d"
-msgstr "Contatore MAP necessario in %s linea %d"
+msgstr "Contatore MAP necessario in %s riga %d"
#: ../spell.c:5132
#, c-format
msgid "Duplicate character in MAP in %s line %d"
-msgstr "Carattere duplicato in MAP in %s linea %d"
+msgstr "Carattere duplicato in MAP in %s riga %d"
#: ../spell.c:5176
#, c-format
msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr "Elemento non riconosciuto o duplicato in %s linea %d: %s"
+msgstr "Elemento non riconosciuto o duplicato in %s riga %d: %s"
#: ../spell.c:5197
#, c-format
msgid "Missing FOL/LOW/UPP line in %s"
-msgstr "Linea FOL/LOW/UPP mancante in %s"
+msgstr "Riga FOL/LOW/UPP mancante in %s"
#: ../spell.c:5220
msgid "COMPOUNDSYLMAX used without SYLLABLE"
@@ -5431,22 +5470,22 @@ msgstr "Troppi suffissi e/o flag composti"
#: ../spell.c:5250
#, c-format
msgid "Missing SOFO%s line in %s"
-msgstr "Linea SOFO%s mancante in %s"
+msgstr "Riga SOFO%s mancante in %s"
#: ../spell.c:5253
#, c-format
msgid "Both SAL and SOFO lines in %s"
-msgstr "Linee sia SAL che SOFO in %s"
+msgstr "Riga sia SAL che SOFO in %s"
#: ../spell.c:5331
#, c-format
msgid "Flag is not a number in %s line %d: %s"
-msgstr "Il flag non è un numero in %s linea %d: %s"
+msgstr "Il flag non è un numero in %s riga %d: %s"
#: ../spell.c:5334
#, c-format
msgid "Illegal flag in %s line %d: %s"
-msgstr "Flag non ammesso in %s linea %d: %s"
+msgstr "Flag non ammesso in %s riga %d: %s"
#: ../spell.c:5493 ../spell.c:5501
#, c-format
@@ -5466,17 +5505,17 @@ msgstr "E760: Nessun contatore parole in %s"
#: ../spell.c:5669
#, c-format
msgid "line %6d, word %6d - %s"
-msgstr "linea %6d, parola %6d - %s"
+msgstr "riga %6d, parola %6d - %s"
#: ../spell.c:5691
#, c-format
msgid "Duplicate word in %s line %d: %s"
-msgstr "Parola duplicata in %s linea %d: %s"
+msgstr "Parola duplicata in %s riga %d: %s"
#: ../spell.c:5694
#, c-format
msgid "First duplicate word in %s line %d: %s"
-msgstr "Prima parola duplicata in %s linea %d: %s"
+msgstr "Prima parola duplicata in %s riga %d: %s"
#: ../spell.c:5746
#, c-format
@@ -5496,37 +5535,37 @@ msgstr "Lettura file parole %s ..."
#: ../spell.c:6155
#, c-format
msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr "Linea /encoding= duplicata ignorata in %s linea %d: %s"
+msgstr "Riga /encoding= duplicata ignorata in %s riga %d: %s"
#: ../spell.c:6159
#, c-format
msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr "Linea /encoding= dopo parola ignorata in %s linea %d: %s"
+msgstr "Riga /encoding= dopo parola ignorata in %s riga %d: %s"
#: ../spell.c:6180
#, c-format
msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr "Linea /regions= duplicata ignorata in %s linea %d: %s"
+msgstr "Riga /regions= duplicata ignorata in %s riga %d: %s"
#: ../spell.c:6185
#, c-format
msgid "Too many regions in %s line %d: %s"
-msgstr "Troppe regioni in %s linea %d: %s"
+msgstr "Troppe regioni in %s riga %d: %s"
#: ../spell.c:6198
#, c-format
msgid "/ line ignored in %s line %d: %s"
-msgstr "Linea / ignorata in %s linea %d: %s"
+msgstr "Riga / ignorata in %s riga %d: %s"
#: ../spell.c:6224
#, c-format
msgid "Invalid region nr in %s line %d: %s"
-msgstr "N. regione non valido in %s linea %d: %s"
+msgstr "N. regione non valido in %s riga %d: %s"
#: ../spell.c:6230
#, c-format
msgid "Unrecognized flags in %s line %d: %s"
-msgstr "Flag non riconosciuti in %s linea %d: %s"
+msgstr "Flag non riconosciuti in %s riga %d: %s"
#: ../spell.c:6257
#, c-format
@@ -5583,7 +5622,7 @@ msgstr "E755: Regione non valida in %s"
#: ../spell.c:7907
msgid "Warning: both compounding and NOBREAK specified"
-msgstr "Attenzione: specificati sia composizione sia NOBREAK"
+msgstr "Avviso: specificati sia composizione sia NOBREAK"
#: ../spell.c:7920
#, c-format
@@ -5700,7 +5739,7 @@ msgstr "la sincronizzazione inizia "
#: ../syntax.c:3443 ../syntax.c:3506
msgid " lines before top line"
-msgstr " linee prima della linea iniziale"
+msgstr " righe prima della riga iniziale"
#: ../syntax.c:3448
msgid ""
@@ -5745,7 +5784,7 @@ msgstr "; corrisp. "
#: ../syntax.c:3515
msgid " line breaks"
-msgstr " interruzioni di linea"
+msgstr " interruzioni di riga"
#: ../syntax.c:4076
msgid "E395: contains argument not accepted here"
@@ -5809,7 +5848,7 @@ msgstr "E402: Spazzatura dopo espressione: %s"
#: ../syntax.c:5120
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr ""
-"E403: syntax sync: espressione di continuazione linea specificata due volte"
+"E403: syntax sync: espressione di continuazione riga specificata due volte"
#: ../syntax.c:5169
#, c-format
@@ -5998,7 +6037,7 @@ msgid ""
" # TO tag FROM line in file/text"
msgstr ""
"\n"
-" # A tag DA__ linea in file/testo"
+" # A tag DA__ riga in file/testo"
#: ../tag.c:1303
#, c-format
@@ -6007,7 +6046,7 @@ msgstr "Ricerca nel tag file %s"
#: ../tag.c:1545
msgid "Ignoring long line in tags file"
-msgstr "Linea lunga ignorata nel tag file"
+msgstr "Riga lunga ignorata nel tag file"
#: ../tag.c:1915
#, c-format
@@ -6177,23 +6216,23 @@ msgstr "E830: Undo numero %<PRId64> non trovato"
#: ../undo.c:1979
msgid "E438: u_undo: line numbers wrong"
-msgstr "E438: u_undo: numeri linee errati"
+msgstr "E438: u_undo: numeri righe errati"
#: ../undo.c:2183
msgid "more line"
-msgstr "linea in più"
+msgstr "riga in più"
#: ../undo.c:2185
msgid "more lines"
-msgstr "linee in più"
+msgstr "righe in più"
#: ../undo.c:2187
msgid "line less"
-msgstr "linea in meno"
+msgstr "riga in meno"
#: ../undo.c:2189
msgid "fewer lines"
-msgstr "linee in meno"
+msgstr "righe in meno"
#: ../undo.c:2193
msgid "change"
@@ -6239,7 +6278,7 @@ msgstr "E439: lista 'undo' non valida"
#: ../undo.c:2495
msgid "E440: undo line missing"
-msgstr "E440: linea di 'undo' mancante"
+msgstr "E440: riga di 'undo' mancante"
#: ../version.c:600
msgid ""
@@ -6439,6 +6478,10 @@ msgstr "E445: Altre finestre contengono modifiche"
msgid "E446: No file name under cursor"
msgstr "E446: Nessun nome file sotto il cursore"
+#: ../window.c:5484
+msgid "List or number required"
+msgstr "È necessaria una lista o un numero"
+
#~ msgid "E831: bf_key_init() called with empty password"
#~ msgstr "E831: chiamata a bf_key_init() con password nulla"
@@ -6524,15 +6567,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Reading from stdin..."
#~ msgstr "Leggo da 'stdin'..."
-#~ msgid "[blowfish]"
-#~ msgstr "[blowfish]"
-
-#~ msgid "[crypted]"
-#~ msgstr "[cifrato]"
-
-#~ msgid "E821: File is encrypted with unknown method"
-#~ msgstr "E821: File cifrato con metodo sconosciuto"
-
#~ msgid "NetBeans disallows writes of unmodified buffers"
#~ msgstr "NetBeans non permette la scrittura di un buffer non modificato"
@@ -6648,8 +6682,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Vim: Received \"die\" request from session manager\n"
#~ msgstr "Vim: Ricevuta richiesta \"die\" dal session manager\n"
-#~ msgid "Close"
-#~ msgstr "Chiusura"
+#~ msgid "Close tab"
+#~ msgstr "Chiudi linguetta"
#~ msgid "New tab"
#~ msgstr "Nuova linguetta"
@@ -6705,9 +6739,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E672: Unable to open window inside MDI application"
#~ msgstr "E672: Non posso aprire la finestra in un'applicazione MDI"
-#~ msgid "Close tab"
-#~ msgstr "Chiudi linguetta"
-
#~ msgid "Open tab..."
#~ msgstr "Apri linguetta..."
@@ -6830,13 +6861,13 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "non sono riuscito ad aprire il buffer"
#~ msgid "cannot delete line"
-#~ msgstr "non posso cancellare la linea"
+#~ msgstr "non posso cancellare la riga"
#~ msgid "cannot replace line"
-#~ msgstr "non posso sostituire la linea"
+#~ msgstr "non posso sostituire la riga"
#~ msgid "cannot insert line"
-#~ msgstr "non posso inserire la linea"
+#~ msgstr "non posso inserire la riga"
#~ msgid "string cannot contain newlines"
#~ msgstr "la stringa non può contenere caratteri 'A CAPO'"
@@ -6857,7 +6888,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "finestra non valida"
#~ msgid "linenr out of range"
-#~ msgstr "numero linea non nell'intervallo"
+#~ msgstr "numero riga non nell'intervallo"
#~ msgid "not allowed in the Vim sandbox"
#~ msgstr "non ammesso in ambiente protetto"
@@ -6865,9 +6896,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E836: This Vim cannot execute :python after using :py3"
#~ msgstr "E836: Python: Impossibile usare :py e :py3 nella stessa sessione"
-#~ msgid "E837: This Vim cannot execute :py3 after using :python"
-#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python"
-
#~ msgid ""
#~ "E263: Sorry, this command is disabled, the Python library could not be "
#~ "loaded."
@@ -6875,14 +6903,18 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ "E263: Spiacente, comando non disponibile, non riesco a caricare libreria "
#~ "programmi Python."
+#~ msgid ""
+#~ "E887: Sorry, this command is disabled, the Python's site module could not be "
+#~ "loaded."
+#~ msgstr ""
+#~ "E887: Spiacente, comando non disponibile, non riesco a caricare il modulo "
+#~ "Python locale."
+
#~ msgid "E659: Cannot invoke Python recursively"
#~ msgstr "E659: Python non può essere chiamato ricorsivamente"
-#~ msgid "line number out of range"
-#~ msgstr "numero linea non nell'intervallo"
-
-#~ msgid "invalid mark name"
-#~ msgstr "nome di mark non valido"
+#~ msgid "E837: This Vim cannot execute :py3 after using :python"
+#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python"
#~ msgid "E265: $_ must be an instance of String"
#~ msgstr "E265: $_ deve essere un'istanza di String"
@@ -7010,7 +7042,10 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "non ancora implementato"
#~ msgid "cannot set line(s)"
-#~ msgstr "non posso impostare linea(e)"
+#~ msgstr "non posso impostare riga(he)"
+
+#~ msgid "invalid mark name"
+#~ msgstr "nome di mark non valido"
#~ msgid "mark not set"
#~ msgstr "mark non impostato"
@@ -7019,7 +7054,10 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "riga %d colonna %d"
#~ msgid "cannot insert/append line"
-#~ msgstr "non riesco a inserire/aggiungere linea"
+#~ msgstr "non riesco a inserire/aggiungere riga"
+
+#~ msgid "line number out of range"
+#~ msgstr "numero linea non nell'intervallo"
#~ msgid "unknown flag: "
#~ msgstr "opzione inesistente: "
@@ -7067,7 +7105,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E572: codice di uscita %d"
#~ msgid "cannot get line"
-#~ msgstr "non riesco a ottenere la linea"
+#~ msgstr "non riesco a ottenere la riga"
#~ msgid "Unable to register a command server name"
#~ msgstr "Non riesco a registrare un nome di server comando"
@@ -7293,7 +7331,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E286: Apertura 'input method' fallita"
#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr "E287: Attenzione: Non posso assegnare IM a 'destroy callback'"
+#~ msgstr "E287: Avviso: Non posso assegnare IM a 'destroy callback'"
#~ msgid "E288: input method doesn't support any style"
#~ msgstr "E288: 'input method' non sopporta alcuno stile"
@@ -7365,12 +7403,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E338: Sorry, no file browser in console mode"
#~ msgstr "E338: Spiacente, niente esplorazione file in modalità console"
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: preservo file...\n"
-
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: Finito.\n"
-
#~ msgid "ERROR: "
#~ msgstr "ERRORE: "
@@ -7391,7 +7423,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ "\n"
#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: La linea sta diventando troppo lunga"
+#~ msgstr "E340: La riga sta diventando troppo lunga"
#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
#~ msgstr "E341: Errore interno: lalloc(%<PRId64>, )"
@@ -7399,15 +7431,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E547: Illegal mouseshape"
#~ msgstr "E547: Forma del mouse non valida"
-#~ msgid "Enter encryption key: "
-#~ msgstr "Immetti chiave di cifratura: "
-
-#~ msgid "Enter same key again: "
-#~ msgstr "Ribatti per conferma la stessa chiave: "
-
-#~ msgid "Keys don't match!"
-#~ msgstr "Le chiavi non corrispondono!"
-
#~ msgid "Cannot connect to Netbeans #2"
#~ msgstr "Non posso connettermi a Netbeans #2"
@@ -7437,7 +7460,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E775: Funzionalità [eval] non disponibile"
#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "libero %<PRId64> linee"
+#~ msgstr "libero %<PRId64> righe"
#~ msgid "E530: Cannot change term in GUI"
#~ msgstr "E530: Non posso modificare 'term' mentre sono nella GUI"
@@ -7538,15 +7561,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E245: Illegal char '%c' in font name \"%s\""
#~ msgstr "E245: Carattere non ammesso '%c' nel font di nome \"%s\""
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: Segnale doppio, esco\n"
-
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: Intercettato segnale fatale %s\n"
-
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: Intercettato segnale fatale\n"
-
#~ msgid "Opening the X display took %<PRId64> msec"
#~ msgstr "Attivazione visualizzazione X ha richiesto %<PRId64> msec"
@@ -7610,7 +7624,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "XSMP SmcOpenConnection fallita: %s"
#~ msgid "At line"
-#~ msgstr "Alla linea"
+#~ msgstr "Alla riga"
#~ msgid "Could not load vim32.dll!"
#~ msgstr "Non riesco a caricare vim32.dll!"
@@ -7875,7 +7889,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr " modo Vim predefinito "
#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "ATTENZIONE: Trovato Windows 95/98/ME"
+#~ msgstr "AVVISO: Trovato Windows 95/98/ME"
#~ msgid "type :help windows95<Enter> for info on this"
#~ msgstr "batti :help windows95<Enter> per info al riguardo"
@@ -7962,6 +7976,22 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Need encryption key for \"%s\""
#~ msgstr "Serve una chiave di cifratura per \"%s\""
+#~ msgid "empty keys are not allowed"
+#~ msgstr "chiavi nulle non consentite"
+
+#~ msgid "dictionary is locked"
+#~ msgstr "il dizionario è bloccato"
+
+#~ msgid "list is locked"
+#~ msgstr "la lista è bloccata"
+
+#~ msgid "failed to add key '%s' to dictionary"
+#~ msgstr "non non riusciato ad aggiungere la chiave '%s' al dizionario"
+
+#~ #, c-format
+#~ msgid "index must be int or slice, not %s"
+#~ msgstr "l'indice deve'essere un intero o un intervallo, non %s"
+
#~ msgid "expected str() or unicode() instance, but got %s"
#~ msgstr "attesa istanza di str() o unicode(), trovato invece %s"
@@ -7985,8 +8015,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "value is too small to fit into C int type"
#~ msgstr "valore troppo piccolo per il tipo int del C"
-#~ msgid "number must be greater then zero"
-#~ msgstr "il numero dev'essere maggiore di zero"
+#~ msgid "number must be greater than zero"
+#~ msgstr "il numero deve essere maggiore di zero"
#~ msgid "number must be greater or equal to zero"
#~ msgstr "il numero dev'essere maggiore o uguale a zero"
@@ -8005,7 +8035,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "expected 3-tuple as imp.find_module() result, but got %s"
#~ msgstr ""
-#~ "atteso terzetto come risultato di imp.find_module(), trovato invece %s"
+#~ "atteso terzetto come risultato di imp.find_module(), trovato invece tuple di "
+#~ "dimens. %d"
#~ msgid ""
#~ "expected 3-tuple as imp.find_module() result, but got tuple of size %d"
@@ -8042,15 +8073,30 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "internal error: failed to get vim list item %d"
#~ msgstr "errore interno: non ho potuto ottenere l'elemento di vim list %d"
-#~ msgid "failed to add item to list"
-#~ msgstr "non ho potuto aggiungere un elemento alla lista"
+#~ msgid "slice step cannot be zero"
+#~ msgstr "il passo scorrendo un intervallo non può essere zero"
+
+#~ #, c-format
+#~ msgid "attempt to assign sequence of size greater than %d to extended slice"
+#~ msgstr "tentativo di assegnare una sequenza maggiore di %d a un intervallo "
+#~ "esteso"
#~ msgid "internal error: no vim list item %d"
#~ msgstr "errore interno: non c'è un elemento di vim list %d"
+#~ msgid "internal error: not enough list items"
+#~ msgstr "errore interno: non ci sono abbastanza elementi per la lista"
+
#~ msgid "internal error: failed to add item to list"
#~ msgstr "errore interno: non ho potuto aggiungere un elemento alla lista"
+#~ msgid "attempt to assign sequence of size %d to extended slice of size %d"
+#~ msgstr "tentativo di assegnare sequenza di dimensione %d a un intervallo "
+#~ " esteso di dimensione %d"
+
+#~ msgid "failed to add item to list"
+#~ msgstr "non ho potuto aggiungere un elemento alla lista"
+
#~ msgid "cannot delete vim.List attributes"
#~ msgstr "non riesco a cancellare gli attributi vim.List"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 3bc6d46dd9..97db69d3f3 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -39,7 +39,6 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
-#include "nvim/tempfile.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -2440,8 +2439,6 @@ int grep_internal(cmdidx_T cmdidx)
void ex_make(exarg_T *eap)
{
char_u *fname;
- char_u *cmd;
- size_t len;
win_T *wp = NULL;
qf_info_T *qi = &ql_info;
int res;
@@ -2479,30 +2476,28 @@ void ex_make(exarg_T *eap)
return;
os_remove((char *)fname); // in case it's not unique
- /*
- * If 'shellpipe' empty: don't redirect to 'errorfile'.
- */
- len = STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1;
+ // If 'shellpipe' empty: don't redirect to 'errorfile'.
+ const size_t len = (STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1
+ + (*p_sp == NUL
+ ? 0
+ : STRLEN(p_sp) + STRLEN(fname) + 3));
+ char *const cmd = xmalloc(len);
+ snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)eap->arg,
+ (char *)p_shq);
if (*p_sp != NUL) {
- len += STRLEN(p_sp) + STRLEN(fname) + 3;
+ append_redir(cmd, len, (char *) p_sp, (char *) fname);
+ }
+ // Output a newline if there's something else than the :make command that
+ // was typed (in which case the cursor is in column 0).
+ if (msg_col == 0) {
+ msg_didout = false;
}
- cmd = xmalloc(len);
- sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg,
- (char *)p_shq);
- if (*p_sp != NUL)
- append_redir(cmd, len, p_sp, fname);
- /*
- * Output a newline if there's something else than the :make command that
- * was typed (in which case the cursor is in column 0).
- */
- if (msg_col == 0)
- msg_didout = FALSE;
msg_start();
MSG_PUTS(":!");
- msg_outtrans(cmd); /* show what we are doing */
+ msg_outtrans((char_u *) cmd); // show what we are doing
- /* let the shell know if we are redirecting output or not */
- do_shell(cmd, *p_sp != NUL ? kShellOptDoOut : 0);
+ // let the shell know if we are redirecting output or not
+ do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0);
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
diff --git a/src/nvim/search.c b/src/nvim/search.c
index fffae1ecb2..faf70472bf 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -453,25 +453,24 @@ void last_pat_prog(regmmatch_T *regmatch)
--emsg_off;
}
-/*
- * lowest level search function.
- * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
- * Start at position 'pos' and return the found position in 'pos'.
- *
- * if (options & SEARCH_MSG) == 0 don't give any messages
- * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
- * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
- * if (options & SEARCH_HIS) put search pattern in history
- * if (options & SEARCH_END) return position at end of match
- * if (options & SEARCH_START) accept match at pos itself
- * if (options & SEARCH_KEEP) keep previous search pattern
- * if (options & SEARCH_FOLD) match only once in a closed fold
- * if (options & SEARCH_PEEK) check for typed char, cancel search
- *
- * Return FAIL (zero) for failure, non-zero for success.
- * Returns the index of the first matching
- * subpattern plus one; one if there was none.
- */
+/// lowest level search function.
+/// Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
+/// Start at position 'pos' and return the found position in 'pos'.
+///
+/// if (options & SEARCH_MSG) == 0 don't give any messages
+/// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
+/// if (options & SEARCH_MSG) == SEARCH_MSG give all messages
+/// if (options & SEARCH_HIS) put search pattern in history
+/// if (options & SEARCH_END) return position at end of match
+/// if (options & SEARCH_START) accept match at pos itself
+/// if (options & SEARCH_KEEP) keep previous search pattern
+/// if (options & SEARCH_FOLD) match only once in a closed fold
+/// if (options & SEARCH_PEEK) check for typed char, cancel search
+/// if (options & SEARCH_COL) start at pos->col instead of zero
+///
+/// @returns FAIL (zero) for failure, non-zero for success.
+/// the index of the first matching
+/// subpattern plus one; one if there was none.
int searchit(
win_T *win, /* window to search in, can be NULL for a
buffer without a window! */
@@ -571,16 +570,14 @@ int searchit(
if (tm != NULL && profile_passed_limit(*tm))
break;
- /*
- * Look for a match somewhere in line "lnum".
- */
+ // Look for a match somewhere in line "lnum".
+ colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0;
nmatched = vim_regexec_multi(&regmatch, win, buf,
- lnum, (colnr_T)0,
- tm
- );
- /* Abort searching on an error (e.g., out of stack). */
- if (called_emsg)
+ lnum, col, tm);
+ // Abort searching on an error (e.g., out of stack).
+ if (called_emsg) {
break;
+ }
if (nmatched > 0) {
/* match may actually be in another line when using \zs */
matchpos = regmatch.startpos[0];
@@ -881,9 +878,8 @@ static void set_vv_searchforward(void)
set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/'));
}
-/*
- * Return the number of the first subpat that matched.
- */
+// Return the number of the first subpat that matched.
+// Return zero if none of them matched.
static int first_submatch(regmmatch_T *rp)
{
int submatch;
diff --git a/src/nvim/search.h b/src/nvim/search.h
index 6947f79d49..d4e40cb287 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -15,19 +15,20 @@
#define ACTION_SHOW_ALL 4
#define ACTION_EXPAND 5
-/* Values for 'options' argument in do_search() and searchit() */
-#define SEARCH_REV 0x01 /* go in reverse of previous dir. */
-#define SEARCH_ECHO 0x02 /* echo the search command and handle options */
-#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */
-#define SEARCH_NFMSG 0x08 /* give all messages except not found */
-#define SEARCH_OPT 0x10 /* interpret optional flags */
-#define SEARCH_HIS 0x20 /* put search pattern in history */
-#define SEARCH_END 0x40 /* put cursor at end of match */
-#define SEARCH_NOOF 0x80 /* don't add offset to position */
-#define SEARCH_START 0x100 /* start search without col offset */
-#define SEARCH_MARK 0x200 /* set previous context mark */
-#define SEARCH_KEEP 0x400 /* keep previous search pattern */
-#define SEARCH_PEEK 0x800 /* peek for typed char, cancel search */
+// Values for 'options' argument in do_search() and searchit()
+#define SEARCH_REV 0x01 ///< go in reverse of previous dir.
+#define SEARCH_ECHO 0x02 ///< echo the search command and handle options
+#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04)
+#define SEARCH_NFMSG 0x08 ///< give all messages except not found
+#define SEARCH_OPT 0x10 ///< interpret optional flags
+#define SEARCH_HIS 0x20 ///< put search pattern in history
+#define SEARCH_END 0x40 ///< put cursor at end of match
+#define SEARCH_NOOF 0x80 ///< don't add offset to position
+#define SEARCH_START 0x100 ///< start search without col offset
+#define SEARCH_MARK 0x200 ///< set previous context mark
+#define SEARCH_KEEP 0x400 ///< keep previous search pattern
+#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search
+#define SEARCH_COL 0x1000 ///< start at specified column instead of zero
/* Values for flags argument for findmatchlimit() */
#define FM_BACKWARD 0x01 /* search backwards */
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index def2de9b1a..32a02b0fb7 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -31,7 +31,6 @@
#include "nvim/misc2.h"
#include "nvim/ex_getln.h"
#include "nvim/search.h"
-#include "nvim/eval.h"
#include "nvim/regexp.h"
#include "nvim/eval_defs.h"
#include "nvim/version.h"
@@ -39,6 +38,8 @@
#include "nvim/fileio.h"
#include "nvim/strings.h"
#include "nvim/quickfix.h"
+#include "nvim/eval/encode.h"
+#include "nvim/eval/decode.h"
#include "nvim/lib/khash.h"
#include "nvim/lib/kvec.h"
@@ -65,9 +66,6 @@ KHASH_SET_INIT_STR(strset)
((char *) copy_option_part((char_u **) src, (char_u *) dest, __VA_ARGS__))
#define find_shada_parameter(...) \
((const char *) find_shada_parameter(__VA_ARGS__))
-#define emsg2(a, b) emsg2((char_u *) a, (char_u *) b)
-#define emsg3(a, b, c) emsg3((char_u *) a, (char_u *) b, (char_u *) c)
-#define emsgu(a, ...) emsgu((char_u *) a, __VA_ARGS__)
#define home_replace_save(a, b) \
((char *)home_replace_save(a, (char_u *)b))
#define home_replace(a, b, c, d, e) \
@@ -761,7 +759,7 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer)
{
const int fd = (int)(intptr_t) sd_writer->cookie;
if (os_fsync(fd) < 0) {
- emsg2(_(SERR "System error while synchronizing ShaDa file: %s"),
+ emsgf(_(SERR "System error while synchronizing ShaDa file: %s"),
os_strerror(errno));
errno = 0;
}
@@ -811,11 +809,11 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader,
{
if (sd_reader->skip(sd_reader, offset) != OK) {
if (sd_reader->error != NULL) {
- emsg2(_(SERR "System error while skipping in ShaDa file: %s"),
+ emsgf(_(SERR "System error while skipping in ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"last entry specified that it occupies %" PRIu64 " bytes, "
"but file ended earlier"),
(uint64_t) offset);
@@ -849,7 +847,7 @@ open_file_start:
goto open_file_start;
}
if (fd != UV_EEXIST) {
- emsg3(_(SERR "System error while opening ShaDa file %s: %s"),
+ emsgf(_(SERR "System error while opening ShaDa file %s: %s"),
fname, os_strerror(fd));
}
return fd;
@@ -897,7 +895,7 @@ close_file_start:
errno = 0;
goto close_file_start;
} else {
- emsg2(_(SERR "System error while closing ShaDa file: %s"),
+ emsgf(_(SERR "System error while closing ShaDa file: %s"),
strerror(errno));
errno = 0;
}
@@ -934,7 +932,7 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len)
ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *) data;
ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len);
if (written_bytes == -1) {
- emsg2(_(SERR "System error while writing ShaDa file: %s"),
+ emsgf(_(SERR "System error while writing ShaDa file: %s"),
sd_writer->error);
return -1;
}
@@ -981,7 +979,7 @@ static int shada_read_file(const char *const file, const int flags)
if (of_ret != 0) {
if (of_ret == UV_ENOENT && (flags & kShaDaMissingError)) {
- emsg3(_(SERR "System error while opening ShaDa file %s for reading: %s"),
+ emsgf(_(SERR "System error while opening ShaDa file %s for reading: %s"),
fname, os_strerror(of_ret));
}
xfree(fname);
@@ -1687,8 +1685,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
do { \
if ((src) != NULL) { \
for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \
- if (vim_to_msgpack(spacker, &li->li_tv, \
- _("additional elements of ShaDa " what)) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &li->li_tv, \
+ _("additional elements of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1706,8 +1705,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
const size_t key_len = strlen((const char *) hi->hi_key); \
msgpack_pack_str(spacker, key_len); \
msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \
- if (vim_to_msgpack(spacker, &di->di_tv, \
- _("additional data of ShaDa " what)) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &di->di_tv, \
+ _("additional data of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1757,7 +1757,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
char vardesc[256] = "variable g:";
memcpy(&vardesc[sizeof("variable g:") - 1], varname.data,
varname.size + 1);
- if (vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
+ if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
== FAIL) {
ret = kSDWriteIgnError;
EMSG2(_(WERR "Failed to write variable %s"),
@@ -2159,7 +2159,7 @@ shada_parse_msgpack_read_next: {}
break;
}
case MSGPACK_UNPACK_PARSE_ERROR: {
- emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
+ emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -2176,7 +2176,7 @@ shada_parse_msgpack_read_next: {}
break;
}
case MSGPACK_UNPACK_CONTINUE: {
- emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
+ emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -2184,7 +2184,7 @@ shada_parse_msgpack_read_next: {}
}
case MSGPACK_UNPACK_EXTRA_BYTES: {
shada_parse_msgpack_extra_bytes:
- emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
+ emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -3265,11 +3265,11 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader,
(void) read_bytes;
if (sd_reader->error != NULL) {
- emsg2(_(SERR "System error while reading ShaDa file: %s"),
+ emsgf(_(SERR "System error while reading ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"last entry specified that it occupies %" PRIu64 " bytes, "
"but file ended earlier"),
(uint64_t) length);
@@ -3304,11 +3304,11 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader,
if (first_char == EOF) {
if (sd_reader->error) {
- emsg2(_(SERR "System error while reading integer from ShaDa file: %s"),
+ emsgf(_(SERR "System error while reading integer from ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"expected positive integer at position %" PRIu64
", but got nothing"),
(uint64_t) fpos);
@@ -3339,7 +3339,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader,
break;
}
default: {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"expected positive integer at position %" PRIu64),
(uint64_t) fpos);
return kSDReadStatusNotShaDa;
@@ -3403,18 +3403,18 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
proc) \
do { \
if (!(condition)) { \
- emsgu(_(READERR(entry_name, error_desc)), initial_fpos); \
+ emsgf(_(READERR(entry_name, error_desc)), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
} \
tgt = proc(obj.via.attr); \
} while (0)
#define CHECK_KEY_IS_STR(entry_name) \
if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \
- emsgu(_(READERR(entry_name, "has key which is not a string")), \
+ emsgf(_(READERR(entry_name, "has key which is not a string")), \
initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
} else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \
- emsgu(_(READERR(entry_name, "has empty key")), initial_fpos); \
+ emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
}
#define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \
@@ -3477,7 +3477,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
typval_T adtv; \
if (msgpack_to_vim(obj, &adtv) == FAIL \
|| adtv.v_type != VAR_DICT) { \
- emsgu(_(READERR(name, \
+ emsgf(_(READERR(name, \
"cannot be converted to a VimL dictionary")), \
initial_fpos); \
ga_clear(&ad_ga); \
@@ -3502,7 +3502,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
}; \
typval_T aetv; \
if (msgpack_to_vim(obj, &aetv) == FAIL) { \
- emsgu(_(READERR(name, "cannot be converted to a VimL list")), \
+ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \
initial_fpos); \
clear_tv(&aetv); \
goto shada_read_next_item_error; \
@@ -3570,7 +3570,7 @@ shada_read_next_item_start:
// kSDItemUnknown cannot possibly pass that far because it is -1 and that
// will fail in msgpack_read_uint64. But kSDItemMissing may and it will
// otherwise be skipped because (1 << 0) will never appear in flags.
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"there is an item at position %" PRIu64 " "
"that must not be there: Missing items are "
"for internal uses only"),
@@ -3640,14 +3640,14 @@ shada_read_next_item_start:
switch ((ShadaEntryType) type_u64) {
case kSDItemHeader: {
if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) {
- emsgu(_(READERR("header", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("header", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
break;
}
case kSDItemSearchPattern: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("search pattern", "is not a dictionary")),
+ emsgf(_(READERR("search pattern", "is not a dictionary")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3678,7 +3678,7 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.search_pattern.pat == NULL) {
- emsgu(_(READERR("search pattern", "has no pattern")), initial_fpos);
+ emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data,
@@ -3690,7 +3690,7 @@ shada_read_next_item_start:
case kSDItemGlobalMark:
case kSDItemLocalMark: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("mark", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
garray_T ad_ga;
@@ -3699,7 +3699,7 @@ shada_read_next_item_start:
CHECK_KEY_IS_STR("mark")
if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) {
if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) {
- emsgu(_(READERR("mark", "has n key which is only valid for "
+ emsgf(_(READERR("mark", "has n key which is only valid for "
"local and global mark entries")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3716,15 +3716,15 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.filemark.fname == NULL) {
- emsgu(_(READERR("mark", "is missing file name")), initial_fpos);
+ emsgf(_(READERR("mark", "is missing file name")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.filemark.mark.lnum <= 0) {
- emsgu(_(READERR("mark", "has invalid line number")), initial_fpos);
+ emsgf(_(READERR("mark", "has invalid line number")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.filemark.mark.col < 0) {
- emsgu(_(READERR("mark", "has invalid column number")), initial_fpos);
+ emsgf(_(READERR("mark", "has invalid column number")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark");
@@ -3732,7 +3732,7 @@ shada_read_next_item_start:
}
case kSDItemRegister: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("register", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("register", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
garray_T ad_ga;
@@ -3742,14 +3742,14 @@ shada_read_next_item_start:
if (CHECK_KEY(unpacked.data.via.map.ptr[i].key,
REG_KEY_CONTENTS)) {
if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("register",
+ emsgf(_(READERR("register",
"has " REG_KEY_CONTENTS
" key with non-array value")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) {
- emsgu(_(READERR("register",
+ emsgf(_(READERR("register",
"has " REG_KEY_CONTENTS " key with empty array")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
@@ -3758,7 +3758,7 @@ shada_read_next_item_start:
unpacked.data.via.map.ptr[i].val.via.array;
for (size_t i = 0; i < arr.size; i++) {
if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("register", "has " REG_KEY_CONTENTS " array "
+ emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array "
"with non-binary value")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3778,7 +3778,7 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.reg.contents == NULL) {
- emsgu(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")),
+ emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3787,29 +3787,29 @@ shada_read_next_item_start:
}
case kSDItemHistoryEntry: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("history", "is not an array")), initial_fpos);
+ emsgf(_(READERR("history", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 2) {
- emsgu(_(READERR("history", "does not have enough elements")),
+ emsgf(_(READERR("history", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type
!= MSGPACK_OBJECT_POSITIVE_INTEGER) {
- emsgu(_(READERR("history", "has wrong history type type")),
+ emsgf(_(READERR("history", "has wrong history type type")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[1].type
!= MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("history", "has wrong history string type")),
+ emsgf(_(READERR("history", "has wrong history string type")),
initial_fpos);
goto shada_read_next_item_error;
}
if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0,
unpacked.data.via.array.ptr[1].via.bin.size) != NULL) {
- emsgu(_(READERR("history", "contains string with zero byte inside")),
+ emsgf(_(READERR("history", "contains string with zero byte inside")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3819,13 +3819,13 @@ shada_read_next_item_start:
entry->data.history_item.histtype == HIST_SEARCH;
if (is_hist_search) {
if (unpacked.data.via.array.size < 3) {
- emsgu(_(READERR("search history",
+ emsgf(_(READERR("search history",
"does not have separator character")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[2].type
!= MSGPACK_OBJECT_POSITIVE_INTEGER) {
- emsgu(_(READERR("search history",
+ emsgf(_(READERR("search history",
"has wrong history separator type")), initial_fpos);
goto shada_read_next_item_error;
}
@@ -3867,22 +3867,16 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemVariable: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("variable", "is not an array")), initial_fpos);
+ emsgf(_(READERR("variable", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 2) {
- emsgu(_(READERR("variable", "does not have enough elements")),
+ emsgf(_(READERR("variable", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("variable", "has wrong variable name type")),
- initial_fpos);
- goto shada_read_next_item_error;
- }
- if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_NIL
- || unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_EXT) {
- emsgu(_(READERR("variable", "has wrong variable value type")),
+ emsgf(_(READERR("variable", "has wrong variable name type")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3891,7 +3885,7 @@ shada_read_next_item_hist_no_conv:
unpacked.data.via.array.ptr[0].via.bin.size);
if (msgpack_to_vim(unpacked.data.via.array.ptr[1],
&(entry->data.global_var.value)) == FAIL) {
- emsgu(_(READERR("variable", "has value that cannot "
+ emsgf(_(READERR("variable", "has value that cannot "
"be converted to the VimL value")), initial_fpos);
goto shada_read_next_item_error;
}
@@ -3912,16 +3906,16 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemSubString: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("sub string", "is not an array")), initial_fpos);
+ emsgf(_(READERR("sub string", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 1) {
- emsgu(_(READERR("sub string", "does not have enough elements")),
+ emsgf(_(READERR("sub string", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("sub string", "has wrong sub string type")),
+ emsgf(_(READERR("sub string", "has wrong sub string type")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3934,7 +3928,7 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemBufferList: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("buffer list", "is not an array")), initial_fpos);
+ emsgf(_(READERR("buffer list", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size == 0) {
@@ -3951,7 +3945,7 @@ shada_read_next_item_hist_no_conv:
{
msgpack_unpacked unpacked = unpacked_2;
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry that is not a dictionary"),
initial_fpos);
@@ -3976,21 +3970,21 @@ shada_read_next_item_hist_no_conv:
}
}
if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry with invalid line number"),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.buffer_list.buffers[i].pos.col < 0) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry with invalid column number"),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.buffer_list.buffers[i].fname == NULL) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry that does not have a file name"),
initial_fpos);
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index fdae89b84c..0acaa9ae2b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -319,7 +319,6 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 8fcb02c3b6..da84106454 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -872,7 +872,7 @@ do_tag (
/* Let the SwapExists event know what tag we are jumping to. */
vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name);
- set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1);
+ set_vim_var_string(VV_SWAPCOMMAND, (char *) IObuff, -1);
/*
* Jump to the desired match.
@@ -1147,6 +1147,22 @@ find_tags (
int get_it_again = FALSE;
int use_cscope = (flags & TAG_CSCOPE);
int verbose = (flags & TAG_VERBOSE);
+ int save_p_ic = p_ic;
+
+ // Change the value of 'ignorecase' according to 'tagcase' for the
+ // duration of this function.
+ switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) {
+ case TC_FOLLOWIC:
+ break;
+ case TC_IGNORE:
+ p_ic = true;
+ break;
+ case TC_MATCH:
+ p_ic = false;
+ break;
+ default:
+ assert(false);
+ }
help_save = curbuf->b_help;
orgpat.pat = pat;
@@ -1955,6 +1971,8 @@ findtag_end:
curbuf->b_help = help_save;
xfree(saved_pat);
+ p_ic = save_p_ic;
+
return retval;
}
diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c
deleted file mode 100644
index afe926b2ef..0000000000
--- a/src/nvim/tempfile.c
+++ /dev/null
@@ -1,139 +0,0 @@
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "nvim/ascii.h"
-#include "nvim/memory.h"
-#include "nvim/misc1.h"
-#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
-#include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/tempfile.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tempfile.c.generated.h"
-#endif
-
-/// Name of Vim's own temp dir. Ends in a slash.
-static char_u *vim_tempdir = NULL;
-
-/// Create a directory for private use by this instance of Neovim.
-/// This is done once, and the same directory is used for all temp files.
-/// This method avoids security problems because of symlink attacks et al.
-/// It's also a bit faster, because we only need to check for an existing
-/// file when creating the directory and not for each temp file.
-static void vim_maketempdir(void)
-{
- static const char *temp_dirs[] = TEMP_DIR_NAMES;
- // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- char_u path[TEMP_FILE_PATH_MAXLEN];
- for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) {
- // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
- // Skip the directory check if the expansion fails.
- expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
- if (template[0] == '$' || !os_isdir(template)) {
- continue;
- }
-
- add_pathsep((char *)template);
- // Concatenate with temporary directory name pattern
- STRCAT(template, "nvimXXXXXX");
-
- if (os_mkdtemp((const char *)template, (char *)path) != 0) {
- continue;
- }
-
- if (vim_settempdir((char *)path)) {
- // Successfully created and set temporary directory so stop trying.
- break;
- } else {
- // Couldn't set `vim_tempdir` to `path` so remove created directory.
- os_rmdir((char *)path);
- }
- }
-}
-
-/// Delete the temp directory and all files it contains.
-void vim_deltempdir(void)
-{
- if (vim_tempdir != NULL) {
- snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir);
-
- char_u **files;
- int file_count;
-
- // Note: We cannot just do `&NameBuff` because it is a statically
- // sized array so `NameBuff == &NameBuff` according to C semantics.
- char_u *buff_list[1] = {NameBuff};
- if (gen_expand_wildcards(1, buff_list, &file_count, &files,
- EW_DIR|EW_FILE|EW_SILENT) == OK) {
- for (int i = 0; i < file_count; ++i) {
- os_remove((char *)files[i]);
- }
- FreeWild(file_count, files);
- }
- path_tail(NameBuff)[-1] = NUL;
- os_rmdir((char *)NameBuff);
-
- xfree(vim_tempdir);
- vim_tempdir = NULL;
- }
-}
-
-/// Get the name of temp directory. This directory would be created on the first
-/// call to this function.
-char_u *vim_gettempdir(void)
-{
- if (vim_tempdir == NULL) {
- vim_maketempdir();
- }
-
- return vim_tempdir;
-}
-
-/// Set Neovim own temporary directory name to `tempdir`. This directory should
-/// be already created. Expand this name to a full path and put it in
-/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
-///
-/// @param tempdir must be no longer than MAXPATHL.
-///
-/// @return false if we run out of memory.
-static bool vim_settempdir(char *tempdir)
-{
- char *buf = verbose_try_malloc(MAXPATHL + 2);
- if (!buf) {
- return false;
- }
- vim_FullName(tempdir, buf, MAXPATHL, false);
- add_pathsep(buf);
- vim_tempdir = (char_u *)xstrdup(buf);
- xfree(buf);
- return true;
-}
-
-/// Return a unique name that can be used for a temp file.
-///
-/// @note The temp file is NOT created.
-///
-/// @return pointer to the temp file name or NULL if Neovim can't create
-/// temporary directory for its own temporary files.
-char_u *vim_tempname(void)
-{
- // Temp filename counter.
- static uint32_t temp_count;
-
- char_u *tempdir = vim_gettempdir();
- if (!tempdir) {
- return NULL;
- }
-
- // There is no need to check if the file exists, because we own the directory
- // and nobody else creates a file in it.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- snprintf((char *)template, TEMP_FILE_PATH_MAXLEN,
- "%s%" PRIu32, tempdir, temp_count++);
- return vim_strsave(template);
-}
diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h
deleted file mode 100644
index c030a70eeb..0000000000
--- a/src/nvim/tempfile.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef NVIM_TEMPFILE_H
-#define NVIM_TEMPFILE_H
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tempfile.h.generated.h"
-#endif
-
-#endif // NVIM_TEMPFILE_H
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 63a7e20880..0440272eb9 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -627,6 +627,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
api_free_object(dict_set_value(buf->b_vars,
cstr_as_string("term_title"),
STRING_OBJ(cstr_as_string(val->string)),
+ false,
&err));
break;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 55a49122d5..4debdd9acc 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -36,9 +36,12 @@ SCRIPTS := \
test_breakindent.out \
test_close_count.out \
test_marks.out \
- test_match_conceal.out \
-NEW_TESTS = test_viml.res
+# Tests using runtest.vim.vim.
+# Keep test_alot*.res as the last one, sort the others.
+NEW_TESTS = \
+ test_viml.res \
+ test_alot.res
SCRIPTS_GUI := test16.out
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
index 7fef87d383..a8c81b832d 100644
--- a/src/nvim/testdir/test17.in
+++ b/src/nvim/testdir/test17.in
@@ -41,17 +41,17 @@ STARTTEST
:!mkdir Xdir1
:!mkdir "Xdir1/dir2"
:e! Xdir1/dir2/foo.a
-i#include "bar.a"
+i#include "bar.a":
:w
:e Xdir1/dir2/bar.a
-i#include "baz.a"
+i#include "baz.a":
:w
:e Xdir1/dir2/baz.a
-i#include "foo.a"
+i#include "foo.a":
:w
:e Xbase.a
:set path=Xdir1/dir2
-i#include <foo.a>
+i#include <foo.a>:
:w
:redir! >>test.out
:checkpath!
@@ -71,17 +71,17 @@ STARTTEST
:endfunction
:let &includeexpr='DotsToSlashes()'
:e! Xdir1/dir2/foo.b
-i%inc /bar/
+i%inc /bar/:
:w
:e Xdir1/dir2/bar.b
-i%inc /baz/
+i%inc /baz/:
:w
:e Xdir1/dir2/baz.b
-i%inc /foo/
+i%inc /foo/:
:w
:e Xbase.b
:set path=Xdir1/dir2
-i%inc /foo/
+i%inc /foo/:
:w
:redir! >>test.out
:checkpath!
@@ -104,20 +104,20 @@ STARTTEST
:endfunction
:let &includeexpr='StripNewlineChar()'
:e! Xdir1/dir2/foo.c
-i%inc bar.c
+i%inc bar.c:
:w
:e Xdir1/dir2/bar.c
-i%inc baz.c
+i%inc baz.c:
:w
:e Xdir1/dir2/baz.c
-i%inc foo.c
+i%inc foo.c:
:w
:e Xdir1/dir2/FALSE.c
-i%inc foo.c
+i%inc foo.c:
:w
:e Xbase.c
:set path=Xdir1/dir2
-i%inc FALSE.c foo.c
+i%inc FALSE.c foo.c:
:w
:redir! >>test.out
:checkpath!
diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in
index 1ce57246ee..d95052e14c 100644
--- a/src/nvim/testdir/test49.in
+++ b/src/nvim/testdir/test49.in
@@ -8,7 +8,9 @@ STARTTEST
:se nomore
:lang mess C
:so test49.vim
-GGGGGGGGGGGGGG"rp:.-,$w! test.out
+:" Go back to this file and append the results from register r.
+:buf test49.in
+G"rp:/^Results/,$w! test.out
:"
:" make valgrind happy
:redir => funclist
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
new file mode 100644
index 0000000000..1d1da94bac
--- /dev/null
+++ b/src/nvim/testdir/test_alot.vim
@@ -0,0 +1,3 @@
+" A series of tests that can run in one Vim invocation.
+" This makes testing go faster, since Vim doesn't need to restart.
+
diff --git a/src/nvim/testdir/test_marks.in b/src/nvim/testdir/test_marks.in
deleted file mode 100644
index 23c2fb65fe..0000000000
--- a/src/nvim/testdir/test_marks.in
+++ /dev/null
@@ -1,34 +0,0 @@
-Tests for marks.
-
-STARTTEST
-:so small.vim
-:" test that a deleted mark is restored after delete-undo-redo-undo
-:/^\t/+1
-:set nocp viminfo+=nviminfo
-madduu
-:let a = string(getpos("'a"))
-:$put ='Mark after delete-undo-redo-undo: '.a
-:''
-ENDTEST
-
- textline A
- textline B
- textline C
-
-STARTTEST
-:" test that CTRL-A and CTRL-X updates last changed mark '[, '].
-:/^123/
-:execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX"
-ENDTEST
-
-CTRL-A CTRL-X:
-123 123 123
-123 123 123
-123 123 123
-
-STARTTEST
-:g/^STARTTEST/.,/^ENDTEST/d
-:wq! test.out
-ENDTEST
-
-Results:
diff --git a/src/nvim/testdir/test_marks.ok b/src/nvim/testdir/test_marks.ok
deleted file mode 100644
index e6c02ee7b0..0000000000
--- a/src/nvim/testdir/test_marks.ok
+++ /dev/null
@@ -1,16 +0,0 @@
-Tests for marks.
-
-
- textline A
- textline B
- textline C
-
-
-CTRL-A CTRL-X:
-AAA 123 123
-123 XXXXXXX
-XXX 123 123
-
-
-Results:
-Mark after delete-undo-redo-undo: [0, 15, 2, 0]
diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim
index 5d65953a9e..9f0618bd45 100644
--- a/src/nvim/testdir/test_viml.vim
+++ b/src/nvim/testdir/test_viml.vim
@@ -1,5 +1,5 @@
" Test various aspects of the Vim language.
-" This was formerly in test49.
+" Most of this was formerly in test49.
"-------------------------------------------------------------------------------
" Test environment {{{1
@@ -908,6 +908,22 @@ endfunc
"-------------------------------------------------------------------------------
+" Test 16: Recognizing {} in variable name. {{{1
+"-------------------------------------------------------------------------------
+
+func Test_curlies()
+ let s:var = 66
+ let ns = 's'
+ call assert_equal(66, {ns}:var)
+
+ let g:a = {}
+ let g:b = 't'
+ let g:a[g:b] = 77
+ call assert_equal(77, g:a['t'])
+endfunc
+
+
+"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=4 tw=80 fdm=marker
" vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "")
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 3b160d0d99..a492c8d7ec 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -69,80 +69,395 @@ static char *features[] = {
// clang-format off
static int included_patches[] = {
+ 1757,
+ 1755,
1654,
1652,
1643,
1641,
+
+ // 1600 NA
+ // 1599 NA
+ // 1598 NA
+ // 1597 NA
+ // 1596,
+ // 1595 NA
+ // 1594 NA
+ // 1593 NA
+ // 1592,
+ // 1591,
+ // 1590,
+ // 1589,
+ // 1588,
+ // 1587 NA
+ // 1586,
+ // 1585,
+ // 1584 NA
+ // 1583 NA
+ // 1582,
+
+ // 1581,
+ // 1580,
+ // 1579,
+ // 1578,
+ // 1577,
+ 1576,
+ // 1575 NA
1574,
+ // 1573,
+ // 1572 NA
+ // 1571,
1570,
+ 1569,
+ // 1568,
+ // 1567,
+ // 1566 NA
+ // 1565,
+ // 1564,
+ // 1563,
+ // 1562,
+ // 1561 NA
+ // 1560,
+ // 1559,
+ // 1558,
+ // 1557,
+ // 1556,
+ // 1555,
+ // 1554,
+ // 1553,
+ // 1552,
+ // 1551,
+ // 1550,
+ // 1549,
+ // 1548,
+ // 1547,
+ // 1546,
+ // 1545 NA
+ // 1544 NA
+ // 1543 NA
+ // 1542 NA
+ // 1541 NA
+ // 1540 NA
+ // 1539 NA
+ // 1538 NA
+ // 1537 NA
+ // 1536 NA
+ // 1535,
+ // 1534 NA
+ // 1533,
+ // 1532 NA
+ // 1531 NA
+ // 1530 NA
+ // 1529 NA
+ // 1528,
+ // 1527 NA
+ // 1526 NA
+ // 1525 NA
+ // 1524 NA
+ // 1523 NA
+ // 1522 NA
+ // 1521,
+ // 1520 NA
+ // 1519 NA
+ // 1518 NA
+ // 1517 NA
+ // 1516,
+ // 1515 NA
+ // 1514 NA
+ // 1513,
+ // 1512 NA
1511,
+ // 1510 NA
+ // 1509 NA
+ // 1508 NA
+ // 1507 NA
+ // 1506 NA
+ // 1505 NA
+ // 1504 NA
+ // 1503 NA
+ // 1502 NA
+ // 1501 NA
+ 1500,
+ // 1499,
+ // 1498 NA
+ // 1497 NA
+ // 1496 NA
+ // 1495 NA
+ // 1494,
+ // 1493 NA
+ // 1492,
+ // 1491,
+ // 1490 NA
+ // 1489 NA
+ // 1488 NA
+ // 1487 NA
+ // 1486,
+ // 1485 NA
+ // 1484 NA
+ // 1483 NA
+ // 1482 NA
+ // 1481 NA
+ // 1480,
+ // 1479,
+ // 1478,
+ // 1477,
+ // 1476 NA
+ // 1475 NA
+ // 1474 NA
+ // 1473 NA
+ // 1472 NA
+ // 1471 NA
+ // 1470 NA
+ // 1469 NA
+ // 1468,
+ // 1467 NA
+ // 1466 NA
+ // 1465 NA
+ // 1464,
+ // 1463 NA
+ // 1462 NA
+ // 1461 NA
+ // 1460 NA
+ // 1459 NA
+ // 1458 NA
+ // 1457 NA
+ // 1456,
+ // 1455 NA
+ // 1454 NA
+ // 1453 NA
+ // 1452 NA
+ // 1451 NA
+ // 1450 NA
+ // 1449 NA
+ // 1448 NA
+ // 1447 NA
+ // 1446 NA
+ // 1445 NA
+ // 1444 NA
+ // 1443 NA
+ // 1442 NA
+ // 1441 NA
+ // 1440 NA
+ // 1439 NA
+ // 1438 NA
+ // 1437 NA
+ // 1436 NA
+ // 1435 NA
+ // 1434 NA
+ // 1433 NA
+ // 1432 NA
+ // 1431 NA
+ // 1430 NA
+ // 1429 NA
+ // 1428 NA
+ // 1427 NA
+ // 1426 NA
1425,
+ // 1424 NA
+ // 1423 NA
+ // 1422 NA
+ // 1421 NA
+ // 1420 NA
+ // 1419 NA
+ // 1418 NA
+ // 1417 NA
+ // 1416 NA
+ // 1415 NA
+ // 1414 NA
+ // 1413 NA
+ // 1412 NA
+ // 1411 NA
+ 1410,
+ // 1409 NA
+ // 1408 NA
+ // 1407 NA
+ 1406,
+ 1405,
+ // 1404 NA
+ // 1403 NA
+ // 1402 NA
+ // 1401,
+ // 1400 NA
+ // 1399 NA
+ // 1398 NA
+ // 1397,
+ // 1396,
+ // 1395 NA
+ // 1394,
+ // 1393 NA
+ // 1392 NA
+ // 1391 NA
+ // 1390 NA
+ // 1389 NA
+ // 1388,
+ // 1387 NA
+ // 1386 NA
+ // 1385 NA
+ // 1384,
+ // 1383 NA
+ // 1382 NA
+ // 1381 NA
+ // 1380 NA
+ // 1379 NA
+ // 1378 NA
+ // 1377 NA
+ // 1376 NA
+ // 1375 NA
+ // 1374 NA
+ // 1373 NA
+ // 1372 NA
+ // 1371 NA
+ // 1370 NA
+ // 1369 NA
+ // 1368 NA
+ // 1367 NA
1366,
+ // 1365,
+ // 1364 NA
+ // 1363 NA
+ // 1362 NA
+ // 1361 NA
+ // 1360 NA
+ // 1359 NA
+ // 1358 NA
+ // 1357 NA
+ // 1356 NA
+ // 1355 NA
+ // 1354 NA
+ // 1353 NA
+ // 1352,
+ // 1351 NA
+ // 1350 NA
+ // 1349 NA
+ // 1348 NA
+ // 1347,
+ 1346,
+ // 1345 NA
+ // 1344 NA
+ // 1343 NA
+ // 1342 NA
+ // 1341 NA
+ // 1340 NA
+ // 1339 NA
+ // 1338 NA
+ // 1337 NA
+ // 1336 NA
+ // 1335 NA
+ // 1334 NA
+ // 1333 NA
+ // 1332 NA
+ // 1331 NA
+ // 1330 NA
+ // 1329 NA
+ // 1328 NA
+ // 1327 NA
+ // 1326 NA
+ // 1325 NA
+ // 1324 NA
+ // 1323 NA
+ // 1322 NA
+ // 1321 NA
+ // 1320 NA
+ // 1319 NA
+ // 1318 NA
+ // 1317 NA
+ // 1316 NA
+ // 1315 NA
+ // 1314 NA
+ // 1313 NA
+ // 1312 NA
+ // 1311 NA
+ // 1310 NA
+ 1309,
+ // 1308 NA
+ // 1307 NA
+ // 1306 NA
+ // 1305,
+ 1304,
+ // 1303 NA
+ // 1302 NA
+ // 1301 NA
+ // 1300,
+ // 1299 NA
+ // 1298 NA
+ // 1297 NA
+ // 1296,
+ // 1295 NA
+ // 1294 NA
+ // 1293 NA
+ 1292,
+ // 1291 NA
+ // 1290 NA
+ // 1289 NA
+ // 1288 NA
+ // 1287 NA
+ // 1286 NA
+ // 1285,
1284,
- // 1283
+ // 1283 NA
1282,
- // 1281
- // 1280
- // 1279
- // 1278
- // 1277
- // 1276
- // 1275
- // 1274
- // 1273
- // 1272
- // 1271
- // 1270
- // 1269
- // 1268
- // 1267
+ // 1281,
+ // 1280 NA
+ // 1279 NA
+ // 1278 NA
+ // 1277 NA
+ // 1276,
+ // 1275 NA
+ // 1274 NA
+ // 1273,
+ // 1272 NA
+ 1271,
+ // 1270 NA
+ 1269,
+ // 1268 NA
+ 1267,
// 1266
- // 1265
- // 1264
- // 1263
- // 1262
- // 1261
- // 1260
- // 1259
- // 1258
- // 1257
- // 1256
- // 1255
- // 1254
- // 1253
- // 1252
- // 1251
- // 1250
- // 1249
- // 1248
- // 1247
- // 1246
- // 1245
- // 1244
- // 1243
- // 1242
- // 1241
- // 1240
- // 1239
- // 1238
- // 1237
- // 1236
- // 1235
- // 1234
- // 1233
- // 1232
- // 1231
- // 1230
- // 1229
- // 1228
- // 1227
- // 1226
- // 1225
- // 1224
- // 1223
- // 1223
- // 1221
- // 1220
+ // 1265 NA
+ // 1264 NA
+ // 1263 NA
+ // 1262 NA
+ // 1261 NA
+ // 1260 NA
+ // 1259,
+ // 1258 NA
+ // 1257 NA
+ // 1256 NA
+ // 1255 NA
+ // 1254 NA
+ // 1253 NA
+ // 1252 NA
+ // 1251 NA
+ // 1250 NA
+ // 1249 NA
+ // 1248 NA
+ // 1247 NA
+ // 1246 NA
+ // 1245 NA
+ // 1244 NA
+ // 1243 NA
+ // 1242 NA
+ // 1241 NA
+ // 1240 NA
+ // 1239 NA
+ // 1238 NA
+ // 1237,
+ // 1236,
+ // 1235 NA
+ // 1234 NA
+ // 1233 NA
+ // 1232 NA
+ // 1231 NA
+ // 1230 NA
+ // 1229 NA
+ 1228,
+ // 1227 NA
+ // 1226 NA
+ // 1225 NA
+ // 1224 NA
+ // 1223,
+ // 1222 NA
+ // 1221 NA
+ // 1220 NA
// 1219 NA
// 1218 NA
// 1217 NA
@@ -174,41 +489,41 @@ static int included_patches[] = {
// 1191 NA
// 1190 NA
// 1189 NA
- // 1188,
+ // 1188 NA
// 1187 NA
// 1186,
// 1185 NA
// 1184 NA
// 1183 NA
// 1182 NA
- // 1181,
+ 1181,
1180,
// 1179,
- // 1178,
+ 1178,
// 1177 NA
// 1176 NA
// 1175 NA
// 1174 NA
- // 1173,
+ 1173,
// 1172 NA
// 1171 NA
// 1170 NA
// 1169 NA
- // 1168,
- // 1167,
- // 1166,
+ 1168,
+ 1167,
+ 1166,
// 1165 NA
- // 1164,
- // 1163,
+ 1164,
+ 1163,
// 1162 NA
// 1161,
- // 1160,
+ 1160,
// 1159 NA
// 1158 NA
- // 1157,
- // 1156,
+ 1157,
+ // 1156 NA
// 1155 NA
- // 1154,
+ // 1154 NA
// 1153,
// 1152 NA
// 1151,
@@ -218,8 +533,8 @@ static int included_patches[] = {
// 1147,
// 1146 NA
// 1145 NA
- // 1144 NA
- // 1143,
+ 1144,
+ 1143,
// 1142,
1141,
// 1140,
@@ -227,8 +542,8 @@ static int included_patches[] = {
// 1138 NA
1137,
// 1136,
- // 1135 NA,
- // 1134 NA,
+ // 1135 NA
+ // 1134 NA
// 1133 NA
// 1132,
// 1131 NA
@@ -242,20 +557,20 @@ static int included_patches[] = {
// 1123,
// 1122 NA
// 1121,
- // 1120,
+ 1120,
// 1119,
// 1118,
- // 1117,
- // 1116,
+ 1117,
+ 1116,
// 1115 NA
- // 1114,
- // 1113,
+ 1114,
+ 1113,
1112,
// 1111,
// 1110,
// 1109 NA
// 1108,
- // 1107,
+ 1107,
// 1106 NA
1105,
// 1104 NA
@@ -269,7 +584,7 @@ static int included_patches[] = {
// 1096,
// 1095 NA
// 1094,
- // 1093,
+ 1093,
// 1092,
// 1091,
// 1090,
@@ -279,30 +594,30 @@ static int included_patches[] = {
// 1086,
1085,
1084,
- // 1083 NA,
- // 1082 NA,
+ // 1083 NA
+ // 1082 NA
1081,
- // 1080 NA,
+ // 1080 NA
// 1079,
- // 1078 NA,
- // 1077 NA,
+ // 1078 NA
+ // 1077 NA
1076,
// 1075,
- // 1074 NA,
+ // 1074 NA
// 1073,
1072,
// 1071,
- // 1070 NA,
- // 1069 NA,
+ // 1070 NA
+ // 1069 NA
// 1068,
- // 1067 NA,
- // 1066 NA,
+ // 1067 NA
+ // 1066 NA
1065,
// 1064,
- // 1063 NA,
- // 1062 NA,
+ // 1063 NA
+ // 1062 NA
// 1061,
- // 1060 NA,
+ // 1060 NA
// 1059,
// 1058,
// 1057,
@@ -317,44 +632,44 @@ static int included_patches[] = {
// 1048,
// 1047,
// 1046,
- // 1045 NA,
- // 1044 NA,
- // 1043 NA,
+ // 1045 NA
+ // 1044 NA
+ // 1043 NA
// 1042,
1041,
- // 1040 NA,
+ // 1040 NA
// 1039,
- // 1038 NA,
+ // 1038 NA
// 1037,
// 1036,
// 1035,
// 1034,
- // 1033 NA,
+ // 1033 NA
1032,
// 1031 NA,
1030,
1029,
- // 1028 NA,
+ // 1028 NA
1027,
- // 1026 NA,
- // 1025 NA,
- // 1024 NA,
- // 1023 NA,
- // 1022 NA,
- // 1021 NA,
- // 1020 NA,
- // 1019 NA,
+ // 1026 NA
+ // 1025 NA
+ // 1024 NA
+ // 1023 NA
+ // 1022 NA
+ // 1021 NA
+ // 1020 NA
+ // 1019 NA
// 1018,
// 1017,
- // 1016 NA,
+ // 1016 NA
// 1015,
- // 1014 NA,
+ // 1014 NA
1013,
- // 1012 NA,
- // 1011 NA,
+ // 1012 NA
+ // 1011 NA
// 1010,
- // 1009 NA,
- // 1008 NA,
+ // 1009 NA
+ // 1008 NA
// 1007,
// 1006,
// 1005,
@@ -364,7 +679,7 @@ static int included_patches[] = {
1001,
1000,
// 999 NA
- // 998,
+ 998,
// 997 NA
// 996 NA
// 995 NA
@@ -378,8 +693,8 @@ static int included_patches[] = {
// 987 NA
// 986 NA
// 985 NA
- // 984,
- // 983,
+ 984,
+ // 983 NA
// 982 NA
981,
980,
@@ -405,13 +720,13 @@ static int included_patches[] = {
// 960 NA
// 959 NA
958,
- // 957,
+ 957,
// 956 NA
955,
// 954 NA
953,
952,
- // 951,
+ 951,
950,
949,
// 948 NA
@@ -420,8 +735,8 @@ static int included_patches[] = {
945,
944,
// 943 NA
- // 942,
- // 941,
+ 942,
+ 941,
// 940 NA
939,
// 938 NA
@@ -540,10 +855,10 @@ static int included_patches[] = {
825,
// 824 NA
823,
- // 822,
+ 822,
// 821 NA
820,
- // 819,
+ 819,
818,
817,
816,
@@ -565,7 +880,7 @@ static int included_patches[] = {
800,
799,
798,
- // 797,
+ // 797 NA
// 796 NA
795,
// 794 NA
@@ -629,7 +944,7 @@ static int included_patches[] = {
736,
// 735 NA
734,
- // 733,
+ // 733 NA
732,
// 731 NA
// 730 NA
@@ -1118,13 +1433,13 @@ static int included_patches[] = {
247,
// 246 NA
245,
- // 244,
+ // 244 NA
243,
242,
241,
240,
239,
- // 238,
+ // 238 NA
237,
236,
235,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 545b903d2f..623ea19e36 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -271,26 +271,11 @@ enum {
# define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs))
-#define MSG(s) msg((char_u *)(s))
-#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr))
-#define EMSG(s) emsg((char_u *)(s))
-#define EMSG2(s, p) emsg2((char_u *)(s), (char_u *)(p))
-#define EMSG3(s, p, q) emsg3((char_u *)(s), (char_u *)(p), \
- (char_u *)(q))
-#define EMSGN(s, n) emsgn((char_u *)(s), (int64_t)(n))
-#define EMSGU(s, n) emsgu((char_u *)(s), (uint64_t)(n))
-#define OUT_STR(s) out_str((char_u *)(s))
-#define OUT_STR_NF(s) out_str_nf((char_u *)(s))
-#define MSG_PUTS(s) msg_puts((char_u *)(s))
-#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a))
-#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s))
-#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0)
-#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a))
-
-/* Prefer using emsg3(), because perror() may send the output to the wrong
- * destination and mess up the screen. */
-#define PERROR(msg) \
- (void) emsg3((char_u *) "%s: %s", (char_u *)msg, (char_u *)strerror(errno))
+#include "nvim/message.h"
+
+// Prefer using emsgf(), because perror() may send the output to the wrong
+// destination and mess up the screen.
+#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno))
#define SHOWCMD_COLS 10 /* columns needed by shown command */
#define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 1b8c953596..bea55c465f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -97,7 +97,7 @@ do_window (
* don't replicate the quickfix buffer. */
if (bt_quickfix(curbuf))
goto newwindow;
- win_split((int)Prenum, 0);
+ (void)win_split((int)Prenum, 0);
break;
/* split current window in two parts, vertically */
@@ -108,7 +108,7 @@ do_window (
* don't replicate the quickfix buffer. */
if (bt_quickfix(curbuf))
goto newwindow;
- win_split((int)Prenum, WSP_VERT);
+ (void)win_split((int)Prenum, WSP_VERT);
break;
/* split current window and edit alternate file */
@@ -2948,7 +2948,7 @@ void free_tabpage(tabpage_T *tp)
unref_var_dict(tp->tp_vars);
-
+ xfree(tp->localdir); // Free tab-local working directory
xfree(tp);
}
@@ -3560,18 +3560,24 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri
curwin->w_cursor.coladd = 0;
changed_line_abv_curs(); /* assume cursor position needs updating */
- if (curwin->w_localdir != NULL) {
- /* Window has a local directory: Save current directory as global
- * directory (unless that was done already) and change to the local
- * directory. */
+ // The new directory is either the local directory of the window, of the tab
+ // or NULL.
+ char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir;
+
+ if (new_dir) {
+ // Window/tab has a local directory: Save current directory as global
+ // directory (unless that was done already) and change to the local
+ // directory.
if (globaldir == NULL) {
char_u cwd[MAXPATHL];
- if (os_dirname(cwd, MAXPATHL) == OK)
+ if (os_dirname(cwd, MAXPATHL) == OK) {
globaldir = vim_strsave(cwd);
+ }
+ }
+ if (os_chdir((char *)new_dir) == 0) {
+ shorten_fnames(true);
}
- if (os_chdir((char *)curwin->w_localdir) == 0)
- shorten_fnames(TRUE);
} else if (globaldir != NULL) {
/* Window doesn't have a local directory and we are not in the global
* directory: Change to the global directory. */