Merge pull request #4131 from ZyX-I/json-functions

Add JSON support
This commit is contained in:
Justin M. Keyes 2016-04-17 20:24:23 -04:00
commit 007d573147
64 changed files with 5355 additions and 2106 deletions

View File

@ -356,6 +356,8 @@ let s:MSGPACK_STANDARD_TYPES = {
\type(''): 'binary',
\type([]): 'array',
\type({}): 'map',
\type(v:true): 'boolean',
\type(v:null): 'nil',
\}
""
@ -379,7 +381,7 @@ endfunction
""
" Dump boolean value.
function s:msgpack_dump_boolean(v) abort
return a:v._VAL ? 'TRUE' : 'FALSE'
return (a:v is v:true || (a:v isnot v:false && a:v._VAL)) ? 'TRUE' : 'FALSE'
endfunction
""

View File

@ -1418,6 +1418,13 @@ v:exception The value of the exception most recently caught and not
:endtry
< Output: "caught oops".
*v:false* *false-variable*
v:false Special value used to put "false" in JSON and msgpack. See
|json_encode()|. This value is converted to "false" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
*v:fcs_reason* *fcs_reason-variable*
v:fcs_reason The reason why the |FileChangedShell| event was triggered.
Can be used in an autocommand to decide what to do and/or what
@ -1557,6 +1564,13 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()|
(not editable) empty lists. To check whether some list is one
of msgpack types, use |is| operator.
*v:null* *null-variable*
v:null Special value used to put "null" in JSON and NIL in msgpack.
See |json_encode()|. This value is converted to "null" when
used as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
*v:oldfiles* *oldfiles-variable*
v:oldfiles List of file names that is loaded from the |shada| file on
startup. These are the files that Vim remembers marks for.
@ -1722,6 +1736,13 @@ v:throwpoint The point where the exception most recently caught and not
:endtry
< Output: "Exception from test.vim, line 2"
*v:true* *true-variable*
v:true Special value used to put "true" in JSON and msgpack. See
|json_encode()|. This value is converted to "true" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to one when used as a Number (e.g. in |expr5| or
|expr7| when used with numeric operators).
*v:val* *val-variable*
v:val Value of the current item of a |List| or |Dictionary|. Only
valid while evaluating the expression used with |map()| and
@ -1932,6 +1953,8 @@ jobstart( {cmd}[, {opts}]) Number Spawns {cmd} as a job
jobstop( {job}) Number Stops a job
jobwait( {ids}[, {timeout}]) Number Wait for a set of jobs
join( {list} [, {sep}]) String join {list} items into one String
json_decode( {expr}) any Convert {expr} from JSON
json_encode( {expr}) String Convert {expr} to JSON
keys( {dict}) List keys in {dict}
len( {expr}) Number the length of {expr}
libcall( {lib}, {func}, {arg}) String call {func} in library {lib} with {arg}
@ -2222,17 +2245,17 @@ assert_equal({expected}, {actual}, [, {msg}])
assert_false({actual}, [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|..
A value is false when it is zero. When "{actual}" is not a
number the assert fails.
|v:errors|, like with |assert_equal()|.
A value is false when it is zero or |v:false|. When "{actual}"
is not a number or |v:false| the assert fails.
When {msg} is omitted an error in the form "Expected False but
got {actual}" is produced.
assert_true({actual}, [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to
|v:errors|, like with |assert_equal()|..
A value is true when it is a non-zeron number. When {actual}
is not a number the assert fails.
|v:errors|, like with |assert_equal()|.
A value is true when it is a non-zero number or |v:true|.
When {actual} is not a number or |v:true| the assert fails.
When {msg} is omitted an error in the form "Expected True but
got {actual}" is produced.
@ -2826,9 +2849,8 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
empty({expr}) *empty()*
Return the Number 1 if {expr} is empty, zero otherwise.
A |List| or |Dictionary| is empty when it does not have any
items. A Number is empty when its value is zero.
For a long |List| this is much faster than comparing the
length with zero.
items. A Number is empty when its value is zero. Special
variable is empty when it is |v:false| or |v:null|.
escape({string}, {chars}) *escape()*
Escape the characters in {chars} that occur in {string} with a
@ -4291,6 +4313,46 @@ join({list} [, {sep}]) *join()*
converted into a string like with |string()|.
The opposite function is |split()|.
json_decode({expr}) *json_decode()*
Convert {expr} from JSON object. Accepts |readfile()|-style
list as the input, as well as regular string. May output any
Vim value. When 'encoding' is not UTF-8 string is converted
from UTF-8 to 'encoding', failing conversion fails
json_decode(). In the following cases it will output
|msgpack-special-dict|:
1. Dictionary contains duplicate key.
2. Dictionary contains empty key.
3. String contains NUL byte. Two special dictionaries: for
dictionary and for string will be emitted in case string
with NUL byte was a dictionary key.
Note: function treats its input as UTF-8 always regardless of
'encoding' value. This is needed because JSON source is
supposed to be external (e.g. |readfile()|) and JSON standard
allows only a few encodings, of which UTF-8 is recommended and
the only one required to be supported. Non-UTF-8 characters
are an error.
json_encode({expr}) *json_encode()*
Convert {expr} into a JSON string. Accepts
|msgpack-special-dict| as the input. Converts from 'encoding'
to UTF-8 when encoding strings. Will not convert |Funcref|s,
mappings with non-string keys (can be created as
|msgpack-special-dict|), values with self-referencing
containers, strings which contain non-UTF-8 characters,
pseudo-UTF-8 strings which contain codepoints reserved for
surrogate pairs (such strings are not valid UTF-8 strings).
When converting 'encoding' is taken into account, if it is not
"utf-8", then conversion is performed before encoding strings.
Non-printable characters are converted into "\u1234" escapes
or special escapes like "\t", other are dumped as-is.
Note: all characters above U+0079 are considered non-printable
when 'encoding' is not UTF-8. This function always outputs
UTF-8 strings as required by the standard thus when 'encoding'
is not unicode resulting string will look incorrect if
"\u1234" notation is not used.
keys({dict}) *keys()*
Return a |List| with all the keys of {dict}. The |List| is in
arbitrary order.
@ -4818,7 +4880,7 @@ msgpackdump({list}) {Nvim} *msgpackdump()*
(dictionary with zero items is represented by 0x80 byte in
messagepack).
Limitations: *E951* *E952*
Limitations: *E951* *E952* *E953*
1. |Funcref|s cannot be dumped.
2. Containers that reference themselves cannot be dumped.
3. Dictionary keys are always dumped as STR strings.
@ -4853,9 +4915,13 @@ msgpackparse({list}) {Nvim} *msgpackparse()*
contains name of the key from |v:msgpack_types|):
Key Value ~
nil Zero, ignored when dumping.
boolean One or zero. When dumping it is only checked that
value is a |Number|.
nil Zero, ignored when dumping. This value cannot
possibly appear in |msgpackparse()| output in Neovim
versions which have |v:null|.
boolean One or zero. When dumping it is only checked that
value is a |Number|. This value cannot possibly
appear in |msgpackparse()| output in Neovim versions
which have |v:true| and |v:false|.
integer |List| with four numbers: sign (-1 or 1), highest two
bits, number with bits from 62nd to 31st, lowest 31
bits. I.e. to get actual number one will need to use
@ -6729,12 +6795,14 @@ trunc({expr}) *trunc()*
type({expr}) *type()*
The result is a Number, depending on the type of {expr}:
Number: 0
String: 1
Number: 0
String: 1
Funcref: 2
List: 3
List: 3
Dictionary: 4
Float: 5
Float: 5
Boolean: 6 (|v:true| and |v:false|)
Null: 7 (|v:null|)
To avoid the magic numbers it should be used this way: >
:if type(myvar) == type(0)
:if type(myvar) == type("")
@ -6742,6 +6810,10 @@ type({expr}) *type()*
:if type(myvar) == type([])
:if type(myvar) == type({})
:if type(myvar) == type(0.0)
:if type(myvar) == type(v:true)
< In place of checking for |v:null| type it is better to check
for |v:null| directly as it is the only value of this type: >
:if myvar is v:null
undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file

View File

@ -99,6 +99,22 @@ are always available and may be used simultaneously in separate plugins. The
error out.
4. Stringifyed infinite and NaN values now use |str2float()| and can be evaled
back.
5. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in
nothing, |E908|, in Neovim it is internal error.
|json_decode()| behaviour changed:
1. It may output |msgpack-special-dict|.
2. |msgpack-special-dict| is emitted also in case of duplicate keys, while in
Vim it errors out.
3. It accepts only valid JSON. Trailing commas are not accepted.
|json_encode()| behaviour slightly changed: now |msgpack-special-dict| values
are accepted, but |v:none| is not.
*v:none* variable is absent. In Vim it represents “no value” in “js” strings
like "[,]" parsed as "[v:none]" by |js_decode()|.
*js_encode()* and *js_decode()* functions are also absent.
Viminfo text files were replaced with binary (messagepack) ShaDa files.
Additional differences:

View File

@ -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)

View File

@ -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

View File

@ -99,4 +99,3 @@ struct key_value_pair {
#endif // NVIM_API_PRIVATE_DEFS_H

View File

@ -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;

View File

@ -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

View File

@ -331,15 +331,31 @@ Object vim_get_var(String name, Error *err)
return dict_get_value(&globvardict, name, err);
}
/// Sets a global variable. Passing 'nil' as value deletes the variable.
/// Sets a global variable
///
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
/// @return the old value if any
/// @return The old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err)
{
return dict_set_value(&globvardict, name, value, err);
return dict_set_value(&globvardict, name, value, false, err);
}
/// Removes a global variable
///
/// @param name The variable name
/// @param[out] err Details of an error that may have occurred
/// @return The old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
Object vim_del_var(String name, Error *err)
{
return dict_set_value(&globvardict, name, NIL, true, err);
}
/// Gets a vim variable

View File

@ -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

View File

@ -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

View File

@ -658,9 +658,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 +670,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 +683,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 +693,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 +712,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 +756,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 +767,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 +795,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 +814,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 +901,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

View File

@ -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
@ -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 */
@ -8480,22 +8480,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 +8506,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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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

1116
src/nvim/eval/decode.c Normal file

File diff suppressed because it is too large Load Diff

13
src/nvim/eval/decode.h Normal file
View File

@ -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

1296
src/nvim/eval/encode.c Normal file

File diff suppressed because it is too large Load Diff

75
src/nvim/eval/encode.h Normal file
View File

@ -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

View File

@ -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.
*/
typedef struct {
char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */
char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */
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;
} typval_T;
/// Special variable values
typedef enum {
kSpecialVarFalse, ///< v:false
kSpecialVarTrue, ///< v:true
kSpecialVarNull, ///< v:null
} SpecialVarValue;
/* 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 */
/// 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 {
VarType v_type; ///< Variable type.
VarLockStatus v_lock; ///< Variable lock status.
union {
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 "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

View File

@ -1326,15 +1326,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 +1344,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 +1364,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 = NUL;
}
STRCAT(buf, " < ");
STRCAT(buf, itmp);
if (*p_shq == NUL) {
p = vim_strchr(cmd, '|');
p = strchr(buf, '|');
if (p != NULL) {
STRCAT(buf, " "); // Insert a space before the '|' for DOS
STRCAT(buf, p);
*p = NUL;
}
}
strncat(buf, " < ", len);
strncat(buf, (char *) itmp, len);
if (*p_shq == NUL) {
p = strchr(cmd, '|');
if (p != NULL) {
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 +2096,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;

View File

@ -3168,27 +3168,27 @@ static char_u *get_mess_env(void)
*/
void set_lang_var(void)
{
char_u *loc;
const char *loc;
# ifdef HAVE_GET_LOCALE_VAL
loc = (char_u *)get_locale_val(LC_CTYPE);
loc = get_locale_val(LC_CTYPE);
# else
/* setlocale() not supported: use the default value */
loc = (char_u *)"C";
// setlocale() not supported: use the default value
loc = "C";
# endif
set_vim_var_string(VV_CTYPE, loc, -1);
/* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
* back to LC_CTYPE if it's empty. */
# ifdef HAVE_WORKING_LIBINTL
loc = get_mess_env();
loc = (char *) get_mess_env();
# else
loc = (char_u *)get_locale_val(LC_MESSAGES);
loc = get_locale_val(LC_MESSAGES);
# endif
set_vim_var_string(VV_LANG, loc, -1);
# ifdef HAVE_GET_LOCALE_VAL
loc = (char_u *)get_locale_val(LC_TIME);
loc = get_locale_val(LC_TIME);
# endif
set_vim_var_string(VV_LC_TIME, loc, -1);
}

View File

@ -7421,10 +7421,10 @@ static int mksession_nl = FALSE; /* use NL only in put_eol() */
static void ex_mkrc(exarg_T *eap)
{
FILE *fd;
int failed = FALSE;
int view_session = FALSE;
int using_vdir = FALSE; /* using 'viewdir'? */
char_u *viewFile = NULL;
int failed = false;
int view_session = false;
int using_vdir = false; // using 'viewdir'?
char *viewFile = NULL;
unsigned *flagp;
if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) {
@ -7435,32 +7435,34 @@ static void ex_mkrc(exarg_T *eap)
* short file name when 'acd' is set, that is checked later. */
did_lcd = FALSE;
char_u *fname;
/* ":mkview" or ":mkview 9": generate file name with 'viewdir' */
char *fname;
// ":mkview" or ":mkview 9": generate file name with 'viewdir'
if (eap->cmdidx == CMD_mkview
&& (*eap->arg == NUL
|| (ascii_isdigit(*eap->arg) && eap->arg[1] == NUL))) {
eap->forceit = TRUE;
fname = (char_u *)get_view_file(*eap->arg);
if (fname == NULL)
eap->forceit = true;
fname = get_view_file(*eap->arg);
if (fname == NULL) {
return;
}
viewFile = fname;
using_vdir = TRUE;
} else if (*eap->arg != NUL)
fname = eap->arg;
else if (eap->cmdidx == CMD_mkvimrc)
fname = (char_u *)VIMRC_FILE;
else if (eap->cmdidx == CMD_mksession)
fname = (char_u *)SESSION_FILE;
else
fname = (char_u *)EXRC_FILE;
using_vdir = true;
} else if (*eap->arg != NUL) {
fname = (char *) eap->arg;
} else if (eap->cmdidx == CMD_mkvimrc) {
fname = VIMRC_FILE;
} else if (eap->cmdidx == CMD_mksession) {
fname = SESSION_FILE;
} else {
fname = EXRC_FILE;
}
/* When using 'viewdir' may have to create the directory. */
if (using_vdir && !os_isdir(p_vdir)) {
vim_mkdir_emsg(p_vdir, 0755);
}
fd = open_exfile(fname, eap->forceit, WRITEBIN);
fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN);
if (fd != NULL) {
if (eap->cmdidx == CMD_mkview)
flagp = &vop_flags;
@ -7504,8 +7506,9 @@ static void ex_mkrc(exarg_T *eap)
|| os_chdir((char *)dirnow) != 0)
*dirnow = NUL;
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
if (vim_chdirfile(fname) == OK)
shorten_fnames(TRUE);
if (vim_chdirfile((char_u *) fname) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL
&& (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
if (os_chdir((char *)globaldir) == 0)
@ -7550,15 +7553,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

View File

@ -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);

View File

@ -2139,9 +2139,10 @@ readfile_charconvert (
else {
close(*fdp); /* close the input file, ignore errors */
*fdp = -1;
if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc,
fname, tmpname) == FAIL)
if (eval_charconvert((char *) fenc, enc_utf8 ? "utf-8" : (char *) p_enc,
(char *) fname, (char *) tmpname) == FAIL) {
errmsg = (char_u *)_("Conversion with 'charconvert' failed");
}
if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) {
errmsg = (char_u *)_("can't read output of 'charconvert'");
}
@ -3435,9 +3436,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 +4741,6 @@ buf_check_timestamp (
{
int retval = 0;
char_u *path;
char_u *tbuf;
char *mesg = NULL;
char *mesg2 = "";
int helpmesg = FALSE;
@ -4810,19 +4810,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 +4874,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) {

View File

@ -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) {
@ -2676,7 +2676,7 @@ static void foldlevelExpr(fline_T *flp)
win = curwin;
curwin = flp->wp;
curbuf = flp->wp->w_buffer;
set_vim_var_nr(VV_LNUM, lnum);
set_vim_var_nr(VV_LNUM, (varnumber_T) lnum);
flp->start = 0;
flp->had_end = flp->end;

View File

@ -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;
}
}

View File

@ -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"

View File

@ -2780,11 +2780,13 @@ void mch_print_end(prt_settings_T *psettings)
}
prt_message((char_u *)_("Sending to printer..."));
/* Not printing to a file: use 'printexpr' to print the file. */
if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
// Not printing to a file: use 'printexpr' to print the file.
if (eval_printexpr((char *) prt_ps_file_name, (char *) psettings->arguments)
== FAIL) {
EMSG(_("E365: Failed to print PostScript file"));
else
} else {
prt_message((char_u *)_("Print job sent."));
}
}
mch_print_cleanup();

View File

@ -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++;

View File

@ -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))

View File

@ -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

View File

@ -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");
}

View File

@ -3194,7 +3194,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

View File

@ -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)
/// 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, a1, a2);
return emsg(IObuff);
}
va_list ap;
va_start(ap, fmt);
vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL);
va_end(ap);
/// Print an error message with one "%" PRId64 and one (int64_t) argument.
int emsgn(char_u *s, int64_t n)
{
if (emsg_not_now()) {
return TRUE; // no error messages at the moment
}
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
}
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;
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 = (char_u *)_("Unknown");
else {
len = (int)STRLEN(p) + 40;
// 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;

View File

@ -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

View File

@ -2250,7 +2250,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) {

View File

@ -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;
}

View File

@ -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

View File

@ -7057,18 +7057,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);

View File

@ -1448,7 +1448,7 @@ do_set (
char_u *oldval = NULL; // previous value if *varp
char_u *newval;
char_u *origval = NULL;
char_u *saved_origval = NULL;
char *saved_origval = NULL;
unsigned newlen;
int comma;
int bs;
@ -1725,7 +1725,7 @@ do_set (
if (!starting && origval != NULL) {
// origval may be freed by
// did_set_string_option(), make a copy.
saved_origval = vim_strsave(origval);
saved_origval = xstrdup((char *) origval);
}
/* Handle side effects, and set the global value for
@ -1740,11 +1740,10 @@ do_set (
}
if (saved_origval != NULL) {
char_u buf_type[7];
vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
char buf_type[7];
vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW,
*(char_u **)varp, -1);
set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1);
set_vim_var_string(VV_OPTION_OLD, saved_origval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
apply_autocmds(EVENT_OPTIONSET,
@ -2324,7 +2323,7 @@ set_string_option (
char_u *s;
char_u **varp;
char_u *oldval;
char_u *saved_oldval = NULL;
char *saved_oldval = NULL;
char_u *r = NULL;
if (options[opt_idx].var == NULL) /* don't set hidden option */
@ -2340,7 +2339,7 @@ set_string_option (
*varp = s;
if (!starting) {
saved_oldval = vim_strsave(oldval);
saved_oldval = xstrdup((char *) oldval);
}
if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL,
@ -2349,10 +2348,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,
@ -3800,7 +3799,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 +3846,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 +4236,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);

View File

@ -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

View File

@ -1495,13 +1495,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 +1522,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 +1539,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);

View File

@ -2440,8 +2440,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 +2477,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

View File

@ -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);

View File

@ -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.

View File

@ -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;
}

View File

@ -78,24 +78,26 @@ static int included_patches[] = {
1511,
1425,
1366,
1304,
1292,
1284,
// 1283
1282,
// 1281
// 1280
// 1279
// 1278
// 1279 NA
// 1278 NA
// 1277
// 1276
// 1275
// 1274
// 1273
// 1272
// 1271
1271,
// 1270
// 1269
1269,
// 1268
// 1267
1267,
// 1266
// 1265
// 1264
@ -119,7 +121,7 @@ static int included_patches[] = {
// 1246
// 1245
// 1244
// 1243
// 1243 NA
// 1242
// 1241
// 1240
@ -131,10 +133,10 @@ static int included_patches[] = {
// 1234
// 1233
// 1232
// 1231
// 1231 NA
// 1230
// 1229
// 1228
1228,
// 1227
// 1226
// 1225
@ -181,32 +183,32 @@ static int included_patches[] = {
// 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,
// 1153,

View File

@ -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))
#include "nvim/message.h"
/* 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))
// 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 */

View File

@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')
local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer
local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq
local curbufmeths, ok = helpers.curbufmeths, helpers.ok
local funcs = helpers.funcs
describe('buffer_* functions', function()
before_each(clear)
@ -234,11 +235,14 @@ describe('buffer_* functions', function()
end)
describe('{get,set}_var', function()
describe('{get,set,del}_var', function()
it('works', function()
curbuf('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua'))
eq({1, 2, {['3'] = 1}}, nvim('eval', 'b:lua'))
eq(1, funcs.exists('b:lua'))
curbufmeths.del_var('lua')
eq(0, funcs.exists('b:lua'))
end)
end)

View File

@ -3,6 +3,9 @@ local helpers = require('test.functional.helpers')
local clear, nvim, tabpage, curtab, eq, ok =
helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq,
helpers.ok
local wait = helpers.wait
local curtabmeths = helpers.curtabmeths
local funcs = helpers.funcs
describe('tabpage_* functions', function()
before_each(clear)
@ -21,11 +24,14 @@ describe('tabpage_* functions', function()
end)
end)
describe('{get,set}_var', function()
describe('{get,set,del}_var', function()
it('works', function()
curtab('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, curtab('get_var', 'lua'))
eq({1, 2, {['3'] = 1}}, nvim('eval', 't:lua'))
eq(1, funcs.exists('t:lua'))
curtabmeths.del_var('lua')
eq(0, funcs.exists('t:lua'))
end)
end)

View File

@ -4,6 +4,8 @@ local Screen = require('test.functional.ui.screen')
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
local os_name = helpers.os_name
local meths = helpers.meths
local funcs = helpers.funcs
describe('vim_* functions', function()
before_each(clear)
@ -70,11 +72,14 @@ describe('vim_* functions', function()
end)
end)
describe('{get,set}_var', function()
describe('{get,set,del}_var', function()
it('works', function()
nvim('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua'))
eq({1, 2, {['3'] = 1}}, nvim('eval', 'g:lua'))
eq(1, funcs.exists('g:lua'))
meths.del_var('lua')
eq(0, funcs.exists('g:lua'))
end)
it('set_var returns the old value', function()
@ -84,6 +89,14 @@ describe('vim_* functions', function()
eq(val1, nvim('set_var', 'lua', val2))
end)
it('del_var returns the old value', function()
local val1 = {1, 2, {['3'] = 1}}
local val2 = {4, 7}
eq(NIL, meths.set_var('lua', val1))
eq(val1, meths.set_var('lua', val2))
eq(val2, meths.del_var('lua'))
end)
it('truncates values with NULs in them', function()
nvim('set_var', 'xxx', 'ab\0cd')
eq('ab', nvim('get_var', 'xxx'))

View File

@ -5,6 +5,8 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq,
helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval
local wait = helpers.wait
local curwinmeths = helpers.curwinmeths
local funcs = helpers.funcs
-- check if str is visible at the beginning of some line
local function is_visible(str)
@ -126,11 +128,14 @@ describe('window_* functions', function()
end)
end)
describe('{get,set}_var', function()
describe('{get,set,del}_var', function()
it('works', function()
curwin('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, curwin('get_var', 'lua'))
eq({1, 2, {['3'] = 1}}, nvim('eval', 'w:lua'))
eq(1, funcs.exists('w:lua'))
curwinmeths.del_var('lua')
eq(0, funcs.exists('w:lua'))
end)
end)

View File

@ -0,0 +1,799 @@
local helpers = require('test.functional.helpers')
local clear = helpers.clear
local funcs = helpers.funcs
local meths = helpers.meths
local eq = helpers.eq
local eval = helpers.eval
local execute = helpers.execute
local exc_exec = helpers.exc_exec
local redir_exec = helpers.redir_exec
describe('json_decode() function', function()
local restart = function(cmd)
clear(cmd)
execute('language C')
execute([[
function Eq(exp, act)
let act = a:act
let exp = a:exp
if type(exp) != type(act)
return 0
endif
if type(exp) == type({})
if sort(keys(exp)) !=# sort(keys(act))
return 0
endif
if sort(keys(exp)) ==# ['_TYPE', '_VAL']
let exp_typ = v:msgpack_types[exp._TYPE]
let act_typ = act._TYPE
if exp_typ isnot act_typ
return 0
endif
return Eq(exp._VAL, act._VAL)
else
return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
endif
else
if type(exp) == type([])
if len(exp) != len(act)
return 0
endif
return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
endif
return exp ==# act
endif
return 1
endfunction
]])
execute([[
function EvalEq(exp, act_expr)
let act = eval(a:act_expr)
if Eq(a:exp, act)
return 1
else
return string(act)
endif
endfunction
]])
end
before_each(restart)
local speq = function(expected, actual_expr)
eq(1, funcs.EvalEq(expected, actual_expr))
end
it('accepts readfile()-style list', function()
eq({Test=1}, funcs.json_decode({
'{',
'\t"Test": 1',
'}',
}))
end)
it('accepts strings with newlines', function()
eq({Test=1}, funcs.json_decode([[
{
"Test": 1
}
]]))
end)
it('parses null, true, false', function()
eq(NIL, funcs.json_decode('null'))
eq(true, funcs.json_decode('true'))
eq(false, funcs.json_decode('false'))
end)
it('fails to parse incomplete null, true, false', function()
eq('Vim(call):E474: Expected null: n',
exc_exec('call json_decode("n")'))
eq('Vim(call):E474: Expected null: nu',
exc_exec('call json_decode("nu")'))
eq('Vim(call):E474: Expected null: nul',
exc_exec('call json_decode("nul")'))
eq('Vim(call):E474: Expected null: nul\n\t',
exc_exec('call json_decode("nul\\n\\t")'))
eq('Vim(call):E474: Expected true: t',
exc_exec('call json_decode("t")'))
eq('Vim(call):E474: Expected true: tr',
exc_exec('call json_decode("tr")'))
eq('Vim(call):E474: Expected true: tru',
exc_exec('call json_decode("tru")'))
eq('Vim(call):E474: Expected true: tru\t\n',
exc_exec('call json_decode("tru\\t\\n")'))
eq('Vim(call):E474: Expected false: f',
exc_exec('call json_decode("f")'))
eq('Vim(call):E474: Expected false: fa',
exc_exec('call json_decode("fa")'))
eq('Vim(call):E474: Expected false: fal',
exc_exec('call json_decode("fal")'))
eq('Vim(call):E474: Expected false: fal <',
exc_exec('call json_decode(" fal <")'))
eq('Vim(call):E474: Expected false: fals',
exc_exec('call json_decode("fals")'))
end)
it('parses integer numbers', function()
eq(100000, funcs.json_decode('100000'))
eq(-100000, funcs.json_decode('-100000'))
eq(100000, funcs.json_decode(' 100000 '))
eq(-100000, funcs.json_decode(' -100000 '))
eq(0, funcs.json_decode('0'))
eq(0, funcs.json_decode('-0'))
end)
it('fails to parse +numbers and .number', function()
eq('Vim(call):E474: Unidentified byte: +1000',
exc_exec('call json_decode("+1000")'))
eq('Vim(call):E474: Unidentified byte: .1000',
exc_exec('call json_decode(".1000")'))
end)
it('fails to parse numbers with leading zeroes', function()
eq('Vim(call):E474: Leading zeroes are not allowed: 00.1',
exc_exec('call json_decode("00.1")'))
eq('Vim(call):E474: Leading zeroes are not allowed: 01',
exc_exec('call json_decode("01")'))
eq('Vim(call):E474: Leading zeroes are not allowed: -01',
exc_exec('call json_decode("-01")'))
eq('Vim(call):E474: Leading zeroes are not allowed: -001.0',
exc_exec('call json_decode("-001.0")'))
end)
it('fails to parse incomplete numbers', function()
eq('Vim(call):E474: Missing number after minus sign: -.1',
exc_exec('call json_decode("-.1")'))
eq('Vim(call):E474: Missing number after minus sign: -',
exc_exec('call json_decode("-")'))
eq('Vim(call):E474: Missing number after decimal dot: -1.',
exc_exec('call json_decode("-1.")'))
eq('Vim(call):E474: Missing number after decimal dot: 0.',
exc_exec('call json_decode("0.")'))
eq('Vim(call):E474: Missing exponent: 0.0e',
exc_exec('call json_decode("0.0e")'))
eq('Vim(call):E474: Missing exponent: 0.0e+',
exc_exec('call json_decode("0.0e+")'))
eq('Vim(call):E474: Missing exponent: 0.0e-',
exc_exec('call json_decode("0.0e-")'))
eq('Vim(call):E474: Missing exponent: 0.0e-',
exc_exec('call json_decode("0.0e-")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e5',
exc_exec('call json_decode("1.e5")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e+5',
exc_exec('call json_decode("1.e+5")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e+',
exc_exec('call json_decode("1.e+")'))
end)
it('parses floating-point numbers', function()
eq('100000.0', eval('string(json_decode("100000.0"))'))
eq(100000.5, funcs.json_decode('100000.5'))
eq(-100000.5, funcs.json_decode('-100000.5'))
eq(-100000.5e50, funcs.json_decode('-100000.5e50'))
eq(100000.5e50, funcs.json_decode('100000.5e50'))
eq(100000.5e50, funcs.json_decode('100000.5e+50'))
eq(-100000.5e-50, funcs.json_decode('-100000.5e-50'))
eq(100000.5e-50, funcs.json_decode('100000.5e-50'))
eq(100000e-50, funcs.json_decode('100000e-50'))
eq(0.5, funcs.json_decode('0.5'))
eq(0.005, funcs.json_decode('0.005'))
eq(0.005, funcs.json_decode('0.00500'))
eq(0.5, funcs.json_decode('0.00500e+002'))
eq(0.00005, funcs.json_decode('0.00500e-002'))
eq(-0.0, funcs.json_decode('-0.0'))
eq(-0.0, funcs.json_decode('-0.0e0'))
eq(-0.0, funcs.json_decode('-0.0e+0'))
eq(-0.0, funcs.json_decode('-0.0e-0'))
eq(-0.0, funcs.json_decode('-0e-0'))
eq(-0.0, funcs.json_decode('-0e-2'))
eq(-0.0, funcs.json_decode('-0e+2'))
eq(0.0, funcs.json_decode('0.0'))
eq(0.0, funcs.json_decode('0.0e0'))
eq(0.0, funcs.json_decode('0.0e+0'))
eq(0.0, funcs.json_decode('0.0e-0'))
eq(0.0, funcs.json_decode('0e-0'))
eq(0.0, funcs.json_decode('0e-2'))
eq(0.0, funcs.json_decode('0e+2'))
end)
it('fails to parse numbers with spaces inside', function()
eq('Vim(call):E474: Missing number after minus sign: - 1000',
exc_exec('call json_decode("- 1000")'))
eq('Vim(call):E474: Missing number after decimal dot: 0. ',
exc_exec('call json_decode("0. ")'))
eq('Vim(call):E474: Missing number after decimal dot: 0. 0',
exc_exec('call json_decode("0. 0")'))
eq('Vim(call):E474: Missing exponent: 0.0e 1',
exc_exec('call json_decode("0.0e 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e+ 1',
exc_exec('call json_decode("0.0e+ 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e- 1',
exc_exec('call json_decode("0.0e- 1")'))
end)
it('fails to parse "," and ":"', function()
eq('Vim(call):E474: Comma not inside container: , ',
exc_exec('call json_decode(" , ")'))
eq('Vim(call):E474: Colon not inside container: : ',
exc_exec('call json_decode(" : ")'))
end)
it('parses empty containers', function()
eq({}, funcs.json_decode('[]'))
eq('[]', eval('string(json_decode("[]"))'))
end)
it('fails to parse "[" and "{"', function()
eq('Vim(call):E474: Unexpected end of input: {',
exc_exec('call json_decode("{")'))
eq('Vim(call):E474: Unexpected end of input: [',
exc_exec('call json_decode("[")'))
end)
it('fails to parse "}" and "]"', function()
eq('Vim(call):E474: No container to close: ]',
exc_exec('call json_decode("]")'))
eq('Vim(call):E474: No container to close: }',
exc_exec('call json_decode("}")'))
end)
it('fails to parse containers which are closed by different brackets',
function()
eq('Vim(call):E474: Closing dictionary with square bracket: ]',
exc_exec('call json_decode("{]")'))
eq('Vim(call):E474: Closing list with curly bracket: }',
exc_exec('call json_decode("[}")'))
end)
it('fails to parse concat inside container', function()
eq('Vim(call):E474: Expected comma before list item: []]',
exc_exec('call json_decode("[[][]]")'))
eq('Vim(call):E474: Expected comma before list item: {}]',
exc_exec('call json_decode("[{}{}]")'))
eq('Vim(call):E474: Expected comma before list item: ]',
exc_exec('call json_decode("[1 2]")'))
eq('Vim(call):E474: Expected comma before dictionary key: ": 4}',
exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")'))
eq('Vim(call):E474: Expected colon before dictionary value: , "3" 4}',
exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")'))
end)
it('fails to parse containers with leading comma or colon', function()
eq('Vim(call):E474: Leading comma: ,}',
exc_exec('call json_decode("{,}")'))
eq('Vim(call):E474: Leading comma: ,]',
exc_exec('call json_decode("[,]")'))
eq('Vim(call):E474: Using colon not in dictionary: :]',
exc_exec('call json_decode("[:]")'))
eq('Vim(call):E474: Unexpected colon: :}',
exc_exec('call json_decode("{:}")'))
end)
it('fails to parse containers with trailing comma', function()
eq('Vim(call):E474: Trailing comma: ]',
exc_exec('call json_decode("[1,]")'))
eq('Vim(call):E474: Trailing comma: }',
exc_exec('call json_decode("{\\"1\\": 2,}")'))
end)
it('fails to parse dictionaries with missing value', function()
eq('Vim(call):E474: Expected value after colon: }',
exc_exec('call json_decode("{\\"1\\":}")'))
eq('Vim(call):E474: Expected value: }',
exc_exec('call json_decode("{\\"1\\"}")'))
end)
it('fails to parse containers with two commas or colons', function()
eq('Vim(call):E474: Duplicate comma: , "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")'))
eq('Vim(call):E474: Duplicate comma: , "2", 2]',
exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")'))
eq('Vim(call):E474: Duplicate colon: : 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")'))
eq('Vim(call):E474: Comma after colon: , 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")'))
eq('Vim(call):E474: Unexpected colon: : "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")'))
eq('Vim(call):E474: Unexpected colon: :, "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")'))
end)
it('fails to parse concat of two values', function()
eq('Vim(call):E474: Trailing characters: []',
exc_exec('call json_decode("{}[]")'))
end)
it('parses containers', function()
eq({1}, funcs.json_decode('[1]'))
eq({NIL, 1}, funcs.json_decode('[null, 1]'))
eq({['1']=2}, funcs.json_decode('{"1": 2}'))
eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}},
funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}'))
end)
it('fails to parse incomplete strings', function()
eq('Vim(call):E474: Expected string end: \t"',
exc_exec('call json_decode("\\t\\"")'))
eq('Vim(call):E474: Expected string end: \t"abc',
exc_exec('call json_decode("\\t\\"abc")'))
eq('Vim(call):E474: Unfinished escape sequence: \t"abc\\',
exc_exec('call json_decode("\\t\\"abc\\\\")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u',
exc_exec('call json_decode("\\t\\"abc\\\\u")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0',
exc_exec('call json_decode("\\t\\"abc\\\\u0")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00',
exc_exec('call json_decode("\\t\\"abc\\\\u00")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
exc_exec('call json_decode("\\t\\"abc\\\\u000")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ',
exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ',
exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ',
exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ',
exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")'))
eq('Vim(call):E474: Expected string end: \t"abc\\u0000',
exc_exec('call json_decode("\\t\\"abc\\\\u0000")'))
end)
it('fails to parse unknown escape sequnces', function()
eq('Vim(call):E474: Unknown escape sequence: \\a"',
exc_exec('call json_decode("\\t\\"\\\\a\\"")'))
end)
it('parses strings properly', function()
eq('\n', funcs.json_decode('"\\n"'))
eq('', funcs.json_decode('""'))
eq('\\/"\t\b\n\r\f', funcs.json_decode([["\\\/\"\t\b\n\r\f"]]))
eq('/a', funcs.json_decode([["\/a"]]))
-- Unicode characters: 2-byte, 3-byte, 4-byte
eq({
'«',
'',
'\240\144\128\128',
}, funcs.json_decode({
'[',
'"«",',
'"ફ",',
'"\240\144\128\128"',
']',
}))
end)
it('fails on strings with invalid bytes', function()
eq('Vim(call):E474: Only UTF-8 strings allowed: \255"',
exc_exec('call json_decode("\\t\\"\\xFF\\"")'))
eq('Vim(call):E474: ASCII control characters cannot be present inside string: ',
exc_exec('call json_decode(["\\"\\n\\""])'))
-- 0xC2 starts 2-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \194"',
exc_exec('call json_decode("\\t\\"\\xC2\\"")'))
-- 0xE0 0xAA starts 3-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \224"',
exc_exec('call json_decode("\\t\\"\\xE0\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \224\170"',
exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")'))
-- 0xF0 0x90 0x80 starts 4-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \240"',
exc_exec('call json_decode("\\t\\"\\xF0\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")'))
-- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \249"',
exc_exec('call json_decode("\\t\\"\\xF9\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")'))
-- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \252"',
exc_exec('call json_decode("\\t\\"\\xFC\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")'))
-- Specification does not allow unquoted characters above 0x10FFFF
eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")'))
-- '"\249\128\128\128\128"',
-- '"\252\144\128\128\128\128"',
end)
it('parses surrogate pairs properly', function()
eq('\240\144\128\128', funcs.json_decode('"\\uD800\\uDC00"'))
eq('\237\160\128a\237\176\128', funcs.json_decode('"\\uD800a\\uDC00"'))
eq('\237\160\128\t\237\176\128', funcs.json_decode('"\\uD800\\t\\uDC00"'))
eq('\237\160\128', funcs.json_decode('"\\uD800"'))
eq('\237\160\128a', funcs.json_decode('"\\uD800a"'))
eq('\237\160\128\t', funcs.json_decode('"\\uD800\\t"'))
eq('\237\176\128', funcs.json_decode('"\\uDC00"'))
eq('\237\176\128a', funcs.json_decode('"\\uDC00a"'))
eq('\237\176\128\t', funcs.json_decode('"\\uDC00\\t"'))
eq('\237\176\128', funcs.json_decode('"\\uDC00"'))
eq('a\237\176\128', funcs.json_decode('"a\\uDC00"'))
eq('\t\237\176\128', funcs.json_decode('"\\t\\uDC00"'))
eq('\237\160\128¬', funcs.json_decode('"\\uD800\\u00AC"'))
eq('\237\160\128\237\160\128', funcs.json_decode('"\\uD800\\uD800"'))
end)
local sp_decode_eq = function(expected, json)
meths.set_var('__json', json)
speq(expected, 'json_decode(g:__json)')
execute('unlet! g:__json')
end
it('parses strings with NUL properly', function()
sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"')
sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"')
sp_decode_eq({_TYPE='string', _VAL={'\n«\n'}}, '"\\u0000\\u00AB\\u0000"')
end)
it('parses dictionaries with duplicate keys to special maps', function()
sp_decode_eq({_TYPE='map', _VAL={{'a', 1}, {'a', 2}}},
'{"a": 1, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'a', 2}}},
'{"b": 3, "a": 1, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}}},
'{"b": 3, "a": 1, "c": 4, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}},
'{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}')
sp_decode_eq({{_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}},
'[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]')
sp_decode_eq({{d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({1, {d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
end)
it('parses dictionaries with empty keys to special maps', function()
sp_decode_eq({_TYPE='map', _VAL={{'', 4}}},
'{"": 4}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}},
'{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}')
sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}},
'{"": 3, "a": 1, "c": 4, "d": 2, "": 4}')
sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}},
'[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]')
end)
it('parses dictionaries with keys with NUL bytes to special maps', function()
sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}},
'{"a\\u0000\\nb": 4}')
sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b', ''}}, 4}}},
'{"a\\u0000\\nb\\n": 4}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {{_TYPE='string', _VAL={'\n'}}, 4}}},
'{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}')
end)
it('converts strings to latin1 when &encoding is latin1', function()
restart('set encoding=latin1')
eq('\171', funcs.json_decode('"\\u00AB"'))
sp_decode_eq({_TYPE='string', _VAL={'\n\171\n'}}, '"\\u0000\\u00AB\\u0000"')
end)
it('fails to convert string to latin1 if it is impossible', function()
restart('set encoding=latin1')
eq('Vim(call):E474: Failed to convert string "ꯍ" from UTF-8',
exc_exec('call json_decode(\'"\\uABCD"\')'))
end)
it('parses U+00C3 correctly', function()
eq('\195\131', funcs.json_decode('"\195\131"'))
end)
it('fails to parse empty string', function()
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode([])'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode([""])'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(" ")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("\\t")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("\\n")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")'))
end)
it('accepts all spaces in every position where space may be put', function()
local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s)
eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str))
end)
it('always treats input as UTF-8', function()
-- When &encoding is latin1 string "«" is U+00C2 U+00AB U+00C2: «Â. So if
-- '"«"' was parsed as latin1 json_decode would return three characters, and
-- only one U+00AB when this string is parsed as latin1.
restart('set encoding=latin1')
eq(('%c'):format(0xAB), funcs.json_decode('"«"'))
end)
it('does not overflow when writing error message about decoding ["", ""]',
function()
eq('\nE474: Attempt to decode a blank string'
.. '\nE474: Failed to parse \n',
redir_exec('call json_decode(["", ""])'))
end)
end)
describe('json_encode() function', function()
before_each(function()
clear()
execute('language C')
end)
it('dumps strings', function()
eq('"Test"', funcs.json_encode('Test'))
eq('""', funcs.json_encode(''))
eq('"\\t"', funcs.json_encode('\t'))
eq('"\\n"', funcs.json_encode('\n'))
eq('"\\u001B"', funcs.json_encode('\27'))
eq('"þÿþ"', funcs.json_encode('þÿþ'))
end)
it('dumps numbers', function()
eq('0', funcs.json_encode(0))
eq('10', funcs.json_encode(10))
eq('-10', funcs.json_encode(-10))
end)
it('dumps floats', function()
eq('0.0', eval('json_encode(0.0)'))
eq('10.5', funcs.json_encode(10.5))
eq('-10.5', funcs.json_encode(-10.5))
eq('-1.0e-5', funcs.json_encode(-1e-5))
eq('1.0e50', eval('json_encode(1.0e50)'))
end)
it('fails to dump NaN and infinite values', function()
eq('Vim(call):E474: Unable to represent NaN value in JSON',
exc_exec('call json_encode(str2float("nan"))'))
eq('Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(str2float("inf"))'))
eq('Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(-str2float("inf"))'))
end)
it('dumps lists', function()
eq('[]', funcs.json_encode({}))
eq('[[]]', funcs.json_encode({{}}))
eq('[[], []]', funcs.json_encode({{}, {}}))
end)
it('dumps dictionaries', function()
eq('{}', eval('json_encode({})'))
eq('{"d": []}', funcs.json_encode({d={}}))
eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}}))
end)
it('cannot dump generic mapping with generic mapping keys and values',
function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todumpv1, todumpv2])')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end)
it('cannot dump generic mapping with ext key', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end)
it('cannot dump generic mapping with array key', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end)
it('cannot dump generic mapping with UINT64_MAX key', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end)
it('cannot dump generic mapping with floating-point key', function()
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end)
it('can dump generic mapping with STR special key and NUL', function()
execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('{"\\u0000": 1}', eval('json_encode(todump)'))
end)
it('can dump generic mapping with BIN special key and NUL', function()
execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('{"\\u0000": 1}', eval('json_encode(todump)'))
end)
it('can dump STR special mapping with NUL and NL', function()
execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}')
eq('"\\u0000\\n"', eval('json_encode(todump)'))
end)
it('can dump BIN special mapping with NUL and NL', function()
execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}')
eq('"\\u0000\\n"', eval('json_encode(todump)'))
end)
it('cannot dump special ext mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)'))
end)
it('can dump special array mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
eq('[5, [""]]', eval('json_encode(todump)'))
end)
it('can dump special UINT64_MAX mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
eq('18446744073709551615', eval('json_encode(todump)'))
end)
it('can dump special INT64_MIN mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [-1, 2, 0, 0]')
eq('-9223372036854775808', eval('json_encode(todump)'))
end)
it('can dump special BOOLEAN true mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
eq('true', eval('json_encode(todump)'))
end)
it('can dump special BOOLEAN false mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
eq('false', eval('json_encode(todump)'))
end)
it('can dump special NIL mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
eq('null', eval('json_encode(todump)'))
end)
it('fails to dump a function reference', function()
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
exc_exec('call json_encode(function("tr"))'))
end)
it('fails to dump a function reference in a list', function()
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
exc_exec('call json_encode([function("tr")])'))
end)
it('fails to dump a recursive list', function()
execute('let todump = [[[]]]')
execute('call add(todump[0][0], todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
end)
it('fails to dump a recursive dict', function()
execute('let todump = {"d": {"d": {}}}')
execute('call extend(todump.d.d, {"d": todump})')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])'))
end)
it('can dump dict with two same dicts inside', function()
execute('let inter = {}')
execute('let todump = {"a": inter, "b": inter}')
eq('{"a": {}, "b": {}}', eval('json_encode(todump)'))
end)
it('can dump list with two same lists inside', function()
execute('let inter = []')
execute('let todump = [inter, inter]')
eq('[[], []]', eval('json_encode(todump)'))
end)
it('fails to dump a recursive list in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
end)
it('fails to dump a recursive (val) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, ["", todump])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])'))
end)
it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}')
execute('call add(todump._VAL[0][1], todump._VAL)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
end)
it('fails to dump a recursive (val) special list in a special dict',
function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, ["", todump._VAL])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
end)
it('fails when called with no arguments', function()
eq('Vim(call):E119: Not enough arguments for function: json_encode',
exc_exec('call json_encode()'))
end)
it('fails when called with two arguments', function()
eq('Vim(call):E118: Too many arguments for function: json_encode',
exc_exec('call json_encode(["", ""], 1)'))
end)
it('converts strings from latin1 when &encoding is latin1', function()
clear('set encoding=latin1')
eq('"\\u00AB"', funcs.json_encode('\171'))
eq('"\\u0000\\u00AB\\u0000"', eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\\n\171\\n"]})'))
end)
it('ignores improper values in &isprint', function()
meths.set_option('isprint', '1')
eq(1, eval('"\1" =~# "\\\\p"'))
eq('"\\u0001"', funcs.json_encode('\1'))
end)
it('fails when using surrogate character in a UTF-8 string', function()
eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128',
exc_exec('call json_encode("\237\160\128")'))
eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191',
exc_exec('call json_encode("\237\175\191")'))
end)
it('dumps control characters as expected', function()
eq([["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]],
eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})'))
end)
it('can dump NULL string', function()
eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)'))
end)
it('can dump NULL list', function()
eq('[]', eval('json_encode(v:_null_list)'))
end)
it('can dump NULL dictionary', function()
eq('{}', eval('json_encode(v:_null_dict)'))
end)
end)

View File

@ -1,5 +1,6 @@
local helpers = require('test.functional.helpers')
local clear = helpers.clear
local funcs = helpers.funcs
local eval, eq = helpers.eval, helpers.eq
local execute = helpers.execute
local nvim = helpers.nvim
@ -382,30 +383,32 @@ describe('msgpack*() functions', function()
eq({"\n"}, eval('parsed'))
eq(1, eval('dumped ==# dumped2'))
end)
it('dump and restore special mapping with floating-point value', function()
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
eq({0.125}, eval('msgpackparse(msgpackdump([todump]))'))
end)
end)
describe('msgpackparse() function', function()
before_each(clear)
it('restores nil as special dict', function()
it('restores nil as v:null', function()
execute('let dumped = ["\\xC0"]')
execute('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL=0}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.nil'))
eq('[v:null]', eval('string(parsed)'))
end)
it('restores boolean false as zero', function()
it('restores boolean false as v:false', function()
execute('let dumped = ["\\xC2"]')
execute('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL=0}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean'))
eq({false}, eval('parsed'))
end)
it('restores boolean true as one', function()
it('restores boolean true as v:true', function()
execute('let dumped = ["\\xC3"]')
execute('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL=1}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean'))
eq({true}, eval('parsed'))
end)
it('restores FIXSTR as special dict', function()
@ -512,33 +515,55 @@ describe('msgpackdump() function', function()
eq({'\129\128\128'}, eval('msgpackdump([todump])'))
end)
it('can dump generic mapping with ext', function()
it('can dump v:true', function()
eq({'\195'}, funcs.msgpackdump({true}))
end)
it('can dump v:false', function()
eq({'\194'}, funcs.msgpackdump({false}))
end)
it('can v:null', function()
execute('let todump = v:null')
end)
it('can dump special bool mapping (true)', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
eq({'\195'}, eval('msgpackdump([todump])'))
end)
it('can dump special bool mapping (false)', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
eq({'\194'}, eval('msgpackdump([todump])'))
end)
it('can dump special nil mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
eq({'\192'}, eval('msgpackdump([todump])'))
end)
it('can dump special ext mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
eq({'\212\005', ''}, eval('msgpackdump([todump])'))
end)
it('can dump generic mapping with array', function()
it('can dump special array mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])'))
end)
it('can dump generic mapping with UINT64_MAX', function()
it('can dump special UINT64_MAX mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])'))
end)
it('can dump generic mapping with INT64_MIN', function()
it('can dump special INT64_MIN mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [-1, 2, 0, 0]')
eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])'))
end)
it('dump and restore generic mapping with floating-point value', function()
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
eq({0.125}, eval('msgpackparse(msgpackdump([todump]))'))
end)
it('fails to dump a function reference', function()
execute('let Todump = function("tr")')
eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
@ -654,4 +679,25 @@ describe('msgpackdump() function', function()
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(0.0)'))
end)
it('fails to dump special value', function()
for _, val in ipairs({'v:true', 'v:false', 'v:null'}) do
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(' .. val .. ')'))
end
end)
it('can dump NULL string', function()
eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])'))
eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])'))
eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])'))
end)
it('can dump NULL list', function()
eq({'\144'}, eval('msgpackdump([v:_null_list])'))
end)
it('can dump NULL dictionary', function()
eq({'\128'}, eval('msgpackdump([v:_null_dict])'))
end)
end)

View File

@ -0,0 +1,171 @@
local helpers = require('test.functional.helpers')
local exc_exec = helpers.exc_exec
local execute = helpers.execute
local meths = helpers.meths
local funcs = helpers.funcs
local meths = helpers.meths
local clear = helpers.clear
local eval = helpers.eval
local eq = helpers.eq
describe('Special values', function()
before_each(clear)
it('do not cause error when freed', function()
execute([[
function Test()
try
return v:true
finally
return 'something else'
endtry
endfunction
]])
eq(0, exc_exec('call Test()'))
end)
it('work with empty()', function()
eq(0, funcs.empty(true))
eq(1, funcs.empty(false))
eq(1, funcs.empty(NIL))
end)
it('can be stringified and evaled back', function()
eq(true, funcs.eval(funcs.string(true)))
eq(false, funcs.eval(funcs.string(false)))
eq(NIL, funcs.eval(funcs.string(NIL)))
end)
it('work with is/isnot properly', function()
eq(1, eval('v:null is v:null'))
eq(0, eval('v:null is v:true'))
eq(0, eval('v:null is v:false'))
eq(1, eval('v:true is v:true'))
eq(0, eval('v:true is v:false'))
eq(1, eval('v:false is v:false'))
eq(0, eval('v:null is 0'))
eq(0, eval('v:true is 0'))
eq(0, eval('v:false is 0'))
eq(0, eval('v:null is 1'))
eq(0, eval('v:true is 1'))
eq(0, eval('v:false is 1'))
eq(0, eval('v:null is ""'))
eq(0, eval('v:true is ""'))
eq(0, eval('v:false is ""'))
eq(0, eval('v:null is "null"'))
eq(0, eval('v:true is "true"'))
eq(0, eval('v:false is "false"'))
eq(0, eval('v:null is []'))
eq(0, eval('v:true is []'))
eq(0, eval('v:false is []'))
eq(0, eval('v:null isnot v:null'))
eq(1, eval('v:null isnot v:true'))
eq(1, eval('v:null isnot v:false'))
eq(0, eval('v:true isnot v:true'))
eq(1, eval('v:true isnot v:false'))
eq(0, eval('v:false isnot v:false'))
eq(1, eval('v:null isnot 0'))
eq(1, eval('v:true isnot 0'))
eq(1, eval('v:false isnot 0'))
eq(1, eval('v:null isnot 1'))
eq(1, eval('v:true isnot 1'))
eq(1, eval('v:false isnot 1'))
eq(1, eval('v:null isnot ""'))
eq(1, eval('v:true isnot ""'))
eq(1, eval('v:false isnot ""'))
eq(1, eval('v:null isnot "null"'))
eq(1, eval('v:true isnot "true"'))
eq(1, eval('v:false isnot "false"'))
eq(1, eval('v:null isnot []'))
eq(1, eval('v:true isnot []'))
eq(1, eval('v:false isnot []'))
end)
it('work with +/-/* properly', function()
eq(1, eval('0 + v:true'))
eq(0, eval('0 + v:null'))
eq(0, eval('0 + v:false'))
eq(-1, eval('0 - v:true'))
eq( 0, eval('0 - v:null'))
eq( 0, eval('0 - v:false'))
eq(1, eval('1 * v:true'))
eq(0, eval('1 * v:null'))
eq(0, eval('1 * v:false'))
end)
it('does not work with +=/-=/.=', function()
meths.set_var('true', true)
meths.set_var('false', false)
execute('let null = v:null')
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let null += 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let true -= 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let false -= 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let null -= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let true .= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let false .= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let null .= 1'))
end)
it('work with . (concat) properly', function()
eq("true", eval('"" . v:true'))
eq("null", eval('"" . v:null'))
eq("false", eval('"" . v:false'))
end)
it('work with type()', function()
eq(6, funcs.type(true))
eq(6, funcs.type(false))
eq(7, funcs.type(NIL))
end)
it('work with copy() and deepcopy()', function()
eq(true, funcs.deepcopy(true))
eq(false, funcs.deepcopy(false))
eq(NIL, funcs.deepcopy(NIL))
eq(true, funcs.copy(true))
eq(false, funcs.copy(false))
eq(NIL, funcs.copy(NIL))
end)
it('fails in index', function()
eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:true[0]'))
eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:false[0]'))
eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:null[0]'))
end)
it('is accepted by assert_true and assert_false', function()
funcs.assert_false(false)
funcs.assert_false(true)
funcs.assert_false(NIL)
funcs.assert_true(false)
funcs.assert_true(true)
funcs.assert_true(NIL)
eq({
'Expected False but got v:true',
'Expected False but got v:null',
'Expected True but got v:false',
'Expected True but got v:null',
}, meths.get_vvar('errors'))
end)
end)

View File

@ -28,6 +28,15 @@ describe('string() function', function()
eq('0.0', eval('string(0.0)'))
end)
it('dumps special v: values', function()
eq('v:true', eval('string(v:true)'))
eq('v:false', eval('string(v:false)'))
eq('v:null', eval('string(v:null)'))
eq('v:true', funcs.string(true))
eq('v:false', funcs.string(false))
eq('v:null', funcs.string(NIL))
end)
it('dumps values with at most six digits after the decimal point',
function()
eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020))
@ -78,6 +87,18 @@ describe('string() function', function()
eq('\'\'\'b\'\'\'\'d\'', funcs.string('\'b\'\'d'))
eq('\'a\'\'b\'\'c\'\'d\'', funcs.string('a\'b\'c\'d'))
end)
it('dumps NULL strings', function()
eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)'))
end)
it('dumps NULL lists', function()
eq('[]', eval('string(v:_null_list)'))
end)
it('dumps NULL dictionaries', function()
eq('{}', eval('string(v:_null_dict)'))
end)
end)
describe('used to represent funcrefs', function()

View File

@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')
local meths = helpers.meths
local eq, nvim_eval, nvim_command, exc_exec =
helpers.eq, helpers.eval, helpers.command, helpers.exc_exec
local ok = helpers.ok
@ -409,6 +410,12 @@ describe('In autoload/msgpack.vim', function()
string_eq('nan', '(1.0/0.0-1.0/0.0)')
string_eq('nan', '-(1.0/0.0-1.0/0.0)')
end)
it('works for special v: values like v:true', function()
string_eq('TRUE', 'v:true')
string_eq('FALSE', 'v:false')
string_eq('NIL', 'v:null')
end)
end)
describe('function msgpack#deepcopy', function()
@ -523,6 +530,20 @@ describe('In autoload/msgpack.vim', function()
eq(2.0, nvim_eval('flt2'))
eq('abc', nvim_eval('bin2'))
end)
it('works for special v: values like v:true', function()
meths.set_var('true', true)
meths.set_var('false', false)
meths.set_var('nil', NIL)
nvim_command('let true2 = msgpack#deepcopy(true)')
nvim_command('let false2 = msgpack#deepcopy(false)')
nvim_command('let nil2 = msgpack#deepcopy(nil)')
eq(true, meths.get_var('true'))
eq(false, meths.get_var('false'))
eq(NIL, meths.get_var('nil'))
end)
end)
describe('function msgpack#eval', function()

View File

@ -22,12 +22,17 @@ describe('ShaDa support code', function()
eq('foo', meths.get_var('STRVAR'))
end)
local autotest = function(tname, varname, varval)
local autotest = function(tname, varname, varval, val_is_expr)
it('is able to dump and read back ' .. tname .. ' variable automatically',
function()
set_additional_cmd('set shada+=!')
reset()
meths.set_var(varname, varval)
if val_is_expr then
nvim_command('let g:' .. varname .. ' = ' .. varval)
varval = meths.get_var(varname)
else
meths.set_var(varname, varval)
end
-- Exit during `reset` is not a regular exit: it does not write shada
-- automatically
nvim_command('qall')
@ -41,6 +46,10 @@ describe('ShaDa support code', function()
autotest('float', 'FLTVAR', 42.5)
autotest('dictionary', 'DCTVAR', {a=10})
autotest('list', 'LSTVAR', {{a=10}, {b=10.5}, {c='str'}})
autotest('true', 'TRUEVAR', true)
autotest('false', 'FALSEVAR', false)
autotest('null', 'NULLVAR', 'v:null', true)
autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true)
it('does not read back variables without `!` in &shada', function()
meths.set_var('STRVAR', 'foo')

View File

@ -0,0 +1,142 @@
local helpers = require('test.unit.helpers')
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
local eq = helpers.eq
local neq = helpers.neq
local ffi = helpers.ffi
local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h',
'./src/nvim/globals.h', './src/nvim/memory.h',
'./src/nvim/message.h')
describe('json_decode_string()', function()
local saved_p_enc = nil
before_each(function()
saved_p_enc = decode.p_enc
end)
after_each(function()
decode.emsg_silent = 0
decode.p_enc = saved_p_enc
while decode.delete_first_msg() == 1 do
-- Delete all messages
end
end)
local char = function(c)
return ffi.gc(decode.xmemdup(c, 1), decode.xfree)
end
it('does not overflow when running with `n…`, `t…`, `f…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
-- This will not crash, but if `len` argument will be ignored it will parse
-- `null` as `null` and if not it will parse `null` as `n`.
eq(0, decode.json_decode_string('null', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('null', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('null', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 4, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
it('does not overflow and crash when running with `n`, `t`, `f`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('n'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string(char('t'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string(char('f'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
it('does not overflow when running with `"…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string('"t"', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('""', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
local check_failure = function(s, len, msg)
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
eq(0, decode.json_decode_string(s, len, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
neq(nil, decode.last_msg_hist)
eq(msg, ffi.string(decode.last_msg_hist.msg))
end
it('does not overflow in error messages', function()
check_failure(']test', 1, 'E474: No container to close: ]')
check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
check_failure('{]test', 2,
'E474: Closing dictionary with square bracket: ]')
check_failure('[1,]test', 4, 'E474: Trailing comma: ]')
check_failure('{"1":}test', 6, 'E474: Expected value after colon: }')
check_failure('{"1"}test', 5, 'E474: Expected value: }')
check_failure(',test', 1, 'E474: Comma not inside container: ,')
check_failure('[1,,1]test', 6, 'E474: Duplicate comma: ,1]')
check_failure('{"1":,}test', 7, 'E474: Comma after colon: ,}')
check_failure('{"1",}test', 6, 'E474: Using comma in place of colon: ,}')
check_failure('{,}test', 3, 'E474: Leading comma: ,}')
check_failure('[,]test', 3, 'E474: Leading comma: ,]')
check_failure(':test', 1, 'E474: Colon not inside container: :')
check_failure('[:]test', 3, 'E474: Using colon not in dictionary: :]')
check_failure('{:}test', 3, 'E474: Unexpected colon: :}')
check_failure('{"1"::1}test', 8, 'E474: Duplicate colon: :1}')
check_failure('ntest', 1, 'E474: Expected null: n')
check_failure('ttest', 1, 'E474: Expected true: t')
check_failure('ftest', 1, 'E474: Expected false: f')
check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\')
check_failure('"\\u"test', 4,
'E474: Unfinished unicode escape sequence: "\\u"')
check_failure('"\\uXXXX"est', 8,
'E474: Expected four hex digits after \\u: \\uXXXX"')
check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"')
check_failure(
'"\t"test', 3,
'E474: ASCII control characters cannot be present inside string: \t"')
check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"')
check_failure('"\252\144\128\128\128\128"test', 8, 'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"')
check_failure('"test', 1, 'E474: Expected string end: "')
decode.p_enc = to_cstr('latin1')
check_failure('"\\uABCD"test', 8,
'E474: Failed to convert string "ꯍ" from UTF-8')
decode.p_enc = saved_p_enc
check_failure('-test', 1, 'E474: Missing number after minus sign: -')
check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.')
check_failure('-1.0etest', 5, 'E474: Missing exponent: -1.0e')
check_failure('?test', 1, 'E474: Unidentified byte: ?')
check_failure('1?test', 2, 'E474: Trailing characters: ?')
check_failure('[1test', 2, 'E474: Unexpected end of input: [1')
end)
it('does not overflow with `-`', function()
check_failure('-0', 1, 'E474: Missing number after minus sign: -')
end)
it('does not overflow and crash when running with `"`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('"'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
end)

View File

@ -0,0 +1,100 @@
local helpers = require('test.unit.helpers')
local eval_helpers = require('test.unit.eval.helpers')
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
local eq = helpers.eq
local list = eval_helpers.list
local lst2tbl = eval_helpers.lst2tbl
local type_key = eval_helpers.type_key
local list_type = eval_helpers.list_type
local null_string = eval_helpers.null_string
local encode = cimport('./src/nvim/eval/encode.h')
describe('encode_list_write()', function()
local encode_list_write = function(l, s)
return encode.encode_list_write(l, to_cstr(s), #s)
end
it('writes empty string', function()
local l = list()
eq(0, encode_list_write(l, ''))
eq({[type_key]=list_type}, lst2tbl(l))
end)
it('writes ASCII string literal with printable characters', function()
local l = list()
eq(0, encode_list_write(l, 'abc'))
eq({[type_key]=list_type, 'abc'}, lst2tbl(l))
end)
it('writes string starting with NL', function()
local l = list()
eq(0, encode_list_write(l, '\nabc'))
eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l))
end)
it('writes string starting with NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\nabc'))
eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l))
eq(0, encode_list_write(l, '\nabc'))
eq({[type_key]=list_type, null_string, 'abc', 'abc'}, lst2tbl(l))
end)
it('writes string ending with NL', function()
local l = list()
eq(0, encode_list_write(l, 'abc\n'))
eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l))
end)
it('writes string ending with NL twice', function()
local l = list()
eq(0, encode_list_write(l, 'abc\n'))
eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l))
eq(0, encode_list_write(l, 'abc\n'))
eq({[type_key]=list_type, 'abc', 'abc', null_string}, lst2tbl(l))
end)
it('writes string starting, ending and containing NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\na\nb\n'))
eq({[type_key]=list_type, null_string, 'a', 'b', null_string}, lst2tbl(l))
eq(0, encode_list_write(l, '\na\nb\n'))
eq({[type_key]=list_type, null_string, 'a', 'b', null_string, 'a', 'b', null_string}, lst2tbl(l))
end)
it('writes string starting, ending and containing NUL with NL between twice', function()
local l = list()
eq(0, encode_list_write(l, '\0\n\0\n\0'))
eq({[type_key]=list_type, '\n', '\n', '\n'}, lst2tbl(l))
eq(0, encode_list_write(l, '\0\n\0\n\0'))
eq({[type_key]=list_type, '\n', '\n', '\n\n', '\n', '\n'}, lst2tbl(l))
end)
it('writes string starting, ending and containing NL with NUL between twice', function()
local l = list()
eq(0, encode_list_write(l, '\n\0\n\0\n'))
eq({[type_key]=list_type, null_string, '\n', '\n', null_string}, lst2tbl(l))
eq(0, encode_list_write(l, '\n\0\n\0\n'))
eq({[type_key]=list_type, null_string, '\n', '\n', null_string, '\n', '\n', null_string}, lst2tbl(l))
end)
it('writes string containing a single NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\n'))
eq({[type_key]=list_type, null_string, null_string}, lst2tbl(l))
eq(0, encode_list_write(l, '\n'))
eq({[type_key]=list_type, null_string, null_string, null_string}, lst2tbl(l))
end)
it('writes string containing a few NLs twice', function()
local l = list()
eq(0, encode_list_write(l, '\n\n\n'))
eq({[type_key]=list_type, null_string, null_string, null_string, null_string}, lst2tbl(l))
eq(0, encode_list_write(l, '\n\n\n'))
eq({[type_key]=list_type, null_string, null_string, null_string, null_string, null_string, null_string, null_string}, lst2tbl(l))
end)
end)

View File

@ -0,0 +1,72 @@
local helpers = require('test.unit.helpers')
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
local ffi = helpers.ffi
local eq = helpers.eq
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h')
local null_string = {[true]='NULL string'}
local null_list = {[true]='NULL list'}
local type_key = {[true]='type key'}
local list_type = {[true]='list type'}
local list = function(...)
local ret = ffi.gc(eval.list_alloc(), eval.list_unref)
eq(0, ret.lv_refcount)
ret.lv_refcount = 1
for i = 1, select('#', ...) do
local val = select(i, ...)
local typ = type(val)
if typ == 'string' then
eval.list_append_string(ret, to_cstr(val))
elseif typ == 'table' and val == null_string then
eval.list_append_string(ret, nil)
elseif typ == 'table' and val == null_list then
eval.list_append_list(ret, nil)
elseif typ == 'table' and val[type_key] == list_type then
local itemlist = ffi.gc(list(table.unpack(val)), nil)
eq(1, itemlist.lv_refcount)
itemlist.lv_refcount = 0
eval.list_append_list(ret, itemlist)
else
assert(false, 'Not implemented yet')
end
end
return ret
end
local lst2tbl = function(l)
local ret = {[type_key]=list_type}
if l == nil then
return ret
end
local li = l.lv_first
-- (listitem_T *) NULL is equal to nil, but yet it is not false.
while li ~= nil do
local typ = li.li_tv.v_type
if typ == eval.VAR_STRING then
str = li.li_tv.vval.v_string
if str == nil then
ret[#ret + 1] = null_string
else
ret[#ret + 1] = ffi.string(str)
end
else
assert(false, 'Not implemented yet')
end
li = li.li_next
end
return ret
end
return {
null_string=null_string,
null_list=null_list,
list_type=list_type,
type_key=type_key,
list=list,
lst2tbl=lst2tbl,
}

View File

@ -0,0 +1,43 @@
local helpers = require('test.unit.helpers')
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
local ffi = helpers.ffi
local eq = helpers.eq
local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h')
local eval_expr = function(expr)
return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv)
eval.clear_tv(tv)
eval.xfree(tv)
end)
end
describe('NULL typval_T', function()
it('is produced by $XXX_UNEXISTENT_VAR_XXX', function()
-- Required for various tests which need to check whether typval_T with NULL
-- string works correctly. This test checks that unexistent environment
-- variable produces NULL string, not that some specific environment
-- variable does not exist. Last bit is left for the test writers.
local unexistent_env = 'XXX_UNEXISTENT_VAR_XXX'
while os.getenv(unexistent_env) ~= nil do
unexistent_env = unexistent_env .. '_XXX'
end
local rettv = eval_expr('$' .. unexistent_env)
eq(eval.VAR_STRING, rettv.v_type)
eq(nil, rettv.vval.v_string)
end)
it('is produced by v:_null_list', function()
local rettv = eval_expr('v:_null_list')
eq(eval.VAR_LIST, rettv.v_type)
eq(nil, rettv.vval.v_list)
end)
it('is produced by v:_null_dict', function()
local rettv = eval_expr('v:_null_dict')
eq(eval.VAR_DICT, rettv.v_type)
eq(nil, rettv.vval.v_dict)
end)
end)

View File

@ -28,8 +28,10 @@ local function filter_complex_blocks(body)
local result = {}
for line in body:gmatch("[^\r\n]+") do
if not (string.find(line, "(^)", 1, true) ~= nil or
string.find(line, "_ISwupper", 1, true)) then
if not (string.find(line, "(^)", 1, true) ~= nil
or string.find(line, "_ISwupper", 1, true)
or string.find(line, "msgpack_zone_push_finalizer")
or string.find(line, "msgpack_unpacker_reserve_buffer")) then
result[#result + 1] = line
end
end
@ -103,6 +105,11 @@ local function cimport(...)
-- request a sorted version of the new lines (same relative order as the
-- original preprocessed file) and feed that to the LuaJIT ffi
local new_lines = new_cdefs:to_table()
if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then
for lnum, line in ipairs(new_lines) do
print(lnum, line)
end
end
ffi.cdef(table.concat(new_lines, "\n"))
return libnvim