mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #15211 from seandewar/blob-port
Port VimL's Blob type - vim-patch:8.1.{0735,0736,0738,0741,0742,0755,0756,0757,0765,0793,0797,0798,0802,1022,1023,1671},8.2.{0121,0184,0404,0521,0829,1473,1866,2712}
This commit is contained in:
commit
7d21b95869
@ -14,8 +14,8 @@ Using expressions is introduced in chapter 41 of the user manual |usr_41.txt|.
|
|||||||
1. Variables *variables*
|
1. Variables *variables*
|
||||||
|
|
||||||
1.1 Variable types ~
|
1.1 Variable types ~
|
||||||
*E712*
|
*E712* *E896* *E897* *E899*
|
||||||
There are six types of variables:
|
There are seven types of variables:
|
||||||
|
|
||||||
*Number* *Integer*
|
*Number* *Integer*
|
||||||
Number A 32 or 64 bit signed number. |expr-number|
|
Number A 32 or 64 bit signed number. |expr-number|
|
||||||
@ -34,7 +34,7 @@ Funcref A reference to a function |Funcref|.
|
|||||||
like a Partial.
|
like a Partial.
|
||||||
Example: function("Callback", [arg], myDict)
|
Example: function("Callback", [arg], myDict)
|
||||||
|
|
||||||
List An ordered sequence of items |List|.
|
List An ordered sequence of items, see |List| for details.
|
||||||
Example: [1, 2, ['a', 'b']]
|
Example: [1, 2, ['a', 'b']]
|
||||||
|
|
||||||
Dictionary An associative, unordered array: Each entry has a key and a
|
Dictionary An associative, unordered array: Each entry has a key and a
|
||||||
@ -43,6 +43,11 @@ Dictionary An associative, unordered array: Each entry has a key and a
|
|||||||
{'blue': "#0000ff", 'red': "#ff0000"}
|
{'blue': "#0000ff", 'red': "#ff0000"}
|
||||||
#{blue: "#0000ff", red: "#ff0000"}
|
#{blue: "#0000ff", red: "#ff0000"}
|
||||||
|
|
||||||
|
Blob Binary Large Object. Stores any sequence of bytes. See |Blob|
|
||||||
|
for details.
|
||||||
|
Example: 0zFF00ED015DAF
|
||||||
|
0z is an empty Blob.
|
||||||
|
|
||||||
The Number and String types are converted automatically, depending on how they
|
The Number and String types are converted automatically, depending on how they
|
||||||
are used.
|
are used.
|
||||||
|
|
||||||
@ -97,6 +102,7 @@ Note that " " and "0" are also non-empty strings, thus considered to be TRUE.
|
|||||||
A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE.
|
A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE.
|
||||||
|
|
||||||
*E745* *E728* *E703* *E729* *E730* *E731*
|
*E745* *E728* *E703* *E729* *E730* *E731*
|
||||||
|
*E974* *E975* *E976*
|
||||||
|List|, |Dictionary|, |Funcref|, and |Blob| types are not automatically
|
|List|, |Dictionary|, |Funcref|, and |Blob| types are not automatically
|
||||||
converted.
|
converted.
|
||||||
|
|
||||||
@ -366,8 +372,8 @@ Changing the order of items in a list: >
|
|||||||
|
|
||||||
For loop ~
|
For loop ~
|
||||||
|
|
||||||
The |:for| loop executes commands for each item in a list. A variable is set
|
The |:for| loop executes commands for each item in a |List| or |Blob|.
|
||||||
to each item in the list in sequence. Example: >
|
A variable is set to each item in the sequence. Example with a List: >
|
||||||
:for item in mylist
|
:for item in mylist
|
||||||
: call Doit(item)
|
: call Doit(item)
|
||||||
:endfor
|
:endfor
|
||||||
@ -400,6 +406,8 @@ It is also possible to put remaining items in a List variable: >
|
|||||||
: endif
|
: endif
|
||||||
:endfor
|
:endfor
|
||||||
|
|
||||||
|
For a Blob one byte at a time is used.
|
||||||
|
|
||||||
|
|
||||||
List functions ~
|
List functions ~
|
||||||
*E714*
|
*E714*
|
||||||
@ -594,7 +602,137 @@ Functions that can be used with a Dictionary: >
|
|||||||
:call map(dict, '">> " . v:val') " prepend ">> " to each item
|
:call map(dict, '">> " . v:val') " prepend ">> " to each item
|
||||||
|
|
||||||
|
|
||||||
1.5 More about variables ~
|
1.5 Blobs ~
|
||||||
|
*blob* *Blob* *Blobs* *E978*
|
||||||
|
A Blob is a binary object. It can be used to read an image from a file and
|
||||||
|
send it over a channel, for example.
|
||||||
|
|
||||||
|
A Blob mostly behaves like a |List| of numbers, where each number has the
|
||||||
|
value of an 8-bit byte, from 0 to 255.
|
||||||
|
|
||||||
|
|
||||||
|
Blob creation ~
|
||||||
|
|
||||||
|
A Blob can be created with a |blob-literal|: >
|
||||||
|
:let b = 0zFF00ED015DAF
|
||||||
|
Dots can be inserted between bytes (pair of hex characters) for readability,
|
||||||
|
they don't change the value: >
|
||||||
|
:let b = 0zFF00.ED01.5DAF
|
||||||
|
|
||||||
|
A blob can be read from a file with |readfile()| passing the {type} argument
|
||||||
|
set to "B", for example: >
|
||||||
|
:let b = readfile('image.png', 'B')
|
||||||
|
|
||||||
|
|
||||||
|
Blob index ~
|
||||||
|
*blob-index* *E979*
|
||||||
|
A byte in the Blob can be accessed by putting the index in square brackets
|
||||||
|
after the Blob. Indexes are zero-based, thus the first byte has index zero. >
|
||||||
|
:let myblob = 0z00112233
|
||||||
|
:let byte = myblob[0] " get the first byte: 0x00
|
||||||
|
:let byte = myblob[2] " get the third byte: 0x22
|
||||||
|
|
||||||
|
A negative index is counted from the end. Index -1 refers to the last byte in
|
||||||
|
the Blob, -2 to the last but one byte, etc. >
|
||||||
|
:let last = myblob[-1] " get the last byte: 0x33
|
||||||
|
|
||||||
|
To avoid an error for an invalid index use the |get()| function. When an item
|
||||||
|
is not available it returns -1 or the default value you specify: >
|
||||||
|
:echo get(myblob, idx)
|
||||||
|
:echo get(myblob, idx, 999)
|
||||||
|
|
||||||
|
|
||||||
|
Blob iteration ~
|
||||||
|
|
||||||
|
The |:for| loop executes commands for each byte of a Blob. The loop variable is
|
||||||
|
set to each byte in the Blob. Example: >
|
||||||
|
:for byte in 0z112233
|
||||||
|
: call Doit(byte)
|
||||||
|
:endfor
|
||||||
|
This calls Doit() with 0x11, 0x22 and 0x33.
|
||||||
|
|
||||||
|
|
||||||
|
Blob concatenation ~
|
||||||
|
|
||||||
|
Two blobs can be concatenated with the "+" operator: >
|
||||||
|
:let longblob = myblob + 0z4455
|
||||||
|
:let myblob += 0z6677
|
||||||
|
|
||||||
|
To change a blob in-place see |blob-modification| below.
|
||||||
|
|
||||||
|
|
||||||
|
Part of a blob ~
|
||||||
|
|
||||||
|
A part of the Blob can be obtained by specifying the first and last index,
|
||||||
|
separated by a colon in square brackets: >
|
||||||
|
:let myblob = 0z00112233
|
||||||
|
:let shortblob = myblob[1:2] " get 0z1122
|
||||||
|
:let shortblob = myblob[2:-1] " get 0z2233
|
||||||
|
|
||||||
|
Omitting the first index is similar to zero. Omitting the last index is
|
||||||
|
similar to -1. >
|
||||||
|
:let endblob = myblob[2:] " from item 2 to the end: 0z2233
|
||||||
|
:let shortblob = myblob[2:2] " Blob with one byte: 0z22
|
||||||
|
:let otherblob = myblob[:] " make a copy of the Blob
|
||||||
|
|
||||||
|
If the first index is beyond the last byte of the Blob or the second byte is
|
||||||
|
before the first byte, the result is an empty Blob. There is no error
|
||||||
|
message.
|
||||||
|
|
||||||
|
If the second index is equal to or greater than the length of the Blob the
|
||||||
|
length minus one is used: >
|
||||||
|
:echo myblob[2:8] " result: 0z2233
|
||||||
|
|
||||||
|
|
||||||
|
Blob modification ~
|
||||||
|
*blob-modification*
|
||||||
|
To change a specific byte of a blob use |:let| this way: >
|
||||||
|
:let blob[4] = 0x44
|
||||||
|
|
||||||
|
When the index is just one beyond the end of the Blob, it is appended. Any
|
||||||
|
higher index is an error.
|
||||||
|
|
||||||
|
To change a sequence of bytes the [:] notation can be used: >
|
||||||
|
let blob[1:3] = 0z445566
|
||||||
|
The length of the replaced bytes must be exactly the same as the value
|
||||||
|
provided. *E972*
|
||||||
|
|
||||||
|
To change part of a blob you can specify the first and last byte to be
|
||||||
|
modified. The value must at least have the number of bytes in the range: >
|
||||||
|
:let blob[3:5] = [3, 4, 5]
|
||||||
|
|
||||||
|
You can also use the functions |add()|, |remove()| and |insert()|.
|
||||||
|
|
||||||
|
|
||||||
|
Blob identity ~
|
||||||
|
|
||||||
|
Blobs can be compared for equality: >
|
||||||
|
if blob == 0z001122
|
||||||
|
And for equal identity: >
|
||||||
|
if blob is otherblob
|
||||||
|
< *blob-identity* *E977*
|
||||||
|
When variable "aa" is a Blob and you assign it to another variable "bb", both
|
||||||
|
variables refer to the same Blob. Then the "is" operator returns true.
|
||||||
|
|
||||||
|
When making a copy using [:] or |copy()| the values are the same, but the
|
||||||
|
identity is different: >
|
||||||
|
:let blob = 0z112233
|
||||||
|
:let blob2 = blob
|
||||||
|
:echo blob == blob2
|
||||||
|
< 1 >
|
||||||
|
:echo blob is blob2
|
||||||
|
< 1 >
|
||||||
|
:let blob3 = blob[:]
|
||||||
|
:echo blob == blob3
|
||||||
|
< 1 >
|
||||||
|
:echo blob is blob3
|
||||||
|
< 0
|
||||||
|
|
||||||
|
Making a copy of a Blob is done with the |copy()| function. Using [:] also
|
||||||
|
works, as explained above.
|
||||||
|
|
||||||
|
|
||||||
|
1.6 More about variables ~
|
||||||
*more-variables*
|
*more-variables*
|
||||||
If you need to know the type of a variable or expression, use the |type()|
|
If you need to know the type of a variable or expression, use the |type()|
|
||||||
function.
|
function.
|
||||||
@ -645,8 +783,9 @@ Expression syntax summary, from least to most significant:
|
|||||||
etc. As above, append ? for ignoring case, # for
|
etc. As above, append ? for ignoring case, # for
|
||||||
matching case
|
matching case
|
||||||
|
|
||||||
expr5 is expr5 same |List| instance
|
expr5 is expr5 same |List|, |Dictionary| or |Blob| instance
|
||||||
expr5 isnot expr5 different |List| instance
|
expr5 isnot expr5 different |List|, |Dictionary| or |Blob|
|
||||||
|
instance
|
||||||
|
|
||||||
|expr5| expr6
|
|expr5| expr6
|
||||||
expr6 + expr6 ... number addition, list or blob concatenation
|
expr6 + expr6 ... number addition, list or blob concatenation
|
||||||
@ -817,12 +956,12 @@ Dictionary and arguments, use |get()| to get the function name: >
|
|||||||
if get(Part1, 'name') == get(Part2, 'name')
|
if get(Part1, 'name') == get(Part2, 'name')
|
||||||
" Part1 and Part2 refer to the same function
|
" Part1 and Part2 refer to the same function
|
||||||
|
|
||||||
When using "is" or "isnot" with a |List| or a |Dictionary| this checks if the
|
Using "is" or "isnot" with a |List|, |Dictionary| or |Blob| checks whether
|
||||||
expressions are referring to the same |List| or |Dictionary| instance. A copy
|
the expressions are referring to the same |List|, |Dictionary| or |Blob|
|
||||||
of a |List| is different from the original |List|. When using "is" without
|
instance. A copy of a |List| is different from the original |List|. When
|
||||||
a |List| or a |Dictionary| it is equivalent to using "equal", using "isnot"
|
using "is" without a |List|, |Dictionary| or |Blob|, it is equivalent to
|
||||||
equivalent to using "not equal". Except that a different type means the
|
using "equal", using "isnot" is equivalent to using "not equal". Except that
|
||||||
values are different: >
|
a different type means the values are different: >
|
||||||
echo 4 == '4'
|
echo 4 == '4'
|
||||||
1
|
1
|
||||||
echo 4 is '4'
|
echo 4 is '4'
|
||||||
@ -1012,6 +1151,12 @@ just above. Also see |sublist| below. Examples: >
|
|||||||
:let l = mylist[4:4] " List with one item
|
:let l = mylist[4:4] " List with one item
|
||||||
:let l = mylist[:] " shallow copy of a List
|
:let l = mylist[:] " shallow copy of a List
|
||||||
|
|
||||||
|
If expr8 is a |Blob| this results in a new |Blob| with the bytes in the
|
||||||
|
indexes expr1a and expr1b, inclusive. Examples: >
|
||||||
|
:let b = 0zDEADBEEF
|
||||||
|
:let bs = b[1:2] " 0zADBE
|
||||||
|
:let bs = b[] " copy of 0zDEADBEEF
|
||||||
|
|
||||||
Using expr8[expr1] or expr8[expr1a : expr1b] on a |Funcref| results in an
|
Using expr8[expr1] or expr8[expr1a : expr1b] on a |Funcref| results in an
|
||||||
error.
|
error.
|
||||||
|
|
||||||
@ -1180,6 +1325,14 @@ encodings. Use "\u00ff" to store character 255 correctly as UTF-8.
|
|||||||
Note that "\000" and "\x00" force the end of the string.
|
Note that "\000" and "\x00" force the end of the string.
|
||||||
|
|
||||||
|
|
||||||
|
blob-literal *blob-literal* *E973*
|
||||||
|
------------
|
||||||
|
|
||||||
|
Hexadecimal starting with 0z or 0Z, with an arbitrary number of bytes.
|
||||||
|
The sequence must be an even number of hex characters. Example: >
|
||||||
|
:let b = 0zFF00ED015DAF
|
||||||
|
|
||||||
|
|
||||||
literal-string *literal-string* *E115*
|
literal-string *literal-string* *E115*
|
||||||
---------------
|
---------------
|
||||||
'string' string constant *expr-'*
|
'string' string constant *expr-'*
|
||||||
@ -2007,19 +2160,21 @@ v:swapcommand Normal mode command to be executed after a file has been
|
|||||||
For ":edit +cmd file" the value is ":cmd\r".
|
For ":edit +cmd file" the value is ":cmd\r".
|
||||||
|
|
||||||
*v:t_TYPE* *v:t_bool* *t_bool-variable*
|
*v:t_TYPE* *v:t_bool* *t_bool-variable*
|
||||||
v:t_bool Value of Boolean type. Read-only. See: |type()|
|
v:t_bool Value of |Boolean| type. Read-only. See: |type()|
|
||||||
*v:t_dict* *t_dict-variable*
|
*v:t_dict* *t_dict-variable*
|
||||||
v:t_dict Value of Dictionary type. Read-only. See: |type()|
|
v:t_dict Value of |Dictionary| type. Read-only. See: |type()|
|
||||||
*v:t_float* *t_float-variable*
|
*v:t_float* *t_float-variable*
|
||||||
v:t_float Value of Float type. Read-only. See: |type()|
|
v:t_float Value of |Float| type. Read-only. See: |type()|
|
||||||
*v:t_func* *t_func-variable*
|
*v:t_func* *t_func-variable*
|
||||||
v:t_func Value of Funcref type. Read-only. See: |type()|
|
v:t_func Value of |Funcref| type. Read-only. See: |type()|
|
||||||
*v:t_list* *t_list-variable*
|
*v:t_list* *t_list-variable*
|
||||||
v:t_list Value of List type. Read-only. See: |type()|
|
v:t_list Value of |List| type. Read-only. See: |type()|
|
||||||
*v:t_number* *t_number-variable*
|
*v:t_number* *t_number-variable*
|
||||||
v:t_number Value of Number type. Read-only. See: |type()|
|
v:t_number Value of |Number| type. Read-only. See: |type()|
|
||||||
*v:t_string* *t_string-variable*
|
*v:t_string* *t_string-variable*
|
||||||
v:t_string Value of String type. Read-only. See: |type()|
|
v:t_string Value of |String| type. Read-only. See: |type()|
|
||||||
|
*v:t_blob* *t_blob-variable*
|
||||||
|
v:t_blob Value of |Blob| type. Read-only. See: |type()|
|
||||||
|
|
||||||
*v:termresponse* *termresponse-variable*
|
*v:termresponse* *termresponse-variable*
|
||||||
v:termresponse The escape sequence returned by the terminal for the DA
|
v:termresponse The escape sequence returned by the terminal for the DA
|
||||||
@ -2103,7 +2258,7 @@ USAGE RESULT DESCRIPTION ~
|
|||||||
|
|
||||||
abs({expr}) Float or Number absolute value of {expr}
|
abs({expr}) Float or Number absolute value of {expr}
|
||||||
acos({expr}) Float arc cosine of {expr}
|
acos({expr}) Float arc cosine of {expr}
|
||||||
add({list}, {item}) List append {item} to |List| {list}
|
add({object}, {item}) List/Blob append {item} to {object}
|
||||||
and({expr}, {expr}) Number bitwise AND
|
and({expr}, {expr}) Number bitwise AND
|
||||||
api_info() Dict api metadata
|
api_info() Dict api metadata
|
||||||
append({lnum}, {string}) Number append {string} below line {lnum}
|
append({lnum}, {string}) Number append {string} below line {lnum}
|
||||||
@ -2315,8 +2470,8 @@ hlID({name}) Number syntax ID of highlight group {name}
|
|||||||
hostname() String name of the machine Vim is running on
|
hostname() String name of the machine Vim is running on
|
||||||
iconv({expr}, {from}, {to}) String convert encoding of {expr}
|
iconv({expr}, {from}, {to}) String convert encoding of {expr}
|
||||||
indent({lnum}) Number indent of line {lnum}
|
indent({lnum}) Number indent of line {lnum}
|
||||||
index({list}, {expr} [, {start} [, {ic}]])
|
index({object}, {expr} [, {start} [, {ic}]])
|
||||||
Number index in {list} where {expr} appears
|
Number index in {object} where {expr} appears
|
||||||
input({prompt} [, {text} [, {completion}]])
|
input({prompt} [, {text} [, {completion}]])
|
||||||
String get input from the user
|
String get input from the user
|
||||||
inputlist({textlist}) Number let the user pick from a choice list
|
inputlist({textlist}) Number let the user pick from a choice list
|
||||||
@ -2324,8 +2479,8 @@ inputrestore() Number restore typeahead
|
|||||||
inputsave() Number save and clear typeahead
|
inputsave() Number save and clear typeahead
|
||||||
inputsecret({prompt} [, {text}])
|
inputsecret({prompt} [, {text}])
|
||||||
String like input() but hiding the text
|
String like input() but hiding the text
|
||||||
insert({list}, {item} [, {idx}])
|
insert({object}, {item} [, {idx}])
|
||||||
List insert {item} in {list} [before {idx}]
|
List insert {item} in {object} [before {idx}]
|
||||||
interrupt() none interrupt script execution
|
interrupt() none interrupt script execution
|
||||||
invert({expr}) Number bitwise invert
|
invert({expr}) Number bitwise invert
|
||||||
isdirectory({directory}) Number |TRUE| if {directory} is a directory
|
isdirectory({directory}) Number |TRUE| if {directory} is a directory
|
||||||
@ -2384,8 +2539,8 @@ min({expr}) Number minimum value of items in {expr}
|
|||||||
mkdir({name} [, {path} [, {prot}]])
|
mkdir({name} [, {path} [, {prot}]])
|
||||||
Number create directory {name}
|
Number create directory {name}
|
||||||
mode([expr]) String current editing mode
|
mode([expr]) String current editing mode
|
||||||
msgpackdump({list}) List dump a list of objects to msgpack
|
msgpackdump({list} [, {type}]) List/Blob dump objects to msgpack
|
||||||
msgpackparse({list}) List parse msgpack to a list of objects
|
msgpackparse({data}) List parse msgpack to a list of objects
|
||||||
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
|
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
|
||||||
nr2char({expr}[, {utf8}]) String single char with ASCII/UTF8 value {expr}
|
nr2char({expr}[, {utf8}]) String single char with ASCII/UTF8 value {expr}
|
||||||
nvim_...({args}...) any call nvim |api| functions
|
nvim_...({args}...) any call nvim |api| functions
|
||||||
@ -2407,7 +2562,7 @@ pyxeval({expr}) any evaluate |python_x| expression
|
|||||||
range({expr} [, {max} [, {stride}]])
|
range({expr} [, {max} [, {stride}]])
|
||||||
List items from {expr} to {max}
|
List items from {expr} to {max}
|
||||||
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
|
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
|
||||||
readfile({fname} [, {binary} [, {max}]])
|
readfile({fname} [, {type} [, {max}]])
|
||||||
List get list of lines from file {fname}
|
List get list of lines from file {fname}
|
||||||
reg_executing() String get the executing register name
|
reg_executing() String get the executing register name
|
||||||
reg_recording() String get the recording register name
|
reg_recording() String get the recording register name
|
||||||
@ -2424,7 +2579,10 @@ remote_read({serverid} [, {timeout}])
|
|||||||
remote_send({server}, {string} [, {idvar}])
|
remote_send({server}, {string} [, {idvar}])
|
||||||
String send key sequence
|
String send key sequence
|
||||||
remote_startserver({name}) none become server {name}
|
remote_startserver({name}) none become server {name}
|
||||||
remove({list}, {idx} [, {end}]) any remove items {idx}-{end} from {list}
|
remove({list}, {idx} [, {end}]) any/List
|
||||||
|
remove items {idx}-{end} from {list}
|
||||||
|
remove({blob}, {idx} [, {end}]) Number/Blob
|
||||||
|
remove bytes {idx}-{end} from {blob}
|
||||||
remove({dict}, {key}) any remove entry {key} from {dict}
|
remove({dict}, {key}) any remove entry {key} from {dict}
|
||||||
rename({from}, {to}) Number rename (move) file from {from} to {to}
|
rename({from}, {to}) Number rename (move) file from {from} to {to}
|
||||||
repeat({expr}, {count}) String repeat {expr} {count} times
|
repeat({expr}, {count}) String repeat {expr} {count} times
|
||||||
@ -2612,8 +2770,8 @@ winrestview({dict}) none restore view of current window
|
|||||||
winsaveview() Dict save view of current window
|
winsaveview() Dict save view of current window
|
||||||
winwidth({nr}) Number width of window {nr}
|
winwidth({nr}) Number width of window {nr}
|
||||||
wordcount() Dict get byte/char/word statistics
|
wordcount() Dict get byte/char/word statistics
|
||||||
writefile({list}, {fname} [, {flags}])
|
writefile({object}, {fname} [, {flags}])
|
||||||
Number write list of lines to file {fname}
|
Number write |Blob| or |List| of lines to file
|
||||||
xor({expr}, {expr}) Number bitwise XOR
|
xor({expr}, {expr}) Number bitwise XOR
|
||||||
|
|
||||||
|
|
||||||
@ -2647,13 +2805,14 @@ acos({expr}) *acos()*
|
|||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
Compute()->acos()
|
Compute()->acos()
|
||||||
|
|
||||||
add({list}, {expr}) *add()*
|
add({object}, {expr}) *add()*
|
||||||
Append the item {expr} to |List| {list}. Returns the
|
Append the item {expr} to |List| or |Blob| {object}. Returns
|
||||||
resulting |List|. Examples: >
|
the resulting |List| or |Blob|. Examples: >
|
||||||
:let alist = add([1, 2, 3], item)
|
:let alist = add([1, 2, 3], item)
|
||||||
:call add(mylist, "woodstock")
|
:call add(mylist, "woodstock")
|
||||||
< Note that when {expr} is a |List| it is appended as a single
|
< Note that when {expr} is a |List| it is appended as a single
|
||||||
item. Use |extend()| to concatenate |Lists|.
|
item. Use |extend()| to concatenate |Lists|.
|
||||||
|
When {object} is a |Blob| then {expr} must be a number.
|
||||||
Use |insert()| to add an item at another position.
|
Use |insert()| to add an item at another position.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
@ -3066,8 +3225,8 @@ chansend({id}, {data}) *chansend()*
|
|||||||
written if the write succeeded, 0 otherwise.
|
written if the write succeeded, 0 otherwise.
|
||||||
See |channel-bytes| for more information.
|
See |channel-bytes| for more information.
|
||||||
|
|
||||||
{data} may be a string, string convertible, or a list. If
|
{data} may be a string, string convertible, |Blob|, or a list.
|
||||||
{data} is a list, the items will be joined by newlines; any
|
If {data} is a list, the items will be joined by newlines; any
|
||||||
newlines in an item will be sent as NUL. To send a final
|
newlines in an item will be sent as NUL. To send a final
|
||||||
newline, include a final empty string. Example: >
|
newline, include a final empty string. Example: >
|
||||||
:call chansend(id, ["abc", "123\n456", ""])
|
:call chansend(id, ["abc", "123\n456", ""])
|
||||||
@ -3640,9 +3799,13 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
|
|||||||
|
|
||||||
empty({expr}) *empty()*
|
empty({expr}) *empty()*
|
||||||
Return the Number 1 if {expr} is empty, zero otherwise.
|
Return the Number 1 if {expr} is empty, zero otherwise.
|
||||||
A |List| or |Dictionary| is empty when it does not have any
|
- A |List| or |Dictionary| is empty when it does not have any
|
||||||
items. A Number is empty when its value is zero. Special
|
items.
|
||||||
variable is empty when it is |v:false| or |v:null|.
|
- A |String| is empty when its length is zero.
|
||||||
|
- A |Number| and |Float| are empty when their value is zero.
|
||||||
|
- |v:false| and |v:null| are empty, |v:true| is not.
|
||||||
|
- A |Blob| is empty when its length is zero.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->empty()
|
mylist->empty()
|
||||||
|
|
||||||
@ -3665,8 +3828,8 @@ escape({string}, {chars}) *escape()*
|
|||||||
*eval()*
|
*eval()*
|
||||||
eval({string}) Evaluate {string} and return the result. Especially useful to
|
eval({string}) Evaluate {string} and return the result. Especially useful to
|
||||||
turn the result of |string()| back into the original value.
|
turn the result of |string()| back into the original value.
|
||||||
This works for Numbers, Floats, Strings and composites of
|
This works for Numbers, Floats, Strings, Blobs and composites
|
||||||
them. Also works for |Funcref|s that refer to existing
|
of them. Also works for |Funcref|s that refer to existing
|
||||||
functions.
|
functions.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
@ -4081,7 +4244,7 @@ filter({expr1}, {expr2}) *filter()*
|
|||||||
|Dictionary| to remain unmodified make a copy first: >
|
|Dictionary| to remain unmodified make a copy first: >
|
||||||
:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
|
:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
|
||||||
|
|
||||||
< Returns {expr1}, the |List| , |Blob| or |Dictionary| that was
|
< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
|
||||||
filtered. When an error is encountered while evaluating
|
filtered. When an error is encountered while evaluating
|
||||||
{expr2} no further items in {expr1} are processed. When
|
{expr2} no further items in {expr1} are processed. When
|
||||||
{expr2} is a Funcref errors inside a function are ignored,
|
{expr2} is a Funcref errors inside a function are ignored,
|
||||||
@ -4362,6 +4525,10 @@ get({list}, {idx} [, {default}]) *get()*
|
|||||||
omitted.
|
omitted.
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->get(idx)
|
mylist->get(idx)
|
||||||
|
get({blob}, {idx} [, {default}])
|
||||||
|
Get byte {idx} from |Blob| {blob}. When this byte is not
|
||||||
|
available return {default}. Return -1 when {default} is
|
||||||
|
omitted.
|
||||||
get({dict}, {key} [, {default}])
|
get({dict}, {key} [, {default}])
|
||||||
Get item with key {key} from |Dictionary| {dict}. When this
|
Get item with key {key} from |Dictionary| {dict}. When this
|
||||||
item is not available return {default}. Return zero when
|
item is not available return {default}. Return zero when
|
||||||
@ -5548,17 +5715,21 @@ indent({lnum}) The result is a Number, which is indent of line {lnum} in the
|
|||||||
When {lnum} is invalid -1 is returned.
|
When {lnum} is invalid -1 is returned.
|
||||||
|
|
||||||
|
|
||||||
index({list}, {expr} [, {start} [, {ic}]]) *index()*
|
index({object}, {expr} [, {start} [, {ic}]]) *index()*
|
||||||
Return the lowest index in |List| {list} where the item has a
|
If {object} is a |List| return the lowest index where the item
|
||||||
value equal to {expr}. There is no automatic conversion, so
|
has a value equal to {expr}. There is no automatic
|
||||||
the String "4" is different from the Number 4. And the number
|
conversion, so the String "4" is different from the Number 4.
|
||||||
4 is different from the Float 4.0. The value of 'ignorecase'
|
And the Number 4 is different from the Float 4.0. The value
|
||||||
is not used here, case always matters.
|
of 'ignorecase' is not used here, case always matters.
|
||||||
|
|
||||||
|
If {object} is a |Blob| return the lowest index where the byte
|
||||||
|
value is equal to {expr}.
|
||||||
|
|
||||||
If {start} is given then start looking at the item with index
|
If {start} is given then start looking at the item with index
|
||||||
{start} (may be negative for an item relative to the end).
|
{start} (may be negative for an item relative to the end).
|
||||||
When {ic} is given and it is |TRUE|, ignore case. Otherwise
|
When {ic} is given and it is |TRUE|, ignore case. Otherwise
|
||||||
case must match.
|
case must match.
|
||||||
-1 is returned when {expr} is not found in {list}.
|
-1 is returned when {expr} is not found in {object}.
|
||||||
Example: >
|
Example: >
|
||||||
:let idx = index(words, "the")
|
:let idx = index(words, "the")
|
||||||
:if index(numbers, 123) >= 0
|
:if index(numbers, 123) >= 0
|
||||||
@ -5717,13 +5888,16 @@ inputsecret({prompt} [, {text}]) *inputsecret()*
|
|||||||
typed on the command-line in response to the issued prompt.
|
typed on the command-line in response to the issued prompt.
|
||||||
NOTE: Command-line completion is not supported.
|
NOTE: Command-line completion is not supported.
|
||||||
|
|
||||||
insert({list}, {item} [, {idx}]) *insert()*
|
insert({object}, {item} [, {idx}]) *insert()*
|
||||||
Insert {item} at the start of |List| {list}.
|
When {object} is a |List| or a |Blob| insert {item} at the start
|
||||||
|
of it.
|
||||||
|
|
||||||
If {idx} is specified insert {item} before the item with index
|
If {idx} is specified insert {item} before the item with index
|
||||||
{idx}. If {idx} is zero it goes before the first item, just
|
{idx}. If {idx} is zero it goes before the first item, just
|
||||||
like omitting {idx}. A negative {idx} is also possible, see
|
like omitting {idx}. A negative {idx} is also possible, see
|
||||||
|list-index|. -1 inserts just before the last item.
|
|list-index|. -1 inserts just before the last item.
|
||||||
Returns the resulting |List|. Examples: >
|
|
||||||
|
Returns the resulting |List| or |Blob|. Examples: >
|
||||||
:let mylist = insert([2, 3, 5], 1)
|
:let mylist = insert([2, 3, 5], 1)
|
||||||
:call insert(mylist, 4, -1)
|
:call insert(mylist, 4, -1)
|
||||||
:call insert(mylist, 6, len(mylist))
|
:call insert(mylist, 6, len(mylist))
|
||||||
@ -5787,16 +5961,16 @@ islocked({expr}) *islocked()* *E786*
|
|||||||
|
|
||||||
id({expr}) *id()*
|
id({expr}) *id()*
|
||||||
Returns a |String| which is a unique identifier of the
|
Returns a |String| which is a unique identifier of the
|
||||||
container type (|List|, |Dict| and |Partial|). It is
|
container type (|List|, |Dict|, |Blob| and |Partial|). It is
|
||||||
guaranteed that for the mentioned types `id(v1) ==# id(v2)`
|
guaranteed that for the mentioned types `id(v1) ==# id(v2)`
|
||||||
returns true iff `type(v1) == type(v2) && v1 is v2`.
|
returns true iff `type(v1) == type(v2) && v1 is v2`.
|
||||||
Note that |v:_null_string|, |v:_null_list|, and |v:_null_dict|
|
Note that |v:_null_string|, |v:_null_list|, |v:_null_dict| and
|
||||||
have the same `id()` with different types because they are
|
|v:_null_blob| have the same `id()` with different types
|
||||||
internally represented as a NULL pointers. `id()` returns a
|
because they are internally represented as NULL pointers.
|
||||||
hexadecimal representanion of the pointers to the containers
|
`id()` returns a hexadecimal representanion of the pointers to
|
||||||
(i.e. like `0x994a40`), same as `printf("%p", {expr})`,
|
the containers (i.e. like `0x994a40`), same as `printf("%p",
|
||||||
but it is advised against counting on the exact format of
|
{expr})`, but it is advised against counting on the exact
|
||||||
return value.
|
format of the return value.
|
||||||
|
|
||||||
It is not guaranteed that `id(no_longer_existing_container)`
|
It is not guaranteed that `id(no_longer_existing_container)`
|
||||||
will not be equal to some other `id()`: new containers may
|
will not be equal to some other `id()`: new containers may
|
||||||
@ -5806,8 +5980,13 @@ items({dict}) *items()*
|
|||||||
Return a |List| with all the key-value pairs of {dict}. Each
|
Return a |List| with all the key-value pairs of {dict}. Each
|
||||||
|List| item is a list with two items: the key of a {dict}
|
|List| item is a list with two items: the key of a {dict}
|
||||||
entry and the value of this entry. The |List| is in arbitrary
|
entry and the value of this entry. The |List| is in arbitrary
|
||||||
order.
|
order. Also see |keys()| and |values()|.
|
||||||
Can also be used as a |method|: >
|
Example: >
|
||||||
|
for [key, value] in items(mydict)
|
||||||
|
echo key . ': ' . value
|
||||||
|
endfor
|
||||||
|
|
||||||
|
< Can also be used as a |method|: >
|
||||||
mydict->items()
|
mydict->items()
|
||||||
|
|
||||||
isnan({expr}) *isnan()*
|
isnan({expr}) *isnan()*
|
||||||
@ -5974,10 +6153,12 @@ json_encode({expr}) *json_encode()*
|
|||||||
surrogate pairs (such strings are not valid UTF-8 strings).
|
surrogate pairs (such strings are not valid UTF-8 strings).
|
||||||
Non-printable characters are converted into "\u1234" escapes
|
Non-printable characters are converted into "\u1234" escapes
|
||||||
or special escapes like "\t", other are dumped as-is.
|
or special escapes like "\t", other are dumped as-is.
|
||||||
|
|Blob|s are converted to arrays of the individual bytes.
|
||||||
|
|
||||||
keys({dict}) *keys()*
|
keys({dict}) *keys()*
|
||||||
Return a |List| with all the keys of {dict}. The |List| is in
|
Return a |List| with all the keys of {dict}. The |List| is in
|
||||||
arbitrary order.
|
arbitrary order. Also see |items()| and |values()|.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mydict->keys()
|
mydict->keys()
|
||||||
|
|
||||||
@ -6141,12 +6322,14 @@ luaeval({expr}[, {expr}])
|
|||||||
to Vim data structures. See |lua-eval| for more details.
|
to Vim data structures. See |lua-eval| for more details.
|
||||||
|
|
||||||
map({expr1}, {expr2}) *map()*
|
map({expr1}, {expr2}) *map()*
|
||||||
{expr1} must be a |List| or a |Dictionary|.
|
{expr1} must be a |List|, |Blob| or |Dictionary|.
|
||||||
Replace each item in {expr1} with the result of evaluating
|
Replace each item in {expr1} with the result of evaluating
|
||||||
{expr2}. {expr2} must be a |string| or |Funcref|.
|
{expr2}. For a |Blob| each byte is replaced.
|
||||||
|
|
||||||
|
{expr2} must be a |string| or |Funcref|.
|
||||||
|
|
||||||
If {expr2} is a |string|, inside {expr2} |v:val| has the value
|
If {expr2} is a |string|, inside {expr2} |v:val| has the value
|
||||||
of the current item. For a |Dictionary| |v:key| has the key
|
of the current item. For a |Dictionary| |v:key| has the key
|
||||||
of the current item and for a |List| |v:key| has the index of
|
of the current item and for a |List| |v:key| has the index of
|
||||||
the current item. For a |Blob| |v:key| has the index of the
|
the current item. For a |Blob| |v:key| has the index of the
|
||||||
current byte.
|
current byte.
|
||||||
@ -6179,11 +6362,11 @@ map({expr1}, {expr2}) *map()*
|
|||||||
|Dictionary| to remain unmodified make a copy first: >
|
|Dictionary| to remain unmodified make a copy first: >
|
||||||
:let tlist = map(copy(mylist), ' v:val . "\t"')
|
:let tlist = map(copy(mylist), ' v:val . "\t"')
|
||||||
|
|
||||||
< Returns {expr1}, the |List| or |Dictionary| that was filtered.
|
< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
|
||||||
When an error is encountered while evaluating {expr2} no
|
filtered. When an error is encountered while evaluating
|
||||||
further items in {expr1} are processed. When {expr2} is a
|
{expr2} no further items in {expr1} are processed. When
|
||||||
Funcref errors inside a function are ignored, unless it was
|
{expr2} is a Funcref errors inside a function are ignored,
|
||||||
defined with the "abort" flag.
|
unless it was defined with the "abort" flag.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->map(expr2)
|
mylist->map(expr2)
|
||||||
@ -6651,11 +6834,15 @@ mode([expr]) Return a string that indicates the current mode.
|
|||||||
the leading character(s).
|
the leading character(s).
|
||||||
Also see |visualmode()|.
|
Also see |visualmode()|.
|
||||||
|
|
||||||
msgpackdump({list}) *msgpackdump()*
|
msgpackdump({list} [, {type}]) *msgpackdump()*
|
||||||
Convert a list of VimL objects to msgpack. Returned value is
|
Convert a list of VimL objects to msgpack. Returned value is a
|
||||||
|readfile()|-style list. Example: >
|
|readfile()|-style list. When {type} contains "B", a |Blob| is
|
||||||
|
returned instead. Example: >
|
||||||
call writefile(msgpackdump([{}]), 'fname.mpack', 'b')
|
call writefile(msgpackdump([{}]), 'fname.mpack', 'b')
|
||||||
< This will write the single 0x80 byte to `fname.mpack` file
|
< or, using a |Blob|: >
|
||||||
|
call writefile(msgpackdump([{}], 'B'), 'fname.mpack')
|
||||||
|
<
|
||||||
|
This will write the single 0x80 byte to a `fname.mpack` file
|
||||||
(dictionary with zero items is represented by 0x80 byte in
|
(dictionary with zero items is represented by 0x80 byte in
|
||||||
messagepack).
|
messagepack).
|
||||||
|
|
||||||
@ -6663,11 +6850,12 @@ msgpackdump({list}) *msgpackdump()*
|
|||||||
1. |Funcref|s cannot be dumped.
|
1. |Funcref|s cannot be dumped.
|
||||||
2. Containers that reference themselves cannot be dumped.
|
2. Containers that reference themselves cannot be dumped.
|
||||||
3. Dictionary keys are always dumped as STR strings.
|
3. Dictionary keys are always dumped as STR strings.
|
||||||
4. Other strings are always dumped as BIN strings.
|
4. Other strings and |Blob|s are always dumped as BIN strings.
|
||||||
5. Points 3. and 4. do not apply to |msgpack-special-dict|s.
|
5. Points 3. and 4. do not apply to |msgpack-special-dict|s.
|
||||||
|
|
||||||
msgpackparse({list}) *msgpackparse()*
|
msgpackparse({data}) *msgpackparse()*
|
||||||
Convert a |readfile()|-style list to a list of VimL objects.
|
Convert a |readfile()|-style list or a |Blob| to a list of
|
||||||
|
VimL objects.
|
||||||
Example: >
|
Example: >
|
||||||
let fname = expand('~/.config/nvim/shada/main.shada')
|
let fname = expand('~/.config/nvim/shada/main.shada')
|
||||||
let mpack = readfile(fname, 'b')
|
let mpack = readfile(fname, 'b')
|
||||||
@ -6677,7 +6865,7 @@ msgpackparse({list}) *msgpackparse()*
|
|||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
1. Mapping ordering is not preserved unless messagepack
|
1. Mapping ordering is not preserved unless messagepack
|
||||||
mapping is dumped using generic mapping
|
mapping is dumped using generic mapping
|
||||||
(|msgpack-special-map|).
|
(|msgpack-special-map|).
|
||||||
2. Since the parser aims to preserve all data untouched
|
2. Since the parser aims to preserve all data untouched
|
||||||
(except for 1.) some strings are parsed to
|
(except for 1.) some strings are parsed to
|
||||||
@ -6721,9 +6909,9 @@ msgpackparse({list}) *msgpackparse()*
|
|||||||
zero byte or if string is a mapping key and mapping is
|
zero byte or if string is a mapping key and mapping is
|
||||||
being represented as special dictionary for other
|
being represented as special dictionary for other
|
||||||
reasons.
|
reasons.
|
||||||
binary |readfile()|-style list of strings. This value will
|
binary |String|, or |Blob| if binary string contains zero
|
||||||
appear in |msgpackparse()| output if binary string
|
byte. This value cannot appear in |msgpackparse()|
|
||||||
contains zero byte.
|
output since blobs were introduced.
|
||||||
array |List|. This value cannot appear in |msgpackparse()|
|
array |List|. This value cannot appear in |msgpackparse()|
|
||||||
output.
|
output.
|
||||||
*msgpack-special-map*
|
*msgpack-special-map*
|
||||||
@ -7168,16 +7356,18 @@ readdir({directory} [, {expr}])
|
|||||||
echo s:tree(".")
|
echo s:tree(".")
|
||||||
<
|
<
|
||||||
*readfile()*
|
*readfile()*
|
||||||
readfile({fname} [, {binary} [, {max}]])
|
readfile({fname} [, {type} [, {max}]])
|
||||||
Read file {fname} and return a |List|, each line of the file
|
Read file {fname} and return a |List|, each line of the file
|
||||||
as an item. Lines are broken at NL characters. Macintosh
|
as an item. Lines are broken at NL characters. Macintosh
|
||||||
files separated with CR will result in a single long line
|
files separated with CR will result in a single long line
|
||||||
(unless a NL appears somewhere).
|
(unless a NL appears somewhere).
|
||||||
All NUL characters are replaced with a NL character.
|
All NUL characters are replaced with a NL character.
|
||||||
When {binary} contains "b" binary mode is used:
|
When {type} contains "b" binary mode is used:
|
||||||
- When the last line ends in a NL an extra empty list item is
|
- When the last line ends in a NL an extra empty list item is
|
||||||
added.
|
added.
|
||||||
- No CR characters are removed.
|
- No CR characters are removed.
|
||||||
|
When {type} contains "B" a |Blob| is returned with the binary
|
||||||
|
data of the file unmodified.
|
||||||
Otherwise:
|
Otherwise:
|
||||||
- CR characters that appear before a NL are removed.
|
- CR characters that appear before a NL are removed.
|
||||||
- Whether the last line ends in a NL or not does not matter.
|
- Whether the last line ends in a NL or not does not matter.
|
||||||
@ -7357,6 +7547,17 @@ remove({list}, {idx} [, {end}]) *remove()*
|
|||||||
< Can also be used as a |method|: >
|
< Can also be used as a |method|: >
|
||||||
mylist->remove(idx)
|
mylist->remove(idx)
|
||||||
|
|
||||||
|
remove({blob}, {idx} [, {end}])
|
||||||
|
Without {end}: Remove the byte at {idx} from |Blob| {blob} and
|
||||||
|
return the byte.
|
||||||
|
With {end}: Remove bytes from {idx} to {end} (inclusive) and
|
||||||
|
return a |Blob| with these bytes. When {idx} points to the same
|
||||||
|
byte as {end} a |Blob| with one byte is returned. When {end}
|
||||||
|
points to a byte before {idx} this is an error.
|
||||||
|
Example: >
|
||||||
|
:echo "last byte: " . remove(myblob, -1)
|
||||||
|
:call remove(mylist, 0, 9)
|
||||||
|
|
||||||
remove({dict}, {key})
|
remove({dict}, {key})
|
||||||
Remove the entry from {dict} with key {key} and return it.
|
Remove the entry from {dict} with key {key} and return it.
|
||||||
Example: >
|
Example: >
|
||||||
@ -7400,9 +7601,11 @@ resolve({filename}) *resolve()* *E655*
|
|||||||
path name) and also keeps a trailing path separator.
|
path name) and also keeps a trailing path separator.
|
||||||
|
|
||||||
*reverse()*
|
*reverse()*
|
||||||
reverse({list}) Reverse the order of items in {list} in-place. Returns
|
reverse({object})
|
||||||
{list}.
|
Reverse the order of items in {object} in-place.
|
||||||
If you want a list to remain unmodified make a copy first: >
|
{object} can be a |List| or a |Blob|.
|
||||||
|
Returns {object}.
|
||||||
|
If you want an object to remain unmodified make a copy first: >
|
||||||
:let revlist = reverse(copy(mylist))
|
:let revlist = reverse(copy(mylist))
|
||||||
< Can also be used as a |method|: >
|
< Can also be used as a |method|: >
|
||||||
mylist->reverse()
|
mylist->reverse()
|
||||||
@ -8803,14 +9006,15 @@ stridx({haystack}, {needle} [, {start}]) *stridx()*
|
|||||||
|
|
||||||
*string()*
|
*string()*
|
||||||
string({expr}) Return {expr} converted to a String. If {expr} is a Number,
|
string({expr}) Return {expr} converted to a String. If {expr} is a Number,
|
||||||
Float, String or a composition of them, then the result can be
|
Float, String, Blob or a composition of them, then the result
|
||||||
parsed back with |eval()|.
|
can be parsed back with |eval()|.
|
||||||
{expr} type result ~
|
{expr} type result ~
|
||||||
String 'string'
|
String 'string'
|
||||||
Number 123
|
Number 123
|
||||||
Float 123.123456 or 1.123456e8 or
|
Float 123.123456 or 1.123456e8 or
|
||||||
`str2float('inf')`
|
`str2float('inf')`
|
||||||
Funcref `function('name')`
|
Funcref `function('name')`
|
||||||
|
Blob 0z00112233.44556677.8899
|
||||||
List [item, item]
|
List [item, item]
|
||||||
Dictionary {key: value, key: value}
|
Dictionary {key: value, key: value}
|
||||||
Note that in String values the ' character is doubled.
|
Note that in String values the ' character is doubled.
|
||||||
@ -9483,6 +9687,7 @@ type({expr}) *type()*
|
|||||||
Float: 5 (|v:t_float|)
|
Float: 5 (|v:t_float|)
|
||||||
Boolean: 6 (|v:true| and |v:false|)
|
Boolean: 6 (|v:true| and |v:false|)
|
||||||
Null: 7 (|v:null|)
|
Null: 7 (|v:null|)
|
||||||
|
Blob: 10 (|v:t_blob|)
|
||||||
For backward compatibility, this method can be used: >
|
For backward compatibility, this method can be used: >
|
||||||
:if type(myvar) == type(0)
|
:if type(myvar) == type(0)
|
||||||
:if type(myvar) == type("")
|
:if type(myvar) == type("")
|
||||||
@ -9567,7 +9772,7 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
|
|||||||
|
|
||||||
values({dict}) *values()*
|
values({dict}) *values()*
|
||||||
Return a |List| with all the values of {dict}. The |List| is
|
Return a |List| with all the values of {dict}. The |List| is
|
||||||
in arbitrary order.
|
in arbitrary order. Also see |items()| and |keys()|.
|
||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mydict->values()
|
mydict->values()
|
||||||
@ -9927,14 +10132,17 @@ wordcount() *wordcount()*
|
|||||||
|
|
||||||
|
|
||||||
*writefile()*
|
*writefile()*
|
||||||
writefile({list}, {fname} [, {flags}])
|
writefile({object}, {fname} [, {flags}])
|
||||||
Write |List| {list} to file {fname}. Each list item is
|
When {object} is a |List| write it to file {fname}. Each list
|
||||||
separated with a NL. Each list item must be a String or
|
item is separated with a NL. Each list item must be a String
|
||||||
Number.
|
or Number.
|
||||||
When {flags} contains "b" then binary mode is used: There will
|
When {flags} contains "b" then binary mode is used: There will
|
||||||
not be a NL after the last list item. An empty item at the
|
not be a NL after the last list item. An empty item at the
|
||||||
end does cause the last line in the file to end in a NL.
|
end does cause the last line in the file to end in a NL.
|
||||||
|
|
||||||
|
When {object} is a |Blob| write the bytes to file {fname}
|
||||||
|
unmodified.
|
||||||
|
|
||||||
When {flags} contains "a" then append mode is used, lines are
|
When {flags} contains "a" then append mode is used, lines are
|
||||||
appended to the file: >
|
appended to the file: >
|
||||||
:call writefile(["foo"], "event.log", "a")
|
:call writefile(["foo"], "event.log", "a")
|
||||||
@ -10450,7 +10658,10 @@ This does NOT work: >
|
|||||||
This cannot be used to set a byte in a String. You
|
This cannot be used to set a byte in a String. You
|
||||||
can do that like this: >
|
can do that like this: >
|
||||||
:let var = var[0:2] . 'X' . var[4:]
|
:let var = var[0:2] . 'X' . var[4:]
|
||||||
<
|
< When {var-name} is a |Blob| then {idx} can be the
|
||||||
|
length of the blob, in which case one byte is
|
||||||
|
appended.
|
||||||
|
|
||||||
*E711* *E719*
|
*E711* *E719*
|
||||||
:let {var-name}[{idx1}:{idx2}] = {expr1} *E708* *E709* *E710*
|
:let {var-name}[{idx1}:{idx2}] = {expr1} *E708* *E709* *E710*
|
||||||
Set a sequence of items in a |List| to the result of
|
Set a sequence of items in a |List| to the result of
|
||||||
@ -10687,10 +10898,18 @@ text...
|
|||||||
:const x = 1
|
:const x = 1
|
||||||
< is equivalent to: >
|
< is equivalent to: >
|
||||||
:let x = 1
|
:let x = 1
|
||||||
:lockvar 1 x
|
:lockvar! x
|
||||||
< This is useful if you want to make sure the variable
|
< This is useful if you want to make sure the variable
|
||||||
is not modified.
|
is not modified. If the value is a List or Dictionary
|
||||||
*E995*
|
literal then the items also cannot be changed: >
|
||||||
|
const ll = [1, 2, 3]
|
||||||
|
let ll[1] = 5 " Error!
|
||||||
|
< Nested references are not locked: >
|
||||||
|
let lvar = ['a']
|
||||||
|
const lconst = [0, lvar]
|
||||||
|
let lconst[0] = 2 " Error!
|
||||||
|
let lconst[1][0] = 'b' " OK
|
||||||
|
< *E995*
|
||||||
|:const| does not allow to for changing a variable. >
|
|:const| does not allow to for changing a variable. >
|
||||||
:let x = 1
|
:let x = 1
|
||||||
:const x = 2 " Error!
|
:const x = 2 " Error!
|
||||||
@ -10807,28 +11026,34 @@ text...
|
|||||||
NOTE: The ":append" and ":insert" commands don't work
|
NOTE: The ":append" and ":insert" commands don't work
|
||||||
properly inside a ":while" and ":for" loop.
|
properly inside a ":while" and ":for" loop.
|
||||||
|
|
||||||
:for {var} in {list} *:for* *E690* *E732*
|
:for {var} in {object} *:for* *E690* *E732*
|
||||||
:endfo[r] *:endfo* *:endfor*
|
:endfo[r] *:endfo* *:endfor*
|
||||||
Repeat the commands between ":for" and ":endfor" for
|
Repeat the commands between ":for" and ":endfor" for
|
||||||
each item in {list}. Variable {var} is set to the
|
each item in {object}. {object} can be a |List| or
|
||||||
value of each item.
|
a |Blob|. Variable {var} is set to the value of each
|
||||||
When an error is detected for a command inside the
|
item. When an error is detected for a command inside
|
||||||
loop, execution continues after the "endfor".
|
the loop, execution continues after the "endfor".
|
||||||
Changing {list} inside the loop affects what items are
|
Changing {object} inside the loop affects what items
|
||||||
used. Make a copy if this is unwanted: >
|
are used. Make a copy if this is unwanted: >
|
||||||
:for item in copy(mylist)
|
:for item in copy(mylist)
|
||||||
< When not making a copy, Vim stores a reference to the
|
<
|
||||||
next item in the list, before executing the commands
|
When {object} is a |List| and not making a copy, Vim
|
||||||
with the current item. Thus the current item can be
|
stores a reference to the next item in the |List|
|
||||||
removed without effect. Removing any later item means
|
before executing the commands with the current item.
|
||||||
it will not be found. Thus the following example
|
Thus the current item can be removed without effect.
|
||||||
works (an inefficient way to make a list empty): >
|
Removing any later item means it will not be found.
|
||||||
|
Thus the following example works (an inefficient way
|
||||||
|
to make a |List| empty): >
|
||||||
for item in mylist
|
for item in mylist
|
||||||
call remove(mylist, 0)
|
call remove(mylist, 0)
|
||||||
endfor
|
endfor
|
||||||
< Note that reordering the list (e.g., with sort() or
|
< Note that reordering the |List| (e.g., with sort() or
|
||||||
reverse()) may have unexpected effects.
|
reverse()) may have unexpected effects.
|
||||||
|
|
||||||
|
When {object} is a |Blob|, Vim always makes a copy to
|
||||||
|
iterate over. Unlike with |List|, modifying the
|
||||||
|
|Blob| does not affect the iteration.
|
||||||
|
|
||||||
:for [{var1}, {var2}, ...] in {listlist}
|
:for [{var1}, {var2}, ...] in {listlist}
|
||||||
:endfo[r]
|
:endfo[r]
|
||||||
Like ":for" above, but each item in {listlist} must be
|
Like ":for" above, but each item in {listlist} must be
|
||||||
|
@ -189,6 +189,9 @@ VIM::Eval({expr}) Evaluates {expr} and returns (success, value) in list
|
|||||||
A |List| is turned into a string by joining the items
|
A |List| is turned into a string by joining the items
|
||||||
and inserting line breaks.
|
and inserting line breaks.
|
||||||
|
|
||||||
|
*perl-Blob*
|
||||||
|
VIM::Blob({expr}) Return Blob literal string 0zXXXX from scalar value.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
3. VIM::Buffer objects *perl-buffer*
|
3. VIM::Buffer objects *perl-buffer*
|
||||||
|
|
||||||
|
@ -326,7 +326,8 @@ semantically equivalent in Lua to:
|
|||||||
end
|
end
|
||||||
|
|
||||||
Lua nils, numbers, strings, tables and booleans are converted to their
|
Lua nils, numbers, strings, tables and booleans are converted to their
|
||||||
respective Vimscript types. Conversion of other Lua types is an error.
|
respective Vimscript types. If a Lua string contains a NUL byte, it will be
|
||||||
|
converted to a |Blob|. Conversion of other Lua types is an error.
|
||||||
|
|
||||||
The magic global "_A" contains the second argument to luaeval().
|
The magic global "_A" contains the second argument to luaeval().
|
||||||
|
|
||||||
|
@ -444,6 +444,16 @@ void set_option_to(uint64_t channel_id, void *to, int type,
|
|||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
|
||||||
TYPVAL_ENCODE_CONV_NIL(tv)
|
TYPVAL_ENCODE_CONV_NIL(tv)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
const size_t len_ = (size_t)(len); \
|
||||||
|
const blob_T *const blob_ = (blob); \
|
||||||
|
kvi_push(edata->stack, STRING_OBJ(((String) { \
|
||||||
|
.data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \
|
||||||
|
.size = len_ \
|
||||||
|
}))); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
do { \
|
do { \
|
||||||
TYPVAL_ENCODE_CONV_NIL(tv); \
|
TYPVAL_ENCODE_CONV_NIL(tv); \
|
||||||
@ -584,6 +594,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
|
|||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
|
@ -516,6 +516,7 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
|
|||||||
/// @param data will be consumed
|
/// @param data will be consumed
|
||||||
size_t channel_send(uint64_t id, char *data, size_t len,
|
size_t channel_send(uint64_t id, char *data, size_t len,
|
||||||
bool data_owned, const char **error)
|
bool data_owned, const char **error)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
Channel *chan = find_channel(id);
|
Channel *chan = find_channel(id);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
394
src/nvim/eval.c
394
src/nvim/eval.c
@ -69,6 +69,7 @@ static char *e_nowhitespace
|
|||||||
= N_("E274: No white space allowed before parenthesis");
|
= N_("E274: No white space allowed before parenthesis");
|
||||||
static char *e_invalwindow = N_("E957: Invalid window number");
|
static char *e_invalwindow = N_("E957: Invalid window number");
|
||||||
static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
|
static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
|
||||||
|
static char *e_write2 = N_("E80: Error while writing: %s");
|
||||||
|
|
||||||
// TODO(ZyX-I): move to eval/executor
|
// TODO(ZyX-I): move to eval/executor
|
||||||
static char *e_letwrong = N_("E734: Wrong variable type for %s=");
|
static char *e_letwrong = N_("E734: Wrong variable type for %s=");
|
||||||
@ -113,6 +114,8 @@ typedef struct {
|
|||||||
int fi_varcount; // nr of variables in the list
|
int fi_varcount; // nr of variables in the list
|
||||||
listwatch_T fi_lw; // keep an eye on the item used.
|
listwatch_T fi_lw; // keep an eye on the item used.
|
||||||
list_T *fi_list; // list being used
|
list_T *fi_list; // list being used
|
||||||
|
int fi_bi; // index of blob
|
||||||
|
blob_T *fi_blob; // blob being used
|
||||||
} forinfo_T;
|
} forinfo_T;
|
||||||
|
|
||||||
// values for vv_flags:
|
// values for vv_flags:
|
||||||
@ -227,6 +230,7 @@ static struct vimvar {
|
|||||||
VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
|
VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
|
VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
|
VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
|
||||||
|
VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_EVENT, "event", VAR_DICT, VV_RO),
|
VV(VV_EVENT, "event", VAR_DICT, VV_RO),
|
||||||
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
|
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
|
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
|
||||||
@ -238,6 +242,7 @@ static struct vimvar {
|
|||||||
VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
|
VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
|
||||||
VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
|
VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
|
||||||
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
|
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
|
||||||
|
VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
|
||||||
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
|
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
|
||||||
};
|
};
|
||||||
#undef VV
|
#undef VV
|
||||||
@ -251,6 +256,7 @@ static struct vimvar {
|
|||||||
#define vv_str vv_di.di_tv.vval.v_string
|
#define vv_str vv_di.di_tv.vval.v_string
|
||||||
#define vv_list vv_di.di_tv.vval.v_list
|
#define vv_list vv_di.di_tv.vval.v_list
|
||||||
#define vv_dict vv_di.di_tv.vval.v_dict
|
#define vv_dict vv_di.di_tv.vval.v_dict
|
||||||
|
#define vv_blob vv_di.di_tv.vval.v_blob
|
||||||
#define vv_partial vv_di.di_tv.vval.v_partial
|
#define vv_partial vv_di.di_tv.vval.v_partial
|
||||||
#define vv_tv vv_di.di_tv
|
#define vv_tv vv_di.di_tv
|
||||||
|
|
||||||
@ -393,6 +399,7 @@ void eval_init(void)
|
|||||||
set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
|
set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
|
||||||
set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
|
set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
|
||||||
set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
|
set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
|
||||||
|
set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
|
||||||
|
|
||||||
set_vim_var_bool(VV_FALSE, kBoolVarFalse);
|
set_vim_var_bool(VV_FALSE, kBoolVarFalse);
|
||||||
set_vim_var_bool(VV_TRUE, kBoolVarTrue);
|
set_vim_var_bool(VV_TRUE, kBoolVarTrue);
|
||||||
@ -2059,18 +2066,17 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Loop until no more [idx] or .key is following.
|
||||||
* Loop until no more [idx] or .key is following.
|
|
||||||
*/
|
|
||||||
lp->ll_tv = &v->di_tv;
|
lp->ll_tv = &v->di_tv;
|
||||||
var1.v_type = VAR_UNKNOWN;
|
var1.v_type = VAR_UNKNOWN;
|
||||||
var2.v_type = VAR_UNKNOWN;
|
var2.v_type = VAR_UNKNOWN;
|
||||||
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
|
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
|
||||||
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
|
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
|
||||||
&& !(lp->ll_tv->v_type == VAR_DICT
|
&& !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL)
|
||||||
&& lp->ll_tv->vval.v_dict != NULL)) {
|
&& !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) {
|
||||||
if (!quiet)
|
if (!quiet) {
|
||||||
EMSG(_("E689: Can only index a List or Dictionary"));
|
EMSG(_("E689: Can only index a List, Dictionary or Blob"));
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (lp->ll_range) {
|
if (lp->ll_range) {
|
||||||
@ -2119,10 +2125,11 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
|
|||||||
tv_clear(&var1);
|
tv_clear(&var1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (rettv != NULL && (rettv->v_type != VAR_LIST
|
if (rettv != NULL
|
||||||
|| rettv->vval.v_list == NULL)) {
|
&& !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL)
|
||||||
|
&& !(rettv->v_type == VAR_BLOB && rettv->vval.v_blob != NULL)) {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
EMSG(_("E709: [:] requires a List value"));
|
EMSG(_("E709: [:] requires a List or Blob value"));
|
||||||
}
|
}
|
||||||
tv_clear(&var1);
|
tv_clear(&var1);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2236,6 +2243,38 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
|
|||||||
|
|
||||||
tv_clear(&var1);
|
tv_clear(&var1);
|
||||||
lp->ll_tv = &lp->ll_di->di_tv;
|
lp->ll_tv = &lp->ll_di->di_tv;
|
||||||
|
} else if (lp->ll_tv->v_type == VAR_BLOB) {
|
||||||
|
// Get the number and item for the only or first index of the List.
|
||||||
|
if (empty1) {
|
||||||
|
lp->ll_n1 = 0;
|
||||||
|
} else {
|
||||||
|
// Is number or string.
|
||||||
|
lp->ll_n1 = (long)tv_get_number(&var1);
|
||||||
|
}
|
||||||
|
tv_clear(&var1);
|
||||||
|
|
||||||
|
const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
|
||||||
|
if (lp->ll_n1 < 0 || lp->ll_n1 > bloblen
|
||||||
|
|| (lp->ll_range && lp->ll_n1 == bloblen)) {
|
||||||
|
if (!quiet) {
|
||||||
|
EMSGN(_(e_blobidx), lp->ll_n1);
|
||||||
|
}
|
||||||
|
tv_clear(&var2);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (lp->ll_range && !lp->ll_empty2) {
|
||||||
|
lp->ll_n2 = (long)tv_get_number(&var2);
|
||||||
|
tv_clear(&var2);
|
||||||
|
if (lp->ll_n2 < 0 || lp->ll_n2 >= bloblen || lp->ll_n2 < lp->ll_n1) {
|
||||||
|
if (!quiet) {
|
||||||
|
EMSGN(_(e_blobidx), lp->ll_n2);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lp->ll_blob = lp->ll_tv->vval.v_blob;
|
||||||
|
lp->ll_tv = NULL;
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Get the number and item for the only or first index of the List.
|
// Get the number and item for the only or first index of the List.
|
||||||
if (empty1) {
|
if (empty1) {
|
||||||
@ -2329,7 +2368,50 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
|
|||||||
if (lp->ll_tv == NULL) {
|
if (lp->ll_tv == NULL) {
|
||||||
cc = *endp;
|
cc = *endp;
|
||||||
*endp = NUL;
|
*endp = NUL;
|
||||||
if (op != NULL && *op != '=') {
|
if (lp->ll_blob != NULL) {
|
||||||
|
if (op != NULL && *op != '=') {
|
||||||
|
EMSG2(_(e_letwrong), op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp->ll_range && rettv->v_type == VAR_BLOB) {
|
||||||
|
if (lp->ll_empty2) {
|
||||||
|
lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp->ll_n2 - lp->ll_n1 + 1 != tv_blob_len(rettv->vval.v_blob)) {
|
||||||
|
EMSG(_("E972: Blob value does not have the right number of bytes"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lp->ll_empty2) {
|
||||||
|
lp->ll_n2 = tv_blob_len(lp->ll_blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int il = lp->ll_n1, ir = 0; il <= lp->ll_n2; il++) {
|
||||||
|
tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool error = false;
|
||||||
|
const char_u val = tv_get_number_chk(rettv, &error);
|
||||||
|
if (!error) {
|
||||||
|
garray_T *const gap = &lp->ll_blob->bv_ga;
|
||||||
|
|
||||||
|
// Allow for appending a byte. Setting a byte beyond
|
||||||
|
// the end is an error otherwise.
|
||||||
|
if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) {
|
||||||
|
ga_grow(&lp->ll_blob->bv_ga, 1);
|
||||||
|
tv_blob_set(lp->ll_blob, lp->ll_n1, val);
|
||||||
|
if (lp->ll_n1 == gap->ga_len) {
|
||||||
|
gap->ga_len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// error for invalid range was already given in get_lval()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (op != NULL && *op != '=') {
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
|
|
||||||
if (is_const) {
|
if (is_const) {
|
||||||
@ -2508,19 +2590,32 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
|
|||||||
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
|
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
|
||||||
*errp = false;
|
*errp = false;
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
l = tv.vval.v_list;
|
if (tv.v_type == VAR_LIST) {
|
||||||
if (tv.v_type != VAR_LIST) {
|
l = tv.vval.v_list;
|
||||||
EMSG(_(e_listreq));
|
if (l == NULL) {
|
||||||
tv_clear(&tv);
|
// a null list is like an empty list: do nothing
|
||||||
} else if (l == NULL) {
|
tv_clear(&tv);
|
||||||
// a null list is like an empty list: do nothing
|
} else {
|
||||||
|
// No need to increment the refcount, it's already set for
|
||||||
|
// the list being used in "tv".
|
||||||
|
fi->fi_list = l;
|
||||||
|
tv_list_watch_add(l, &fi->fi_lw);
|
||||||
|
fi->fi_lw.lw_item = tv_list_first(l);
|
||||||
|
}
|
||||||
|
} else if (tv.v_type == VAR_BLOB) {
|
||||||
|
fi->fi_bi = 0;
|
||||||
|
if (tv.vval.v_blob != NULL) {
|
||||||
|
typval_T btv;
|
||||||
|
|
||||||
|
// Make a copy, so that the iteration still works when the
|
||||||
|
// blob is changed.
|
||||||
|
tv_blob_copy(&tv, &btv);
|
||||||
|
fi->fi_blob = btv.vval.v_blob;
|
||||||
|
}
|
||||||
tv_clear(&tv);
|
tv_clear(&tv);
|
||||||
} else {
|
} else {
|
||||||
/* No need to increment the refcount, it's already set for the
|
EMSG(_(e_listblobreq));
|
||||||
* list being used in "tv". */
|
tv_clear(&tv);
|
||||||
fi->fi_list = l;
|
|
||||||
tv_list_watch_add(l, &fi->fi_lw);
|
|
||||||
fi->fi_lw.lw_item = tv_list_first(l);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2542,6 +2637,19 @@ bool next_for_item(void *fi_void, char_u *arg)
|
|||||||
{
|
{
|
||||||
forinfo_T *fi = (forinfo_T *)fi_void;
|
forinfo_T *fi = (forinfo_T *)fi_void;
|
||||||
|
|
||||||
|
if (fi->fi_blob != NULL) {
|
||||||
|
if (fi->fi_bi >= tv_blob_len(fi->fi_blob)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_NUMBER;
|
||||||
|
tv.v_lock = VAR_FIXED;
|
||||||
|
tv.vval.v_number = tv_blob_get(fi->fi_blob, fi->fi_bi);
|
||||||
|
fi->fi_bi++;
|
||||||
|
return ex_let_vars(arg, &tv, true,
|
||||||
|
fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
|
||||||
|
}
|
||||||
|
|
||||||
listitem_T *item = fi->fi_lw.lw_item;
|
listitem_T *item = fi->fi_lw.lw_item;
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
return false;
|
return false;
|
||||||
@ -2565,6 +2673,9 @@ void free_for_info(void *fi_void)
|
|||||||
tv_list_watch_remove(fi->fi_list, &fi->fi_lw);
|
tv_list_watch_remove(fi->fi_list, &fi->fi_lw);
|
||||||
tv_list_unref(fi->fi_list);
|
tv_list_unref(fi->fi_list);
|
||||||
}
|
}
|
||||||
|
if (fi != NULL && fi->fi_blob != NULL) {
|
||||||
|
tv_blob_unref(fi->fi_blob);
|
||||||
|
}
|
||||||
xfree(fi);
|
xfree(fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2974,7 +3085,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED,
|
|||||||
} else {
|
} else {
|
||||||
di->di_flags &= ~DI_FLAGS_LOCK;
|
di->di_flags &= ~DI_FLAGS_LOCK;
|
||||||
}
|
}
|
||||||
tv_item_lock(&di->di_tv, deep, lock);
|
tv_item_lock(&di->di_tv, deep, lock, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (lp->ll_range) {
|
} else if (lp->ll_range) {
|
||||||
@ -2982,16 +3093,16 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED,
|
|||||||
|
|
||||||
// (un)lock a range of List items.
|
// (un)lock a range of List items.
|
||||||
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
|
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
|
||||||
tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock);
|
tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false);
|
||||||
li = TV_LIST_ITEM_NEXT(lp->ll_list, li);
|
li = TV_LIST_ITEM_NEXT(lp->ll_list, li);
|
||||||
lp->ll_n1++;
|
lp->ll_n1++;
|
||||||
}
|
}
|
||||||
} else if (lp->ll_list != NULL) {
|
} else if (lp->ll_list != NULL) {
|
||||||
// (un)lock a List item.
|
// (un)lock a List item.
|
||||||
tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock);
|
tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false);
|
||||||
} else {
|
} else {
|
||||||
// (un)lock a Dictionary item.
|
// (un)lock a Dictionary item.
|
||||||
tv_item_lock(&lp->ll_di->di_tv, deep, lock);
|
tv_item_lock(&lp->ll_di->di_tv, deep, lock, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -3607,7 +3718,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
if (op != '+' && op != '-' && op != '.')
|
if (op != '+' && op != '-' && op != '.')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if ((op != '+' || rettv->v_type != VAR_LIST)
|
if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB))
|
||||||
&& (op == '.' || rettv->v_type != VAR_FLOAT)) {
|
&& (op == '.' || rettv->v_type != VAR_FLOAT)) {
|
||||||
// For "list + ...", an illegal use of the first operand as
|
// For "list + ...", an illegal use of the first operand as
|
||||||
// a number cannot be determined before evaluating the 2nd
|
// a number cannot be determined before evaluating the 2nd
|
||||||
@ -3653,6 +3764,21 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
rettv->vval.v_string = p;
|
rettv->vval.v_string = p;
|
||||||
|
} else if (op == '+' && rettv->v_type == VAR_BLOB
|
||||||
|
&& var2.v_type == VAR_BLOB) {
|
||||||
|
const blob_T *const b1 = rettv->vval.v_blob;
|
||||||
|
const blob_T *const b2 = var2.vval.v_blob;
|
||||||
|
blob_T *const b = tv_blob_alloc();
|
||||||
|
|
||||||
|
for (int i = 0; i < tv_blob_len(b1); i++) {
|
||||||
|
ga_append(&b->bv_ga, tv_blob_get(b1, i));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < tv_blob_len(b2); i++) {
|
||||||
|
ga_append(&b->bv_ga, tv_blob_get(b2, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_clear(rettv);
|
||||||
|
tv_blob_set_ret(rettv, b);
|
||||||
} else if (op == '+' && rettv->v_type == VAR_LIST
|
} else if (op == '+' && rettv->v_type == VAR_LIST
|
||||||
&& var2.v_type == VAR_LIST) {
|
&& var2.v_type == VAR_LIST) {
|
||||||
// Concatenate Lists.
|
// Concatenate Lists.
|
||||||
@ -3673,10 +3799,12 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
} else {
|
} else {
|
||||||
n1 = tv_get_number_chk(rettv, &error);
|
n1 = tv_get_number_chk(rettv, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
/* This can only happen for "list + non-list". For
|
// This can only happen for "list + non-list" or
|
||||||
* "non-list + ..." or "something - ...", we returned
|
// "blob + non-blob". For "non-list + ..." or
|
||||||
* before evaluating the 2nd operand. */
|
// "something - ...", we returned before evaluating the
|
||||||
|
// 2nd operand.
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
|
tv_clear(&var2);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (var2.v_type == VAR_FLOAT)
|
if (var2.v_type == VAR_FLOAT)
|
||||||
@ -3850,6 +3978,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
|
|||||||
|
|
||||||
// Handle sixth level expression:
|
// Handle sixth level expression:
|
||||||
// number number constant
|
// number number constant
|
||||||
|
// 0zFFFFFFFF Blob constant
|
||||||
// "string" string constant
|
// "string" string constant
|
||||||
// 'string' literal string constant
|
// 'string' literal string constant
|
||||||
// &option-name option value
|
// &option-name option value
|
||||||
@ -3946,7 +4075,37 @@ static int eval7(
|
|||||||
rettv->v_type = VAR_FLOAT;
|
rettv->v_type = VAR_FLOAT;
|
||||||
rettv->vval.v_float = f;
|
rettv->vval.v_float = f;
|
||||||
}
|
}
|
||||||
|
} else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) {
|
||||||
|
blob_T *blob = NULL;
|
||||||
|
// Blob constant: 0z0123456789abcdef
|
||||||
|
if (evaluate) {
|
||||||
|
blob = tv_blob_alloc();
|
||||||
|
}
|
||||||
|
char_u *bp;
|
||||||
|
for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) {
|
||||||
|
if (!ascii_isxdigit(bp[1])) {
|
||||||
|
if (blob != NULL) {
|
||||||
|
EMSG(_("E973: Blob literal should have an even number of hex "
|
||||||
|
"characters"));
|
||||||
|
ga_clear(&blob->bv_ga);
|
||||||
|
XFREE_CLEAR(blob);
|
||||||
|
}
|
||||||
|
ret = FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (blob != NULL) {
|
||||||
|
ga_append(&blob->bv_ga, (hex2nr(*bp) << 4) + hex2nr(*(bp + 1)));
|
||||||
|
}
|
||||||
|
if (bp[2] == '.' && ascii_isxdigit(bp[3])) {
|
||||||
|
bp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blob != NULL) {
|
||||||
|
tv_blob_set_ret(rettv, blob);
|
||||||
|
}
|
||||||
|
*arg = bp;
|
||||||
} else {
|
} else {
|
||||||
|
// decimal, hex or octal number
|
||||||
vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
|
vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
EMSG2(_(e_invexpr2), *arg);
|
EMSG2(_(e_invexpr2), *arg);
|
||||||
@ -4336,7 +4495,8 @@ eval_index(
|
|||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT: {
|
case VAR_DICT:
|
||||||
|
case VAR_BLOB: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4462,6 +4622,53 @@ eval_index(
|
|||||||
rettv->vval.v_string = (char_u *)v;
|
rettv->vval.v_string = (char_u *)v;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
len = tv_blob_len(rettv->vval.v_blob);
|
||||||
|
if (range) {
|
||||||
|
// The resulting variable is a sub-blob. If the indexes
|
||||||
|
// are out of range the result is empty.
|
||||||
|
if (n1 < 0) {
|
||||||
|
n1 = len + n1;
|
||||||
|
if (n1 < 0) {
|
||||||
|
n1 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n2 < 0) {
|
||||||
|
n2 = len + n2;
|
||||||
|
} else if (n2 >= len) {
|
||||||
|
n2 = len - 1;
|
||||||
|
}
|
||||||
|
if (n1 >= len || n2 < 0 || n1 > n2) {
|
||||||
|
tv_clear(rettv);
|
||||||
|
rettv->v_type = VAR_BLOB;
|
||||||
|
rettv->vval.v_blob = NULL;
|
||||||
|
} else {
|
||||||
|
blob_T *const blob = tv_blob_alloc();
|
||||||
|
ga_grow(&blob->bv_ga, n2 - n1 + 1);
|
||||||
|
blob->bv_ga.ga_len = n2 - n1 + 1;
|
||||||
|
for (long i = n1; i <= n2; i++) {
|
||||||
|
tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i));
|
||||||
|
}
|
||||||
|
tv_clear(rettv);
|
||||||
|
tv_blob_set_ret(rettv, blob);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The resulting variable is a byte value.
|
||||||
|
// If the index is too big or negative that is an error.
|
||||||
|
if (n1 < 0) {
|
||||||
|
n1 = len + n1;
|
||||||
|
}
|
||||||
|
if (n1 < len && n1 >= 0) {
|
||||||
|
const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
|
||||||
|
tv_clear(rettv);
|
||||||
|
rettv->v_type = VAR_NUMBER;
|
||||||
|
rettv->vval.v_number = v;
|
||||||
|
} else {
|
||||||
|
EMSGN(_(e_blobidx), n1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
len = tv_list_len(rettv->vval.v_list);
|
len = tv_list_len(rettv->vval.v_list);
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
@ -5398,7 +5605,8 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_STRING: {
|
case VAR_STRING:
|
||||||
|
case VAR_BLOB: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6161,6 +6369,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
dict_T *d = NULL;
|
dict_T *d = NULL;
|
||||||
typval_T save_val;
|
typval_T save_val;
|
||||||
typval_T save_key;
|
typval_T save_key;
|
||||||
|
blob_T *b = NULL;
|
||||||
int rem = false;
|
int rem = false;
|
||||||
int todo;
|
int todo;
|
||||||
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
|
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
|
||||||
@ -6170,7 +6379,12 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
int save_did_emsg;
|
int save_did_emsg;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_LIST) {
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
tv_copy(&argvars[0], rettv);
|
||||||
|
if ((b = argvars[0].vval.v_blob) == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (argvars[0].v_type == VAR_LIST) {
|
||||||
tv_copy(&argvars[0], rettv);
|
tv_copy(&argvars[0], rettv);
|
||||||
if ((l = argvars[0].vval.v_list) == NULL
|
if ((l = argvars[0].vval.v_list) == NULL
|
||||||
|| (!map
|
|| (!map
|
||||||
@ -6184,7 +6398,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EMSG2(_(e_listdictarg), ermsg);
|
EMSG2(_(e_listdictblobarg), ermsg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6234,6 +6448,34 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash_unlock(ht);
|
hash_unlock(ht);
|
||||||
|
} else if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
vimvars[VV_KEY].vv_type = VAR_NUMBER;
|
||||||
|
|
||||||
|
for (int i = 0; i < b->bv_ga.ga_len; i++) {
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_NUMBER;
|
||||||
|
const varnumber_T val = tv_blob_get(b, i);
|
||||||
|
tv.vval.v_number = val;
|
||||||
|
vimvars[VV_KEY].vv_nr = idx;
|
||||||
|
if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tv.v_type != VAR_NUMBER) {
|
||||||
|
EMSG(_(e_invalblob));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (map) {
|
||||||
|
if (tv.vval.v_number != val) {
|
||||||
|
tv_blob_set(b, i, tv.vval.v_number);
|
||||||
|
}
|
||||||
|
} else if (rem) {
|
||||||
|
char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
|
||||||
|
memmove(p + i, p + i + 1, (size_t)b->bv_ga.ga_len - i - 1);
|
||||||
|
b->bv_ga.ga_len--;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(argvars[0].v_type == VAR_LIST);
|
assert(argvars[0].v_type == VAR_LIST);
|
||||||
vimvars[VV_KEY].vv_type = VAR_NUMBER;
|
vimvars[VV_KEY].vv_type = VAR_NUMBER;
|
||||||
@ -7728,10 +7970,61 @@ bool write_list(FileDescriptor *const fp, const list_T *const list,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
write_list_error:
|
write_list_error:
|
||||||
emsgf(_("E80: Error while writing: %s"), os_strerror(error));
|
emsgf(_(e_write2), os_strerror(error));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a blob to file with descriptor `fp`.
|
||||||
|
///
|
||||||
|
/// @param[in] fp File to write to.
|
||||||
|
/// @param[in] blob Blob to write.
|
||||||
|
///
|
||||||
|
/// @return true on success, or false on failure.
|
||||||
|
bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
const int len = tv_blob_len(blob);
|
||||||
|
if (len > 0) {
|
||||||
|
const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len);
|
||||||
|
if (written < (ptrdiff_t)len) {
|
||||||
|
error = (int)written;
|
||||||
|
goto write_blob_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = file_flush(fp);
|
||||||
|
if (error != 0) {
|
||||||
|
goto write_blob_error;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
write_blob_error:
|
||||||
|
EMSG2(_(e_write2), os_strerror(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a blob from a file `fd`.
|
||||||
|
///
|
||||||
|
/// @param[in] fd File to read from.
|
||||||
|
/// @param[in,out] blob Blob to write to.
|
||||||
|
///
|
||||||
|
/// @return true on success, or false on failure.
|
||||||
|
bool read_blob(FILE *const fd, blob_T *const blob)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
FileInfo file_info;
|
||||||
|
if (!os_fileinfo_fd(fileno(fd), &file_info)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const int size = (int)os_fileinfo_size(&file_info);
|
||||||
|
ga_grow(&blob->bv_ga, size);
|
||||||
|
blob->bv_ga.ga_len = size;
|
||||||
|
if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
|
||||||
|
< (size_t)blob->bv_ga.ga_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Saves a typval_T as a string.
|
/// Saves a typval_T as a string.
|
||||||
///
|
///
|
||||||
/// For lists or buffers, replaces NLs with NUL and separates items with NLs.
|
/// For lists or buffers, replaces NLs with NUL and separates items with NLs.
|
||||||
@ -9248,7 +9541,10 @@ static void set_var_const(const char *name, const size_t name_len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_const) {
|
if (is_const) {
|
||||||
tv_item_lock(&v->di_tv, 1, true);
|
// Like :lockvar! name: lock the value and what it contains, but only
|
||||||
|
// if the reference count is up to one. That locks only literal
|
||||||
|
// values.
|
||||||
|
tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9456,6 +9752,9 @@ int var_item_copy(const vimconv_T *const conv,
|
|||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case VAR_BLOB:
|
||||||
|
tv_blob_copy(from, to);
|
||||||
|
break;
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
to->v_type = VAR_DICT;
|
to->v_type = VAR_DICT;
|
||||||
to->v_lock = VAR_UNLOCKED;
|
to->v_lock = VAR_UNLOCKED;
|
||||||
@ -10815,6 +11114,29 @@ int typval_compare(
|
|||||||
// For "is" a different type always means false, for "notis"
|
// For "is" a different type always means false, for "notis"
|
||||||
// it means true.
|
// it means true.
|
||||||
n1 = type == EXPR_ISNOT;
|
n1 = type == EXPR_ISNOT;
|
||||||
|
} else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) {
|
||||||
|
if (type_is) {
|
||||||
|
n1 = typ1->v_type == typ2->v_type
|
||||||
|
&& typ1->vval.v_blob == typ2->vval.v_blob;
|
||||||
|
if (type == EXPR_ISNOT) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
} else if (typ1->v_type != typ2->v_type
|
||||||
|
|| (type != EXPR_EQUAL && type != EXPR_NEQUAL)) {
|
||||||
|
if (typ1->v_type != typ2->v_type) {
|
||||||
|
EMSG(_("E977: Can only compare Blob with Blob"));
|
||||||
|
} else {
|
||||||
|
EMSG(_(e_invalblob));
|
||||||
|
}
|
||||||
|
tv_clear(typ1);
|
||||||
|
return FAIL;
|
||||||
|
} else {
|
||||||
|
// Compare two Blobs for being equal or unequal.
|
||||||
|
n1 = tv_blob_equal(typ1->vval.v_blob, typ2->vval.v_blob);
|
||||||
|
if (type == EXPR_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) {
|
} else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) {
|
||||||
if (type_is) {
|
if (type_is) {
|
||||||
n1 = typ1->v_type == typ2->v_type
|
n1 = typ1->v_type == typ2->v_type
|
||||||
|
@ -63,6 +63,7 @@ typedef struct lval_S {
|
|||||||
dict_T *ll_dict; ///< The Dictionary or NULL.
|
dict_T *ll_dict; ///< The Dictionary or NULL.
|
||||||
dictitem_T *ll_di; ///< The dictitem or NULL.
|
dictitem_T *ll_di; ///< The dictitem or NULL.
|
||||||
char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL.
|
char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL.
|
||||||
|
blob_T *ll_blob; ///< The Blob or NULL.
|
||||||
} lval_T;
|
} lval_T;
|
||||||
|
|
||||||
/// enum used by var_flavour()
|
/// enum used by var_flavour()
|
||||||
@ -154,6 +155,7 @@ typedef enum {
|
|||||||
VV_TYPE_DICT,
|
VV_TYPE_DICT,
|
||||||
VV_TYPE_FLOAT,
|
VV_TYPE_FLOAT,
|
||||||
VV_TYPE_BOOL,
|
VV_TYPE_BOOL,
|
||||||
|
VV_TYPE_BLOB,
|
||||||
VV_EVENT,
|
VV_EVENT,
|
||||||
VV_ECHOSPACE,
|
VV_ECHOSPACE,
|
||||||
VV_ARGV,
|
VV_ARGV,
|
||||||
@ -165,6 +167,7 @@ typedef enum {
|
|||||||
VV__NULL_STRING, // String with NULL value. For test purposes only.
|
VV__NULL_STRING, // String with NULL value. For test purposes only.
|
||||||
VV__NULL_LIST, // List with NULL value. For test purposes only.
|
VV__NULL_LIST, // List with NULL value. For test purposes only.
|
||||||
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
|
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
|
||||||
|
VV__NULL_BLOB, // Blob with NULL value. For test purposes only.
|
||||||
VV_LUA,
|
VV_LUA,
|
||||||
} VimVarIndex;
|
} VimVarIndex;
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ return {
|
|||||||
min={args=1, base=1},
|
min={args=1, base=1},
|
||||||
mkdir={args={1, 3}},
|
mkdir={args={1, 3}},
|
||||||
mode={args={0, 1}},
|
mode={args={0, 1}},
|
||||||
msgpackdump={args=1},
|
msgpackdump={args={1, 2}},
|
||||||
msgpackparse={args=1},
|
msgpackparse={args=1},
|
||||||
nextnonblank={args=1},
|
nextnonblank={args=1},
|
||||||
nr2char={args={1, 2}},
|
nr2char={args={1, 2}},
|
||||||
|
@ -246,14 +246,15 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv,
|
|||||||
/// Convert char* string to typval_T
|
/// Convert char* string to typval_T
|
||||||
///
|
///
|
||||||
/// Depending on whether string has (no) NUL bytes, it may use a special
|
/// Depending on whether string has (no) NUL bytes, it may use a special
|
||||||
/// dictionary or decode string to VAR_STRING.
|
/// dictionary, VAR_BLOB, or decode string to VAR_STRING.
|
||||||
///
|
///
|
||||||
/// @param[in] s String to decode.
|
/// @param[in] s String to decode.
|
||||||
/// @param[in] len String length.
|
/// @param[in] len String length.
|
||||||
/// @param[in] hasnul Whether string has NUL byte, not or it was not yet
|
/// @param[in] hasnul Whether string has NUL byte, not or it was not yet
|
||||||
/// determined.
|
/// determined.
|
||||||
/// @param[in] binary If true, save special string type as kMPBinary,
|
/// @param[in] binary Determines decode type if string has NUL bytes.
|
||||||
/// otherwise kMPString.
|
/// If true convert string to VAR_BLOB, otherwise to the
|
||||||
|
/// kMPString special type.
|
||||||
/// @param[in] s_allocated If true, then `s` was allocated and can be saved in
|
/// @param[in] s_allocated If true, then `s` was allocated and can be saved in
|
||||||
/// a returned structure. If it is not saved there, it
|
/// a returned structure. If it is not saved there, it
|
||||||
/// will be freed.
|
/// will be freed.
|
||||||
@ -269,21 +270,28 @@ typval_T decode_string(const char *const s, const size_t len,
|
|||||||
? ((s != NULL) && (memchr(s, NUL, len) != NULL))
|
? ((s != NULL) && (memchr(s, NUL, len) != NULL))
|
||||||
: (bool)hasnul);
|
: (bool)hasnul);
|
||||||
if (really_hasnul) {
|
if (really_hasnul) {
|
||||||
list_T *const list = tv_list_alloc(kListLenMayKnow);
|
|
||||||
tv_list_ref(list);
|
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
|
tv.v_lock = VAR_UNLOCKED;
|
||||||
.v_type = VAR_LIST,
|
if (binary) {
|
||||||
.v_lock = VAR_UNLOCKED,
|
tv_blob_alloc_ret(&tv);
|
||||||
.vval = { .v_list = list },
|
ga_concat_len(&tv.vval.v_blob->bv_ga, s, len);
|
||||||
}));
|
} else {
|
||||||
const int elw_ret = encode_list_write((void *)list, s, len);
|
list_T *const list = tv_list_alloc(kListLenMayKnow);
|
||||||
if (s_allocated) {
|
tv_list_ref(list);
|
||||||
xfree((void *)s);
|
create_special_dict(&tv, kMPString,
|
||||||
}
|
((typval_T){
|
||||||
if (elw_ret == -1) {
|
.v_type = VAR_LIST,
|
||||||
tv_clear(&tv);
|
.v_lock = VAR_UNLOCKED,
|
||||||
return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
|
.vval = { .v_list = list },
|
||||||
|
}));
|
||||||
|
const int elw_ret = encode_list_write((void *)list, s, len);
|
||||||
|
if (s_allocated) {
|
||||||
|
xfree((void *)s);
|
||||||
|
}
|
||||||
|
if (elw_ret == -1) {
|
||||||
|
tv_clear(&tv);
|
||||||
|
return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tv;
|
return tv;
|
||||||
} else {
|
} else {
|
||||||
|
@ -47,6 +47,14 @@ const char *const encode_special_var_names[] = {
|
|||||||
# include "eval/encode.c.generated.h"
|
# include "eval/encode.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Msgpack callback for writing to a Blob
|
||||||
|
int encode_blob_write(void *const data, const char *const buf, const size_t len)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
ga_concat_len(&((blob_T *)data)->bv_ga, buf, len);
|
||||||
|
return (int)len;
|
||||||
|
}
|
||||||
|
|
||||||
/// Msgpack callback for writing to readfile()-style list
|
/// Msgpack callback for writing to readfile()-style list
|
||||||
int encode_list_write(void *const data, const char *const buf, const size_t len)
|
int encode_list_write(void *const data, const char *const buf, const size_t len)
|
||||||
FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
@ -319,6 +327,30 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
const blob_T *const blob_ = (blob); \
|
||||||
|
const int len_ = (len); \
|
||||||
|
if (len_ == 0) { \
|
||||||
|
ga_concat(gap, "0z"); \
|
||||||
|
} else { \
|
||||||
|
/* Allocate space for "0z", the two hex chars per byte, and a */ \
|
||||||
|
/* "." separator after every eight hex chars. */ \
|
||||||
|
/* Example: "0z00112233.44556677.8899" */ \
|
||||||
|
ga_grow(gap, 2 + 2 * len_ + (len_ - 1) / 4); \
|
||||||
|
ga_concat(gap, "0z"); \
|
||||||
|
char numbuf[NUMBUFLEN]; \
|
||||||
|
for (int i_ = 0; i_ < len_; i_++) { \
|
||||||
|
if (i_ > 0 && (i_ & 3) == 0) { \
|
||||||
|
ga_append(gap, '.'); \
|
||||||
|
} \
|
||||||
|
vim_snprintf((char *)numbuf, ARRAY_SIZE(numbuf), "%02X", \
|
||||||
|
(int)tv_blob_get(blob_, i_)); \
|
||||||
|
ga_concat(gap, numbuf); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
||||||
do { \
|
do { \
|
||||||
char numbuf[NUMBUFLEN]; \
|
char numbuf[NUMBUFLEN]; \
|
||||||
@ -705,6 +737,28 @@ static inline int convert_to_json_string(garray_T *const gap,
|
|||||||
return FAIL; \
|
return FAIL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
const blob_T *const blob_ = (blob); \
|
||||||
|
const int len_ = (len); \
|
||||||
|
if (len_ == 0) { \
|
||||||
|
ga_concat(gap, "[]"); \
|
||||||
|
} else { \
|
||||||
|
ga_append(gap, '['); \
|
||||||
|
char numbuf[NUMBUFLEN]; \
|
||||||
|
for (int i_ = 0; i_ < len_; i_++) { \
|
||||||
|
if (i_ > 0) { \
|
||||||
|
ga_concat(gap, ", "); \
|
||||||
|
} \
|
||||||
|
vim_snprintf((char *)numbuf, ARRAY_SIZE(numbuf), "%d", \
|
||||||
|
(int)tv_blob_get(blob_, i_)); \
|
||||||
|
ga_concat(gap, numbuf); \
|
||||||
|
} \
|
||||||
|
ga_append(gap, ']'); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
return conv_error(_("E474: Error while dumping %s, %s: " \
|
return conv_error(_("E474: Error while dumping %s, %s: " \
|
||||||
@ -770,6 +824,7 @@ bool encode_check_json_key(const typval_T *const tv)
|
|||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
@ -904,6 +959,15 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
const size_t len_ = (size_t)(len); \
|
||||||
|
msgpack_pack_bin(packer, len_); \
|
||||||
|
if (len_ > 0) { \
|
||||||
|
msgpack_pack_bin_body(packer, (blob)->bv_ga.ga_data, len_); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
||||||
msgpack_pack_int64(packer, (int64_t)(num))
|
msgpack_pack_int64(packer, (int64_t)(num))
|
||||||
|
|
||||||
@ -982,6 +1046,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
|
@ -38,6 +38,20 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
|
|||||||
case VAR_SPECIAL: {
|
case VAR_SPECIAL: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
if (*op != '+' || tv2->v_type != VAR_BLOB) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Blob += Blob
|
||||||
|
if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) {
|
||||||
|
blob_T *const b1 = tv1->vval.v_blob;
|
||||||
|
blob_T *const b2 = tv2->vval.v_blob;
|
||||||
|
for (int i = 0; i < tv_blob_len(b2); i++) {
|
||||||
|
ga_append(&b1->bv_ga, (char)tv_blob_get(b2, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
if (*op != '+' || tv2->v_type != VAR_LIST) {
|
if (*op != '+' || tv2->v_type != VAR_LIST) {
|
||||||
break;
|
break;
|
||||||
|
@ -96,6 +96,7 @@ PRAGMA_DIAG_POP
|
|||||||
|
|
||||||
|
|
||||||
static char *e_listarg = N_("E686: Argument of %s must be a List");
|
static char *e_listarg = N_("E686: Argument of %s must be a List");
|
||||||
|
static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
|
||||||
static char *e_invalwindow = N_("E957: Invalid window number");
|
static char *e_invalwindow = N_("E957: Invalid window number");
|
||||||
|
|
||||||
/// Dummy va_list for passing to vim_snprintf
|
/// Dummy va_list for passing to vim_snprintf
|
||||||
@ -321,8 +322,20 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
tv_list_append_tv(l, &argvars[1]);
|
tv_list_append_tv(l, &argvars[1]);
|
||||||
tv_copy(&argvars[0], rettv);
|
tv_copy(&argvars[0], rettv);
|
||||||
}
|
}
|
||||||
|
} else if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
|
if (b != NULL
|
||||||
|
&& !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) {
|
||||||
|
bool error = false;
|
||||||
|
const varnumber_T n = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
ga_append(&b->bv_ga, (int)n);
|
||||||
|
tv_copy(&argvars[0], rettv);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
EMSG(_(e_listreq));
|
EMSG(_(e_listblobreq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -959,7 +972,17 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ptrdiff_t input_len = 0;
|
ptrdiff_t input_len = 0;
|
||||||
char *input = save_tv_as_string(&argvars[1], &input_len, false);
|
char *input = NULL;
|
||||||
|
if (argvars[1].v_type == VAR_BLOB) {
|
||||||
|
const blob_T *const b = argvars[1].vval.v_blob;
|
||||||
|
input_len = tv_blob_len(b);
|
||||||
|
if (input_len > 0) {
|
||||||
|
input = xmemdup(b->bv_ga.ga_data, input_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input = save_tv_as_string(&argvars[1], &input_len, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (!input) {
|
if (!input) {
|
||||||
// Either the error has been handled by save_tv_as_string(),
|
// Either the error has been handled by save_tv_as_string(),
|
||||||
// or there is no input to send.
|
// or there is no input to send.
|
||||||
@ -1874,6 +1897,10 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
n = argvars[0].vval.v_special == kSpecialVarNull;
|
n = argvars[0].vval.v_special == kSpecialVarNull;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
n = (tv_blob_len(argvars[0].vval.v_blob) == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
internal_error("f_empty(UNKNOWN)");
|
internal_error("f_empty(UNKNOWN)");
|
||||||
break;
|
break;
|
||||||
@ -2791,7 +2818,23 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
typval_T *tv = NULL;
|
typval_T *tv = NULL;
|
||||||
bool what_is_dict = false;
|
bool what_is_dict = false;
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_LIST) {
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
bool error = false;
|
||||||
|
int idx = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
rettv->v_type = VAR_NUMBER;
|
||||||
|
if (idx < 0) {
|
||||||
|
idx = tv_blob_len(argvars[0].vval.v_blob) + idx;
|
||||||
|
}
|
||||||
|
if (idx < 0 || idx >= tv_blob_len(argvars[0].vval.v_blob)) {
|
||||||
|
rettv->vval.v_number = -1;
|
||||||
|
} else {
|
||||||
|
rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx);
|
||||||
|
tv = rettv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (argvars[0].v_type == VAR_LIST) {
|
||||||
if ((l = argvars[0].vval.v_list) != NULL) {
|
if ((l = argvars[0].vval.v_list) != NULL) {
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
@ -2852,7 +2895,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EMSG2(_(e_listdictarg), "get()");
|
EMSG2(_(e_listdictblobarg), "get()");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tv == NULL) {
|
if (tv == NULL) {
|
||||||
@ -4791,8 +4834,38 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
bool ic = false;
|
bool ic = false;
|
||||||
|
|
||||||
rettv->vval.v_number = -1;
|
rettv->vval.v_number = -1;
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
EMSG(_(e_listreq));
|
bool error = false;
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
|
start = tv_get_number_chk(&argvars[2], &error);
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
|
if (b == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (start < 0) {
|
||||||
|
start = tv_blob_len(b) + start;
|
||||||
|
if (start < 0) {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (idx = start; idx < tv_blob_len(b); idx++) {
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_NUMBER;
|
||||||
|
tv.vval.v_number = tv_blob_get(b, idx);
|
||||||
|
if (tv_equal(&tv, &argvars[1], ic, false)) {
|
||||||
|
rettv->vval.v_number = idx;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
EMSG(_(e_listblobreq));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list_T *const l = argvars[0].vval.v_list;
|
list_T *const l = argvars[0].vval.v_list;
|
||||||
@ -4921,8 +4994,46 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
list_T *l;
|
list_T *l;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
EMSG2(_(e_listarg), "insert()");
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
|
|
||||||
|
if (b == NULL
|
||||||
|
|| var_check_lock(b->bv_lock, N_("insert() argument"),
|
||||||
|
TV_TRANSLATE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long before = 0;
|
||||||
|
const int len = tv_blob_len(b);
|
||||||
|
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
|
before = (long)tv_get_number_chk(&argvars[2], &error);
|
||||||
|
if (error) {
|
||||||
|
return; // type error; errmsg already given
|
||||||
|
}
|
||||||
|
if (before < 0 || before > len) {
|
||||||
|
EMSG2(_(e_invarg2), tv_get_string(&argvars[2]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const int val = tv_get_number_chk(&argvars[1], &error);
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val < 0 || val > 255) {
|
||||||
|
EMSG2(_(e_invarg2), tv_get_string(&argvars[1]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ga_grow(&b->bv_ga, 1);
|
||||||
|
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
||||||
|
memmove(p + before + 1, p + before, (size_t)len - before);
|
||||||
|
*(p + before) = val;
|
||||||
|
b->bv_ga.ga_len++;
|
||||||
|
|
||||||
|
tv_copy(&argvars[0], rettv);
|
||||||
|
} else if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
EMSG2(_(e_listblobarg), "insert()");
|
||||||
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
||||||
N_("insert() argument"), TV_TRANSLATE)) {
|
N_("insert() argument"), TV_TRANSLATE)) {
|
||||||
long before = 0;
|
long before = 0;
|
||||||
@ -5582,6 +5693,10 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
tv_get_string(&argvars[0]));
|
tv_get_string(&argvars[0]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
rettv->vval.v_number = tv_blob_len(argvars[0].vval.v_blob);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list);
|
rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list);
|
||||||
break;
|
break;
|
||||||
@ -6392,9 +6507,16 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
EMSG2(_(e_listarg), "msgpackdump()");
|
EMSG2(_(e_listarg), "msgpackdump()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
|
|
||||||
list_T *const list = argvars[0].vval.v_list;
|
list_T *const list = argvars[0].vval.v_list;
|
||||||
msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);
|
msgpack_packer *packer;
|
||||||
|
if (argvars[1].v_type != VAR_UNKNOWN
|
||||||
|
&& strequal(tv_get_string(&argvars[1]), "B")) {
|
||||||
|
tv_blob_alloc_ret(rettv);
|
||||||
|
packer = msgpack_packer_new(rettv->vval.v_blob, &encode_blob_write);
|
||||||
|
} else {
|
||||||
|
packer = msgpack_packer_new(tv_list_alloc_ret(rettv, kListLenMayKnow),
|
||||||
|
&encode_list_write);
|
||||||
|
}
|
||||||
const char *const msg = _("msgpackdump() argument, index %i");
|
const char *const msg = _("msgpackdump() argument, index %i");
|
||||||
// Assume that translation will not take more then 4 times more space
|
// Assume that translation will not take more then 4 times more space
|
||||||
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
|
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
|
||||||
@ -6402,23 +6524,50 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
TV_LIST_ITER(list, li, {
|
TV_LIST_ITER(list, li, {
|
||||||
vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx);
|
vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx);
|
||||||
idx++;
|
idx++;
|
||||||
if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
|
if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
msgpack_packer_free(lpacker);
|
msgpack_packer_free(packer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "msgpackparse" function
|
static int msgpackparse_convert_item(const msgpack_object data,
|
||||||
static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
const msgpack_unpack_return result,
|
||||||
|
list_T *const ret_list,
|
||||||
|
const bool fail_if_incomplete)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
switch (result) {
|
||||||
EMSG2(_(e_listarg), "msgpackparse()");
|
case MSGPACK_UNPACK_PARSE_ERROR:
|
||||||
return;
|
EMSG2(_(e_invarg2), "Failed to parse msgpack string");
|
||||||
|
return FAIL;
|
||||||
|
case MSGPACK_UNPACK_NOMEM_ERROR:
|
||||||
|
EMSG(_(e_outofmem));
|
||||||
|
return FAIL;
|
||||||
|
case MSGPACK_UNPACK_CONTINUE:
|
||||||
|
if (fail_if_incomplete) {
|
||||||
|
EMSG2(_(e_invarg2), "Incomplete msgpack string");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return NOTDONE;
|
||||||
|
case MSGPACK_UNPACK_SUCCESS: {
|
||||||
|
typval_T tv = { .v_type = VAR_UNKNOWN };
|
||||||
|
if (msgpack_to_vim(data, &tv) == FAIL) {
|
||||||
|
EMSG2(_(e_invarg2), "Failed to convert msgpack string");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
tv_list_append_owned_tv(ret_list, tv);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
|
}
|
||||||
const list_T *const list = argvars[0].vval.v_list;
|
|
||||||
|
static void msgpackparse_unpack_list(const list_T *const list,
|
||||||
|
list_T *const ret_list)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(2)
|
||||||
|
{
|
||||||
if (tv_list_len(list) == 0) {
|
if (tv_list_len(list) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -6437,43 +6586,28 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
do {
|
do {
|
||||||
if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) {
|
if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) {
|
||||||
EMSG(_(e_outofmem));
|
EMSG(_(e_outofmem));
|
||||||
goto f_msgpackparse_exit;
|
goto end;
|
||||||
}
|
}
|
||||||
size_t read_bytes;
|
size_t read_bytes;
|
||||||
const int rlret = encode_read_from_list(
|
const int rlret = encode_read_from_list(
|
||||||
&lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes);
|
&lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes);
|
||||||
if (rlret == FAIL) {
|
if (rlret == FAIL) {
|
||||||
EMSG2(_(e_invarg2), "List item is not a string");
|
EMSG2(_(e_invarg2), "List item is not a string");
|
||||||
goto f_msgpackparse_exit;
|
goto end;
|
||||||
}
|
}
|
||||||
msgpack_unpacker_buffer_consumed(unpacker, read_bytes);
|
msgpack_unpacker_buffer_consumed(unpacker, read_bytes);
|
||||||
if (read_bytes == 0) {
|
if (read_bytes == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while (unpacker->off < unpacker->used) {
|
while (unpacker->off < unpacker->used) {
|
||||||
const msgpack_unpack_return result = msgpack_unpacker_next(unpacker,
|
const msgpack_unpack_return result
|
||||||
&unpacked);
|
= msgpack_unpacker_next(unpacker, &unpacked);
|
||||||
if (result == MSGPACK_UNPACK_PARSE_ERROR) {
|
const int conv_result = msgpackparse_convert_item(unpacked.data, result,
|
||||||
EMSG2(_(e_invarg2), "Failed to parse msgpack string");
|
ret_list, rlret == OK);
|
||||||
goto f_msgpackparse_exit;
|
if (conv_result == NOTDONE) {
|
||||||
}
|
|
||||||
if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
|
|
||||||
EMSG(_(e_outofmem));
|
|
||||||
goto f_msgpackparse_exit;
|
|
||||||
}
|
|
||||||
if (result == MSGPACK_UNPACK_SUCCESS) {
|
|
||||||
typval_T tv = { .v_type = VAR_UNKNOWN };
|
|
||||||
if (msgpack_to_vim(unpacked.data, &tv) == FAIL) {
|
|
||||||
EMSG2(_(e_invarg2), "Failed to convert msgpack string");
|
|
||||||
goto f_msgpackparse_exit;
|
|
||||||
}
|
|
||||||
tv_list_append_owned_tv(ret_list, tv);
|
|
||||||
}
|
|
||||||
if (result == MSGPACK_UNPACK_CONTINUE) {
|
|
||||||
if (rlret == OK) {
|
|
||||||
EMSG2(_(e_invarg2), "Incomplete msgpack string");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
} else if (conv_result == FAIL) {
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rlret == OK) {
|
if (rlret == OK) {
|
||||||
@ -6481,10 +6615,47 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
f_msgpackparse_exit:
|
end:
|
||||||
msgpack_unpacked_destroy(&unpacked);
|
|
||||||
msgpack_unpacker_free(unpacker);
|
msgpack_unpacker_free(unpacker);
|
||||||
return;
|
msgpack_unpacked_destroy(&unpacked);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msgpackparse_unpack_blob(const blob_T *const blob,
|
||||||
|
list_T *const ret_list)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(2)
|
||||||
|
{
|
||||||
|
const int len = tv_blob_len(blob);
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msgpack_unpacked unpacked;
|
||||||
|
msgpack_unpacked_init(&unpacked);
|
||||||
|
for (size_t offset = 0; offset < (size_t)len;) {
|
||||||
|
const msgpack_unpack_return result
|
||||||
|
= msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset);
|
||||||
|
if (msgpackparse_convert_item(unpacked.data, result, ret_list, true)
|
||||||
|
!= OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msgpack_unpacked_destroy(&unpacked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "msgpackparse" function
|
||||||
|
static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
|
||||||
|
EMSG2(_(e_listblobarg), "msgpackparse()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
|
||||||
|
if (argvars[0].v_type == VAR_LIST) {
|
||||||
|
msgpackparse_unpack_list(argvars[0].vval.v_list, ret_list);
|
||||||
|
} else {
|
||||||
|
msgpackparse_unpack_blob(argvars[0].vval.v_blob, ret_list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6895,6 +7066,7 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
bool binary = false;
|
bool binary = false;
|
||||||
|
bool blob = false;
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
|
char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
|
||||||
int io_size = sizeof(buf);
|
int io_size = sizeof(buf);
|
||||||
@ -6907,22 +7079,41 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
if (argvars[1].v_type != VAR_UNKNOWN) {
|
if (argvars[1].v_type != VAR_UNKNOWN) {
|
||||||
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
|
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
|
||||||
binary = true;
|
binary = true;
|
||||||
|
} else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) {
|
||||||
|
blob = true;
|
||||||
}
|
}
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
maxline = tv_get_number(&argvars[2]);
|
maxline = tv_get_number(&argvars[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
|
|
||||||
|
|
||||||
// Always open the file in binary mode, library functions have a mind of
|
// Always open the file in binary mode, library functions have a mind of
|
||||||
// their own about CR-LF conversion.
|
// their own about CR-LF conversion.
|
||||||
const char *const fname = tv_get_string(&argvars[0]);
|
const char *const fname = tv_get_string(&argvars[0]);
|
||||||
|
|
||||||
|
if (os_isdir((const char_u *)fname)) {
|
||||||
|
EMSG2(_(e_isadir2), fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (*fname == NUL || (fd = os_fopen(fname, READBIN)) == NULL) {
|
if (*fname == NUL || (fd = os_fopen(fname, READBIN)) == NULL) {
|
||||||
EMSG2(_(e_notopen), *fname == NUL ? _("<empty>") : fname);
|
EMSG2(_(e_notopen), *fname == NUL ? _("<empty>") : fname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blob) {
|
||||||
|
tv_blob_alloc_ret(rettv);
|
||||||
|
if (!read_blob(fd, rettv->vval.v_blob)) {
|
||||||
|
EMSG2(_(e_notread), fname);
|
||||||
|
// An empty blob is returned on error.
|
||||||
|
tv_blob_free(rettv->vval.v_blob);
|
||||||
|
rettv->vval.v_blob = NULL;
|
||||||
|
}
|
||||||
|
fclose(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
|
||||||
|
|
||||||
while (maxline < 0 || tv_list_len(l) < maxline) {
|
while (maxline < 0 || tv_list_len(l) < maxline) {
|
||||||
readlen = (int)fread(buf, 1, io_size, fd);
|
readlen = (int)fread(buf, 1, io_size, fd);
|
||||||
|
|
||||||
@ -7191,8 +7382,64 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
|
|
||||||
|
if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
const int len = tv_blob_len(b);
|
||||||
|
|
||||||
|
if (idx < 0) {
|
||||||
|
// count from the end
|
||||||
|
idx = len + idx;
|
||||||
|
}
|
||||||
|
if (idx < 0 || idx >= len) {
|
||||||
|
EMSGN(_(e_blobidx), idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argvars[2].v_type == VAR_UNKNOWN) {
|
||||||
|
// Remove one item, return its value.
|
||||||
|
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
||||||
|
rettv->vval.v_number = (varnumber_T)(*(p + idx));
|
||||||
|
memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
||||||
|
b->bv_ga.ga_len--;
|
||||||
|
} else {
|
||||||
|
// Remove range of items, return blob with values.
|
||||||
|
end = (long)tv_get_number_chk(&argvars[2], &error);
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (end < 0) {
|
||||||
|
// count from the end
|
||||||
|
end = len + end;
|
||||||
|
}
|
||||||
|
if (end >= len || idx > end) {
|
||||||
|
EMSGN(_(e_blobidx), end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
blob_T *const blob = tv_blob_alloc();
|
||||||
|
blob->bv_ga.ga_len = end - idx + 1;
|
||||||
|
ga_grow(&blob->bv_ga, end - idx + 1);
|
||||||
|
|
||||||
|
char_u *const p = (char_u *)b->bv_ga.ga_data;
|
||||||
|
memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
||||||
|
(size_t)(end - idx + 1));
|
||||||
|
tv_blob_set_ret(rettv, blob);
|
||||||
|
|
||||||
|
if (len - end - 1 > 0) {
|
||||||
|
memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
|
||||||
|
}
|
||||||
|
b->bv_ga.ga_len -= end - idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (argvars[0].v_type != VAR_LIST) {
|
} else if (argvars[0].v_type != VAR_LIST) {
|
||||||
EMSG2(_(e_listdictarg), "remove()");
|
EMSG2(_(e_listdictblobarg), "remove()");
|
||||||
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
||||||
arg_errmsg, TV_TRANSLATE)) {
|
arg_errmsg, TV_TRANSLATE)) {
|
||||||
bool error = false;
|
bool error = false;
|
||||||
@ -7466,13 +7713,25 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
*/
|
*/
|
||||||
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
list_T *l;
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
blob_T *const b = argvars[0].vval.v_blob;
|
||||||
EMSG2(_(e_listarg), "reverse()");
|
const int len = tv_blob_len(b);
|
||||||
} else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
|
|
||||||
N_("reverse() argument"), TV_TRANSLATE)) {
|
for (int i = 0; i < len / 2; i++) {
|
||||||
tv_list_reverse(l);
|
const char_u tmp = tv_blob_get(b, i);
|
||||||
tv_list_set_ret(rettv, l);
|
tv_blob_set(b, i, tv_blob_get(b, len - i - 1));
|
||||||
|
tv_blob_set(b, len - i - 1, tmp);
|
||||||
|
}
|
||||||
|
tv_blob_set_ret(rettv, b);
|
||||||
|
} else if (argvars[0].v_type != VAR_LIST) {
|
||||||
|
EMSG2(_(e_listblobarg), "reverse()");
|
||||||
|
} else {
|
||||||
|
list_T *const l = argvars[0].vval.v_list;
|
||||||
|
if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"),
|
||||||
|
TV_TRANSLATE)) {
|
||||||
|
tv_list_reverse(l);
|
||||||
|
tv_list_set_ret(rettv, l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11292,15 +11551,16 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
int n = -1;
|
int n = -1;
|
||||||
|
|
||||||
switch (argvars[0].v_type) {
|
switch (argvars[0].v_type) {
|
||||||
case VAR_NUMBER: n = VAR_TYPE_NUMBER; break;
|
case VAR_NUMBER: n = VAR_TYPE_NUMBER; break;
|
||||||
case VAR_STRING: n = VAR_TYPE_STRING; break;
|
case VAR_STRING: n = VAR_TYPE_STRING; break;
|
||||||
case VAR_PARTIAL:
|
case VAR_PARTIAL:
|
||||||
case VAR_FUNC: n = VAR_TYPE_FUNC; break;
|
case VAR_FUNC: n = VAR_TYPE_FUNC; break;
|
||||||
case VAR_LIST: n = VAR_TYPE_LIST; break;
|
case VAR_LIST: n = VAR_TYPE_LIST; break;
|
||||||
case VAR_DICT: n = VAR_TYPE_DICT; break;
|
case VAR_DICT: n = VAR_TYPE_DICT; break;
|
||||||
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
|
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
|
||||||
case VAR_BOOL: n = VAR_TYPE_BOOL; break;
|
case VAR_BOOL: n = VAR_TYPE_BOOL; break;
|
||||||
case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break;
|
case VAR_SPECIAL: n = VAR_TYPE_SPECIAL; break;
|
||||||
|
case VAR_BLOB: n = VAR_TYPE_BLOB; break;
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
internal_error("f_type(UNKNOWN)");
|
internal_error("f_type(UNKNOWN)");
|
||||||
break;
|
break;
|
||||||
@ -11679,16 +11939,17 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type == VAR_LIST) {
|
||||||
EMSG2(_(e_listarg), "writefile()");
|
TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
|
||||||
|
if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (argvars[0].v_type != VAR_BLOB) {
|
||||||
|
EMSG2(_(e_invarg2),
|
||||||
|
_("writefile() first argument must be a List or a Blob"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const list_T *const list = argvars[0].vval.v_list;
|
|
||||||
TV_LIST_ITER_CONST(list, li, {
|
|
||||||
if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bool binary = false;
|
bool binary = false;
|
||||||
bool append = false;
|
bool append = false;
|
||||||
@ -11728,7 +11989,13 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
emsgf(_("E482: Can't open file %s for writing: %s"),
|
emsgf(_("E482: Can't open file %s for writing: %s"),
|
||||||
fname, os_strerror(error));
|
fname, os_strerror(error));
|
||||||
} else {
|
} else {
|
||||||
if (write_list(&fp, list, binary)) {
|
bool write_ok;
|
||||||
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
|
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
|
||||||
|
} else {
|
||||||
|
write_ok = write_list(&fp, argvars[0].vval.v_list, binary);
|
||||||
|
}
|
||||||
|
if (write_ok) {
|
||||||
rettv->vval.v_number = 0;
|
rettv->vval.v_number = 0;
|
||||||
}
|
}
|
||||||
if ((error = file_close(&fp, do_fsync)) != 0) {
|
if ((error = file_close(&fp, do_fsync)) != 0) {
|
||||||
|
@ -2125,6 +2125,77 @@ void tv_dict_set_keys_readonly(dict_T *const dict)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//{{{1 Blobs
|
||||||
|
//{{{2 Alloc/free
|
||||||
|
|
||||||
|
/// Allocate an empty blob.
|
||||||
|
///
|
||||||
|
/// Caller should take care of the reference count.
|
||||||
|
///
|
||||||
|
/// @return [allocated] new blob.
|
||||||
|
blob_T *tv_blob_alloc(void)
|
||||||
|
FUNC_ATTR_NONNULL_RET
|
||||||
|
{
|
||||||
|
blob_T *const blob = xcalloc(1, sizeof(blob_T));
|
||||||
|
ga_init(&blob->bv_ga, 1, 100);
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free a blob. Ignores the reference count.
|
||||||
|
///
|
||||||
|
/// @param[in,out] b Blob to free.
|
||||||
|
void tv_blob_free(blob_T *const b)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
ga_clear(&b->bv_ga);
|
||||||
|
xfree(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unreference a blob.
|
||||||
|
///
|
||||||
|
/// Decrements the reference count and frees blob when it becomes zero.
|
||||||
|
///
|
||||||
|
/// @param[in,out] b Blob to operate on.
|
||||||
|
void tv_blob_unref(blob_T *const b)
|
||||||
|
{
|
||||||
|
if (b != NULL && --b->bv_refcount <= 0) {
|
||||||
|
tv_blob_free(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//{{{2 Operations on the whole blob
|
||||||
|
|
||||||
|
/// Check whether two blobs are equal.
|
||||||
|
///
|
||||||
|
/// @param[in] b1 First blob.
|
||||||
|
/// @param[in] b2 Second blob.
|
||||||
|
///
|
||||||
|
/// @return true if blobs are equal, false otherwise.
|
||||||
|
bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2)
|
||||||
|
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
|
{
|
||||||
|
const int len1 = tv_blob_len(b1);
|
||||||
|
const int len2 = tv_blob_len(b2);
|
||||||
|
|
||||||
|
// empty and NULL are considered the same
|
||||||
|
if (len1 == 0 && len2 == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (b1 == b2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (len1 != len2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < b1->bv_ga.ga_len; i++) {
|
||||||
|
if (tv_blob_get(b1, i) != tv_blob_get(b2, i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//{{{1 Generic typval operations
|
//{{{1 Generic typval operations
|
||||||
//{{{2 Init/alloc/clear
|
//{{{2 Init/alloc/clear
|
||||||
//{{{3 Alloc
|
//{{{3 Alloc
|
||||||
@ -2169,6 +2240,44 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
|
|||||||
tv_dict_set_ret(ret_tv, d);
|
tv_dict_set_ret(ret_tv, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate an empty blob for a return value.
|
||||||
|
///
|
||||||
|
/// Also sets reference count.
|
||||||
|
///
|
||||||
|
/// @param[out] ret_tv Structure where blob is saved.
|
||||||
|
void tv_blob_alloc_ret(typval_T *const ret_tv)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
blob_T *const b = tv_blob_alloc();
|
||||||
|
tv_blob_set_ret(ret_tv, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy a blob typval to a different typval.
|
||||||
|
///
|
||||||
|
/// @param[in] from Blob object to copy from.
|
||||||
|
/// @param[out] to Blob object to copy to.
|
||||||
|
void tv_blob_copy(typval_T *const from, typval_T *const to)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
assert(from->v_type == VAR_BLOB);
|
||||||
|
|
||||||
|
to->v_type = VAR_BLOB;
|
||||||
|
to->v_lock = VAR_UNLOCKED;
|
||||||
|
if (from->vval.v_blob == NULL) {
|
||||||
|
to->vval.v_blob = NULL;
|
||||||
|
} else {
|
||||||
|
tv_blob_alloc_ret(to);
|
||||||
|
int len = from->vval.v_blob->bv_ga.ga_len;
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
to->vval.v_blob->bv_ga.ga_data
|
||||||
|
= xmemdup(from->vval.v_blob->bv_ga.ga_data, (size_t)len);
|
||||||
|
}
|
||||||
|
to->vval.v_blob->bv_ga.ga_len = len;
|
||||||
|
to->vval.v_blob->bv_ga.ga_maxlen = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//{{{3 Clear
|
//{{{3 Clear
|
||||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
||||||
|
|
||||||
@ -2210,6 +2319,13 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
|
|||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
tv_blob_unref(tv->vval.v_blob); \
|
||||||
|
tv->vval.v_blob = NULL; \
|
||||||
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
static inline int _nothing_conv_func_start(typval_T *const tv,
|
static inline int _nothing_conv_func_start(typval_T *const tv,
|
||||||
char_u *const fun)
|
char_u *const fun)
|
||||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
|
||||||
@ -2392,6 +2508,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv,
|
|||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||||
@ -2449,6 +2566,10 @@ void tv_free(typval_T *tv)
|
|||||||
xfree(tv->vval.v_string);
|
xfree(tv->vval.v_string);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
tv_blob_unref(tv->vval.v_blob);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
tv_list_unref(tv->vval.v_list);
|
tv_list_unref(tv->vval.v_list);
|
||||||
break;
|
break;
|
||||||
@ -2509,6 +2630,12 @@ void tv_copy(const typval_T *const from, typval_T *const to)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
if (from->vval.v_blob != NULL) {
|
||||||
|
to->vval.v_blob->bv_refcount++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
tv_list_ref(to->vval.v_list);
|
tv_list_ref(to->vval.v_list);
|
||||||
break;
|
break;
|
||||||
@ -2533,7 +2660,10 @@ void tv_copy(const typval_T *const from, typval_T *const to)
|
|||||||
/// @param[out] tv Item to (un)lock.
|
/// @param[out] tv Item to (un)lock.
|
||||||
/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything.
|
/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything.
|
||||||
/// @param[in] lock True if it is needed to lock an item, false to unlock.
|
/// @param[in] lock True if it is needed to lock an item, false to unlock.
|
||||||
void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
|
/// @param[in] check_refcount If true, do not lock a list or dict with a
|
||||||
|
/// reference count larger than 1.
|
||||||
|
void tv_item_lock(typval_T *const tv, const int deep, const bool lock,
|
||||||
|
const bool check_refcount)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
// TODO(ZyX-I): Make this not recursive
|
// TODO(ZyX-I): Make this not recursive
|
||||||
@ -2560,14 +2690,21 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
|
|||||||
CHANGE_LOCK(lock, tv->v_lock);
|
CHANGE_LOCK(lock, tv->v_lock);
|
||||||
|
|
||||||
switch (tv->v_type) {
|
switch (tv->v_type) {
|
||||||
|
case VAR_BLOB: {
|
||||||
|
blob_T *const b = tv->vval.v_blob;
|
||||||
|
if (b != NULL && !(check_refcount && b->bv_refcount > 1)) {
|
||||||
|
CHANGE_LOCK(lock, b->bv_lock);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST: {
|
case VAR_LIST: {
|
||||||
list_T *const l = tv->vval.v_list;
|
list_T *const l = tv->vval.v_list;
|
||||||
if (l != NULL) {
|
if (l != NULL && !(check_refcount && l->lv_refcount > 1)) {
|
||||||
CHANGE_LOCK(lock, l->lv_lock);
|
CHANGE_LOCK(lock, l->lv_lock);
|
||||||
if (deep < 0 || deep > 1) {
|
if (deep < 0 || deep > 1) {
|
||||||
// Recursive: lock/unlock the items the List contains.
|
// Recursive: lock/unlock the items the List contains.
|
||||||
TV_LIST_ITER(l, li, {
|
TV_LIST_ITER(l, li, {
|
||||||
tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock);
|
tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock, check_refcount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2575,12 +2712,12 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
|
|||||||
}
|
}
|
||||||
case VAR_DICT: {
|
case VAR_DICT: {
|
||||||
dict_T *const d = tv->vval.v_dict;
|
dict_T *const d = tv->vval.v_dict;
|
||||||
if (d != NULL) {
|
if (d != NULL && !(check_refcount && d->dv_refcount > 1)) {
|
||||||
CHANGE_LOCK(lock, d->dv_lock);
|
CHANGE_LOCK(lock, d->dv_lock);
|
||||||
if (deep < 0 || deep > 1) {
|
if (deep < 0 || deep > 1) {
|
||||||
// recursive: lock/unlock the items the List contains
|
// recursive: lock/unlock the items the List contains
|
||||||
TV_DICT_ITER(d, di, {
|
TV_DICT_ITER(d, di, {
|
||||||
tv_item_lock(&di->di_tv, deep - 1, lock);
|
tv_item_lock(&di->di_tv, deep - 1, lock, check_refcount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2646,10 +2783,11 @@ bool tv_check_lock(const typval_T *tv, const char *name,
|
|||||||
VarLockStatus lock = VAR_UNLOCKED;
|
VarLockStatus lock = VAR_UNLOCKED;
|
||||||
|
|
||||||
switch (tv->v_type) {
|
switch (tv->v_type) {
|
||||||
// case VAR_BLOB:
|
case VAR_BLOB:
|
||||||
// if (tv->vval.v_blob != NULL)
|
if (tv->vval.v_blob != NULL) {
|
||||||
// lock = tv->vval.v_blob->bv_lock;
|
lock = tv->vval.v_blob->bv_lock;
|
||||||
// break;
|
}
|
||||||
|
break;
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
if (tv->vval.v_list != NULL) {
|
if (tv->vval.v_list != NULL) {
|
||||||
lock = tv->vval.v_list->lv_lock;
|
lock = tv->vval.v_list->lv_lock;
|
||||||
@ -2769,6 +2907,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic,
|
|||||||
recursive_cnt--;
|
recursive_cnt--;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
return tv_blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
|
||||||
|
}
|
||||||
case VAR_NUMBER: {
|
case VAR_NUMBER: {
|
||||||
return tv1->vval.v_number == tv2->vval.v_number;
|
return tv1->vval.v_number == tv2->vval.v_number;
|
||||||
}
|
}
|
||||||
@ -2835,6 +2976,10 @@ bool tv_check_str_or_nr(const typval_T *const tv)
|
|||||||
EMSG(_("E728: Expected a Number or a String, Dictionary found"));
|
EMSG(_("E728: Expected a Number or a String, Dictionary found"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
EMSG(_("E974: Expected a Number or a String, Blob found"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case VAR_BOOL: {
|
case VAR_BOOL: {
|
||||||
EMSG(_("E5299: Expected a Number or a String, Boolean found"));
|
EMSG(_("E5299: Expected a Number or a String, Boolean found"));
|
||||||
return false;
|
return false;
|
||||||
@ -2860,6 +3005,7 @@ static const char *const num_errors[] = {
|
|||||||
[VAR_LIST]=N_("E745: Using a List as a Number"),
|
[VAR_LIST]=N_("E745: Using a List as a Number"),
|
||||||
[VAR_DICT]=N_("E728: Using a Dictionary as a Number"),
|
[VAR_DICT]=N_("E728: Using a Dictionary as a Number"),
|
||||||
[VAR_FLOAT]=N_("E805: Using a Float as a Number"),
|
[VAR_FLOAT]=N_("E805: Using a Float as a Number"),
|
||||||
|
[VAR_BLOB]=N_("E974: Using a Blob as a Number"),
|
||||||
[VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"),
|
[VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2888,6 +3034,7 @@ bool tv_check_num(const typval_T *const tv)
|
|||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
case VAR_BLOB:
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
EMSG(_(num_errors[tv->v_type]));
|
EMSG(_(num_errors[tv->v_type]));
|
||||||
return false;
|
return false;
|
||||||
@ -2905,6 +3052,7 @@ static const char *const str_errors[] = {
|
|||||||
[VAR_LIST]=N_("E730: using List as a String"),
|
[VAR_LIST]=N_("E730: using List as a String"),
|
||||||
[VAR_DICT]=N_("E731: using Dictionary as a String"),
|
[VAR_DICT]=N_("E731: using Dictionary as a String"),
|
||||||
[VAR_FLOAT]=((const char *)e_float_as_string),
|
[VAR_FLOAT]=((const char *)e_float_as_string),
|
||||||
|
[VAR_BLOB]=N_("E976: using Blob as a String"),
|
||||||
[VAR_UNKNOWN]=N_("E908: using an invalid value as a String"),
|
[VAR_UNKNOWN]=N_("E908: using an invalid value as a String"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2933,6 +3081,7 @@ bool tv_check_str(const typval_T *const tv)
|
|||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
case VAR_BLOB:
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
EMSG(_(str_errors[tv->v_type]));
|
EMSG(_(str_errors[tv->v_type]));
|
||||||
return false;
|
return false;
|
||||||
@ -2980,6 +3129,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
|
|||||||
case VAR_PARTIAL:
|
case VAR_PARTIAL:
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
|
case VAR_BLOB:
|
||||||
case VAR_FLOAT: {
|
case VAR_FLOAT: {
|
||||||
EMSG(_(num_errors[tv->v_type]));
|
EMSG(_(num_errors[tv->v_type]));
|
||||||
break;
|
break;
|
||||||
@ -3075,6 +3225,10 @@ float_T tv_get_float(const typval_T *const tv)
|
|||||||
EMSG(_("E907: Using a special value as a Float"));
|
EMSG(_("E907: Using a special value as a Float"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
EMSG(_("E975: Using a Blob as a Float"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
emsgf(_(e_intern2), "tv_get_float(UNKNOWN)");
|
emsgf(_(e_intern2), "tv_get_float(UNKNOWN)");
|
||||||
break;
|
break;
|
||||||
@ -3134,6 +3288,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
|
|||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
case VAR_BLOB:
|
||||||
case VAR_UNKNOWN: {
|
case VAR_UNKNOWN: {
|
||||||
EMSG(_(str_errors[tv->v_type]));
|
EMSG(_(str_errors[tv->v_type]));
|
||||||
return false;
|
return false;
|
||||||
|
@ -64,6 +64,7 @@ enum ListLenSpecials {
|
|||||||
typedef struct listvar_S list_T;
|
typedef struct listvar_S list_T;
|
||||||
typedef struct dictvar_S dict_T;
|
typedef struct dictvar_S dict_T;
|
||||||
typedef struct partial_S partial_T;
|
typedef struct partial_S partial_T;
|
||||||
|
typedef struct blobvar_S blob_T;
|
||||||
|
|
||||||
typedef struct ufunc ufunc_T;
|
typedef struct ufunc ufunc_T;
|
||||||
|
|
||||||
@ -123,6 +124,7 @@ typedef enum {
|
|||||||
VAR_SPECIAL, ///< Special value (null), .v_special
|
VAR_SPECIAL, ///< Special value (null), .v_special
|
||||||
///< is used.
|
///< is used.
|
||||||
VAR_PARTIAL, ///< Partial, .v_partial is used.
|
VAR_PARTIAL, ///< Partial, .v_partial is used.
|
||||||
|
VAR_BLOB, ///< Blob, .v_blob is used.
|
||||||
} VarType;
|
} VarType;
|
||||||
|
|
||||||
/// Structure that holds an internal variable value
|
/// Structure that holds an internal variable value
|
||||||
@ -138,6 +140,7 @@ typedef struct {
|
|||||||
list_T *v_list; ///< List for VAR_LIST, 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.
|
dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
|
||||||
partial_T *v_partial; ///< Closure: function with args.
|
partial_T *v_partial; ///< Closure: function with args.
|
||||||
|
blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL.
|
||||||
} vval; ///< Actual value.
|
} vval; ///< Actual value.
|
||||||
} typval_T;
|
} typval_T;
|
||||||
|
|
||||||
@ -252,6 +255,13 @@ struct dictvar_S {
|
|||||||
LuaRef lua_table_ref;
|
LuaRef lua_table_ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Structure to hold info about a Blob
|
||||||
|
struct blobvar_S {
|
||||||
|
garray_T bv_ga; ///< Growarray with the data.
|
||||||
|
int bv_refcount; ///< Reference count.
|
||||||
|
VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED.
|
||||||
|
};
|
||||||
|
|
||||||
/// Type used for script ID
|
/// Type used for script ID
|
||||||
typedef int scid_T;
|
typedef int scid_T;
|
||||||
/// Format argument for scid_T
|
/// Format argument for scid_T
|
||||||
@ -711,6 +721,65 @@ static inline bool tv_dict_is_watched(const dict_T *const d)
|
|||||||
return d && !QUEUE_EMPTY(&d->watchers);
|
return d && !QUEUE_EMPTY(&d->watchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b)
|
||||||
|
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1);
|
||||||
|
|
||||||
|
/// Set a blob as the return value.
|
||||||
|
///
|
||||||
|
/// Increments the reference count.
|
||||||
|
///
|
||||||
|
/// @param[out] tv Object to receive the blob.
|
||||||
|
/// @param[in,out] b Blob to pass to the object.
|
||||||
|
static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b)
|
||||||
|
{
|
||||||
|
tv->v_type = VAR_BLOB;
|
||||||
|
tv->vval.v_blob = b;
|
||||||
|
if (b != NULL) {
|
||||||
|
b->bv_refcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int tv_blob_len(const blob_T *const b)
|
||||||
|
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/// Get the length of the data in the blob, in bytes.
|
||||||
|
///
|
||||||
|
/// @param[in] b Blob to check.
|
||||||
|
static inline int tv_blob_len(const blob_T *const b)
|
||||||
|
{
|
||||||
|
if (b == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return b->bv_ga.ga_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char_u tv_blob_get(const blob_T *const b, int idx)
|
||||||
|
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/// Get the byte at index `idx` in the blob.
|
||||||
|
///
|
||||||
|
/// @param[in] b Blob to index. Cannot be NULL.
|
||||||
|
/// @param[in] idx Index in a blob. Must be valid.
|
||||||
|
///
|
||||||
|
/// @return Byte value at the given index.
|
||||||
|
static inline char_u tv_blob_get(const blob_T *const b, int idx)
|
||||||
|
{
|
||||||
|
return ((char_u *)b->bv_ga.ga_data)[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tv_blob_set(blob_T *const b, int idx, char_u c)
|
||||||
|
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
|
||||||
|
|
||||||
|
/// Store the byte `c` at index `idx` in the blob.
|
||||||
|
///
|
||||||
|
/// @param[in] b Blob to index. Cannot be NULL.
|
||||||
|
/// @param[in] idx Index in a blob. Must be valid.
|
||||||
|
/// @param[in] c Value to store.
|
||||||
|
static inline void tv_blob_set(blob_T *const b, int idx, char_u c)
|
||||||
|
{
|
||||||
|
((char_u *)b->bv_ga.ga_data)[idx] = c;
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize VimL object
|
/// Initialize VimL object
|
||||||
///
|
///
|
||||||
/// Initializes to unlocked VAR_UNKNOWN object.
|
/// Initializes to unlocked VAR_UNKNOWN object.
|
||||||
|
@ -83,6 +83,13 @@
|
|||||||
/// @param len String length.
|
/// @param len String length.
|
||||||
/// @param type EXT type.
|
/// @param type EXT type.
|
||||||
|
|
||||||
|
/// @def TYPVAL_ENCODE_CONV_BLOB
|
||||||
|
/// @brief Macros used to convert a blob
|
||||||
|
///
|
||||||
|
/// @param tv Pointer to typval where value is stored. May not be NULL.
|
||||||
|
/// @param blob Pointer to the blob to convert.
|
||||||
|
/// @param len Blob length.
|
||||||
|
|
||||||
/// @def TYPVAL_ENCODE_CONV_FUNC_START
|
/// @def TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
/// @brief Macros used when starting to convert a funcref or a partial
|
/// @brief Macros used when starting to convert a funcref or a partial
|
||||||
///
|
///
|
||||||
@ -330,6 +337,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
|||||||
TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
|
TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VAR_BLOB: {
|
||||||
|
TYPVAL_ENCODE_CONV_BLOB(tv, tv->vval.v_blob,
|
||||||
|
tv_blob_len(tv->vval.v_blob));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAR_FUNC: {
|
case VAR_FUNC: {
|
||||||
TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
|
TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
|
||||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
|
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
|
||||||
|
@ -939,13 +939,18 @@ EXTERN char_u e_readonlyvar[] INIT(= N_(
|
|||||||
"E46: Cannot change read-only variable \"%.*s\""));
|
"E46: Cannot change read-only variable \"%.*s\""));
|
||||||
EXTERN char_u e_stringreq[] INIT(= N_("E928: String required"));
|
EXTERN char_u e_stringreq[] INIT(= N_("E928: String required"));
|
||||||
EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required"));
|
EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required"));
|
||||||
|
EXTERN char_u e_blobidx[] INIT(= N_("E979: Blob index out of range: %" PRId64));
|
||||||
|
EXTERN char_u e_invalblob[] INIT(= N_("E978: Invalid operation for Blob"));
|
||||||
EXTERN char_u e_toomanyarg[] INIT(= N_(
|
EXTERN char_u e_toomanyarg[] INIT(= N_(
|
||||||
"E118: Too many arguments for function: %s"));
|
"E118: Too many arguments for function: %s"));
|
||||||
EXTERN char_u e_dictkey[] INIT(= N_(
|
EXTERN char_u e_dictkey[] INIT(= N_(
|
||||||
"E716: Key not present in Dictionary: \"%s\""));
|
"E716: Key not present in Dictionary: \"%s\""));
|
||||||
EXTERN char_u e_listreq[] INIT(= N_("E714: List required"));
|
EXTERN char_u e_listreq[] INIT(= N_("E714: List required"));
|
||||||
|
EXTERN char_u e_listblobreq[] INIT(= N_("E897: List or Blob required"));
|
||||||
EXTERN char_u e_listdictarg[] INIT(= N_(
|
EXTERN char_u e_listdictarg[] INIT(= N_(
|
||||||
"E712: Argument of %s must be a List or Dictionary"));
|
"E712: Argument of %s must be a List or Dictionary"));
|
||||||
|
EXTERN char_u e_listdictblobarg[] INIT(= N_(
|
||||||
|
"E896: Argument of %s must be a List, Dictionary or Blob"));
|
||||||
EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
|
EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
|
||||||
EXTERN char_u e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
|
EXTERN char_u e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
|
||||||
EXTERN char_u e_secure[] INIT(= N_("E523: Not allowed here"));
|
EXTERN char_u e_secure[] INIT(= N_("E523: Not allowed here"));
|
||||||
|
@ -481,6 +481,14 @@ static bool typval_conv_special = false;
|
|||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
|
||||||
TYPVAL_ENCODE_CONV_NIL(tv)
|
TYPVAL_ENCODE_CONV_NIL(tv)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
|
||||||
|
do { \
|
||||||
|
const blob_T *const blob_ = (blob); \
|
||||||
|
lua_pushlstring(lstate, \
|
||||||
|
blob_ != NULL ? (const char *)blob_->bv_ga.ga_data : "", \
|
||||||
|
(size_t)(len)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
do { \
|
do { \
|
||||||
TYPVAL_ENCODE_CONV_NIL(tv); \
|
TYPVAL_ENCODE_CONV_NIL(tv); \
|
||||||
@ -579,6 +587,7 @@ static bool typval_conv_special = false;
|
|||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
|
#undef TYPVAL_ENCODE_CONV_BLOB
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
|
@ -1653,6 +1653,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kSDItemVariable: {
|
case kSDItemVariable: {
|
||||||
|
if (entry.data.global_var.value.v_type == VAR_TYPE_BLOB) {
|
||||||
|
// Strings and Blobs both pack as msgpack BINs; differentiate them by
|
||||||
|
// storing an additional VAR_TYPE_BLOB element alongside Blobs
|
||||||
|
list_T *const list = tv_list_alloc(1);
|
||||||
|
tv_list_append_number(list, VAR_TYPE_BLOB);
|
||||||
|
entry.data.global_var.additional_elements = list;
|
||||||
|
}
|
||||||
const size_t arr_size = 2 + (size_t)(
|
const size_t arr_size = 2 + (size_t)(
|
||||||
tv_list_len(entry.data.global_var.additional_elements));
|
tv_list_len(entry.data.global_var.additional_elements));
|
||||||
msgpack_pack_array(spacker, arr_size);
|
msgpack_pack_array(spacker, arr_size);
|
||||||
@ -3937,15 +3944,38 @@ shada_read_next_item_start:
|
|||||||
entry->data.global_var.name =
|
entry->data.global_var.name =
|
||||||
xmemdupz(unpacked.data.via.array.ptr[0].via.bin.ptr,
|
xmemdupz(unpacked.data.via.array.ptr[0].via.bin.ptr,
|
||||||
unpacked.data.via.array.ptr[0].via.bin.size);
|
unpacked.data.via.array.ptr[0].via.bin.size);
|
||||||
if (msgpack_to_vim(unpacked.data.via.array.ptr[1],
|
SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2,
|
||||||
&(entry->data.global_var.value)) == FAIL) {
|
entry->data.global_var.additional_elements,
|
||||||
|
"variable");
|
||||||
|
bool is_blob = false;
|
||||||
|
// A msgpack BIN could be a String or Blob; an additional VAR_TYPE_BLOB
|
||||||
|
// element is stored with Blobs which can be used to differentiate them
|
||||||
|
if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_BIN) {
|
||||||
|
const listitem_T *type_item
|
||||||
|
= tv_list_first(entry->data.global_var.additional_elements);
|
||||||
|
if (type_item != NULL) {
|
||||||
|
const typval_T *type_tv = TV_LIST_ITEM_TV(type_item);
|
||||||
|
if (type_tv->v_type != VAR_NUMBER
|
||||||
|
|| type_tv->vval.v_number != VAR_TYPE_BLOB) {
|
||||||
|
emsgf(_(READERR("variable", "has wrong variable type")),
|
||||||
|
initial_fpos);
|
||||||
|
goto shada_read_next_item_error;
|
||||||
|
}
|
||||||
|
is_blob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_blob) {
|
||||||
|
const msgpack_object_bin *const bin
|
||||||
|
= &unpacked.data.via.array.ptr[1].via.bin;
|
||||||
|
blob_T *const blob = tv_blob_alloc();
|
||||||
|
ga_concat_len(&blob->bv_ga, bin->ptr, (size_t)bin->size);
|
||||||
|
tv_blob_set_ret(&entry->data.global_var.value, blob);
|
||||||
|
} else if (msgpack_to_vim(unpacked.data.via.array.ptr[1],
|
||||||
|
&(entry->data.global_var.value)) == FAIL) {
|
||||||
emsgf(_(READERR("variable", "has value that cannot "
|
emsgf(_(READERR("variable", "has value that cannot "
|
||||||
"be converted to the VimL value")), initial_fpos);
|
"be converted to the VimL value")), initial_fpos);
|
||||||
goto shada_read_next_item_error;
|
goto shada_read_next_item_error;
|
||||||
}
|
}
|
||||||
SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2,
|
|
||||||
entry->data.global_var.additional_elements,
|
|
||||||
"variable");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kSDItemSubString: {
|
case kSDItemSubString: {
|
||||||
|
349
src/nvim/testdir/test_blob.vim
Normal file
349
src/nvim/testdir/test_blob.vim
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
" Tests for the Blob types
|
||||||
|
|
||||||
|
func TearDown()
|
||||||
|
" Run garbage collection after every test
|
||||||
|
call test_garbagecollect_now()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Tests for Blob type
|
||||||
|
|
||||||
|
" Blob creation from constant
|
||||||
|
func Test_blob_create()
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(v:t_blob, type(b))
|
||||||
|
call assert_equal(4, len(b))
|
||||||
|
call assert_equal(0xDE, b[0])
|
||||||
|
call assert_equal(0xAD, b[1])
|
||||||
|
call assert_equal(0xBE, b[2])
|
||||||
|
call assert_equal(0xEF, b[3])
|
||||||
|
call assert_fails('let x = b[4]')
|
||||||
|
|
||||||
|
call assert_equal(0xDE, get(b, 0))
|
||||||
|
call assert_equal(0xEF, get(b, 3))
|
||||||
|
|
||||||
|
call assert_fails('let b = 0z1', 'E973:')
|
||||||
|
call assert_fails('let b = 0z1x', 'E973:')
|
||||||
|
call assert_fails('let b = 0z12345', 'E973:')
|
||||||
|
|
||||||
|
call assert_equal(0z, v:_null_blob)
|
||||||
|
|
||||||
|
let b = 0z001122.33445566.778899.aabbcc.dd
|
||||||
|
call assert_equal(0z00112233445566778899aabbccdd, b)
|
||||||
|
call assert_fails('let b = 0z1.1')
|
||||||
|
call assert_fails('let b = 0z.')
|
||||||
|
call assert_fails('let b = 0z001122.')
|
||||||
|
call assert_fails('call get("", 1)', 'E896:')
|
||||||
|
call assert_equal(0, len(v:_null_blob))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" assignment to a blob
|
||||||
|
func Test_blob_assign()
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
let b2 = b[1:2]
|
||||||
|
call assert_equal(0zADBE, b2)
|
||||||
|
|
||||||
|
let bcopy = b[:]
|
||||||
|
call assert_equal(b, bcopy)
|
||||||
|
call assert_false(b is bcopy)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
let b2 = b
|
||||||
|
call assert_true(b is b2)
|
||||||
|
let b[:] = 0z11223344
|
||||||
|
call assert_equal(0z11223344, b)
|
||||||
|
call assert_equal(0z11223344, b2)
|
||||||
|
call assert_true(b is b2)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
let b[3:] = 0z66
|
||||||
|
call assert_equal(0zDEADBE66, b)
|
||||||
|
let b[:1] = 0z8899
|
||||||
|
call assert_equal(0z8899BE66, b)
|
||||||
|
|
||||||
|
call assert_fails('let b[2:3] = 0z112233', 'E972:')
|
||||||
|
call assert_fails('let b[2:3] = 0z11', 'E972:')
|
||||||
|
call assert_fails('let b[3:2] = 0z', 'E979:')
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
let b += 0z99
|
||||||
|
call assert_equal(0zDEADBEEF99, b)
|
||||||
|
|
||||||
|
call assert_fails('let b .= 0z33', 'E734:')
|
||||||
|
call assert_fails('let b .= "xx"', 'E734:')
|
||||||
|
call assert_fails('let b += "xx"', 'E734:')
|
||||||
|
call assert_fails('let b[1:1] .= 0z55', 'E734:')
|
||||||
|
|
||||||
|
let l = [0z12]
|
||||||
|
let m = deepcopy(l)
|
||||||
|
let m[0] = 0z34 " E742 or E741 should not occur.
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_get_range()
|
||||||
|
let b = 0z0011223344
|
||||||
|
call assert_equal(0z2233, b[2:3])
|
||||||
|
call assert_equal(0z223344, b[2:-1])
|
||||||
|
call assert_equal(0z00, b[0:-5])
|
||||||
|
call assert_equal(0z, b[0:-11])
|
||||||
|
call assert_equal(0z44, b[-1:])
|
||||||
|
call assert_equal(0z0011223344, b[:])
|
||||||
|
call assert_equal(0z0011223344, b[:-1])
|
||||||
|
call assert_equal(0z, b[5:6])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_get()
|
||||||
|
let b = 0z0011223344
|
||||||
|
call assert_equal(0x00, get(b, 0))
|
||||||
|
call assert_equal(0x22, get(b, 2, 999))
|
||||||
|
call assert_equal(0x44, get(b, 4))
|
||||||
|
call assert_equal(0x44, get(b, -1))
|
||||||
|
call assert_equal(-1, get(b, 5))
|
||||||
|
call assert_equal(999, get(b, 5, 999))
|
||||||
|
call assert_equal(-1, get(b, -8))
|
||||||
|
call assert_equal(999, get(b, -8, 999))
|
||||||
|
call assert_equal(10, get(v:_null_blob, 2, 10))
|
||||||
|
|
||||||
|
call assert_equal(0x00, b[0])
|
||||||
|
call assert_equal(0x22, b[2])
|
||||||
|
call assert_equal(0x44, b[4])
|
||||||
|
call assert_equal(0x44, b[-1])
|
||||||
|
call assert_fails('echo b[5]', 'E979:')
|
||||||
|
call assert_fails('echo b[-8]', 'E979:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_to_string()
|
||||||
|
let b = 0z00112233445566778899aabbccdd
|
||||||
|
call assert_equal('0z00112233.44556677.8899AABB.CCDD', string(b))
|
||||||
|
call assert_equal(b, eval(string(b)))
|
||||||
|
call remove(b, 4, -1)
|
||||||
|
call assert_equal('0z00112233', string(b))
|
||||||
|
call remove(b, 0, 3)
|
||||||
|
call assert_equal('0z', string(b))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_compare()
|
||||||
|
let b1 = 0z0011
|
||||||
|
let b2 = 0z1100
|
||||||
|
let b3 = 0z001122
|
||||||
|
call assert_true(b1 == b1)
|
||||||
|
call assert_false(b1 == b2)
|
||||||
|
call assert_false(b1 == b3)
|
||||||
|
call assert_true(b1 != b2)
|
||||||
|
call assert_true(b1 != b3)
|
||||||
|
call assert_true(b1 == 0z0011)
|
||||||
|
call assert_fails('echo b1 == 9', 'E977:')
|
||||||
|
call assert_fails('echo b1 != 9', 'E977:')
|
||||||
|
|
||||||
|
call assert_false(b1 is b2)
|
||||||
|
let b2 = b1
|
||||||
|
call assert_true(b1 == b2)
|
||||||
|
call assert_true(b1 is b2)
|
||||||
|
let b2 = copy(b1)
|
||||||
|
call assert_true(b1 == b2)
|
||||||
|
call assert_false(b1 is b2)
|
||||||
|
let b2 = b1[:]
|
||||||
|
call assert_true(b1 == b2)
|
||||||
|
call assert_false(b1 is b2)
|
||||||
|
|
||||||
|
call assert_fails('let x = b1 > b2')
|
||||||
|
call assert_fails('let x = b1 < b2')
|
||||||
|
call assert_fails('let x = b1 - b2')
|
||||||
|
call assert_fails('let x = b1 / b2')
|
||||||
|
call assert_fails('let x = b1 * b2')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" test for range assign
|
||||||
|
func Test_blob_range_assign()
|
||||||
|
let b = 0z00
|
||||||
|
let b[1] = 0x11
|
||||||
|
let b[2] = 0x22
|
||||||
|
call assert_equal(0z001122, b)
|
||||||
|
call assert_fails('let b[4] = 0x33', 'E979:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_for_loop()
|
||||||
|
let blob = 0z00010203
|
||||||
|
let i = 0
|
||||||
|
for byte in blob
|
||||||
|
call assert_equal(i, byte)
|
||||||
|
let i += 1
|
||||||
|
endfor
|
||||||
|
call assert_equal(4, i)
|
||||||
|
|
||||||
|
let blob = 0z00
|
||||||
|
call remove(blob, 0)
|
||||||
|
call assert_equal(0, len(blob))
|
||||||
|
for byte in blob
|
||||||
|
call assert_error('loop over empty blob')
|
||||||
|
endfor
|
||||||
|
|
||||||
|
let blob = 0z0001020304
|
||||||
|
let i = 0
|
||||||
|
for byte in blob
|
||||||
|
call assert_equal(i, byte)
|
||||||
|
if i == 1
|
||||||
|
call remove(blob, 0)
|
||||||
|
elseif i == 3
|
||||||
|
call remove(blob, 3)
|
||||||
|
endif
|
||||||
|
let i += 1
|
||||||
|
endfor
|
||||||
|
call assert_equal(5, i)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_concatenate()
|
||||||
|
let b = 0z0011
|
||||||
|
let b += 0z2233
|
||||||
|
call assert_equal(0z00112233, b)
|
||||||
|
|
||||||
|
call assert_fails('let b += "a"')
|
||||||
|
call assert_fails('let b += 88')
|
||||||
|
|
||||||
|
let b = 0zDEAD + 0zBEEF
|
||||||
|
call assert_equal(0zDEADBEEF, b)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_add()
|
||||||
|
let b = 0z0011
|
||||||
|
call add(b, 0x22)
|
||||||
|
call assert_equal(0z001122, b)
|
||||||
|
call add(b, '51')
|
||||||
|
call assert_equal(0z00112233, b)
|
||||||
|
|
||||||
|
call assert_fails('call add(b, [9])', 'E745:')
|
||||||
|
call assert_fails('call add("", 0x01)', 'E897:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_empty()
|
||||||
|
call assert_false(empty(0z001122))
|
||||||
|
call assert_true(empty(0z))
|
||||||
|
call assert_true(empty(v:_null_blob))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test removing items in blob
|
||||||
|
func Test_blob_func_remove()
|
||||||
|
" Test removing 1 element
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(0xDE, remove(b, 0))
|
||||||
|
call assert_equal(0zADBEEF, b)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(0xEF, remove(b, -1))
|
||||||
|
call assert_equal(0zDEADBE, b)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(0xAD, remove(b, 1))
|
||||||
|
call assert_equal(0zDEBEEF, b)
|
||||||
|
|
||||||
|
" Test removing range of element(s)
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(0zBE, remove(b, 2, 2))
|
||||||
|
call assert_equal(0zDEADEF, b)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_equal(0zADBE, remove(b, 1, 2))
|
||||||
|
call assert_equal(0zDEEF, b)
|
||||||
|
|
||||||
|
" Test invalid cases
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call assert_fails("call remove(b, 5)", 'E979:')
|
||||||
|
call assert_fails("call remove(b, 1, 5)", 'E979:')
|
||||||
|
call assert_fails("call remove(b, 3, 2)", 'E979:')
|
||||||
|
call assert_fails("call remove(1, 0)", 'E896:')
|
||||||
|
call assert_fails("call remove(b, b)", 'E974:')
|
||||||
|
call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:')
|
||||||
|
|
||||||
|
" Translated from v8.2.3284
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
lockvar b
|
||||||
|
call assert_fails('call remove(b, 0)', 'E741:')
|
||||||
|
unlockvar b
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_read_write()
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call writefile(b, 'Xblob')
|
||||||
|
let br = readfile('Xblob', 'B')
|
||||||
|
call assert_equal(b, br)
|
||||||
|
call delete('Xblob')
|
||||||
|
|
||||||
|
" This was crashing when calling readfile() with a directory.
|
||||||
|
call assert_fails("call readfile('.', 'B')", 'E17: "." is a directory')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" filter() item in blob
|
||||||
|
func Test_blob_filter()
|
||||||
|
call assert_equal(0z, filter(0zDEADBEEF, '0'))
|
||||||
|
call assert_equal(0zADBEEF, filter(0zDEADBEEF, 'v:val != 0xDE'))
|
||||||
|
call assert_equal(0zDEADEF, filter(0zDEADBEEF, 'v:val != 0xBE'))
|
||||||
|
call assert_equal(0zDEADBE, filter(0zDEADBEEF, 'v:val != 0xEF'))
|
||||||
|
call assert_equal(0zDEADBEEF, filter(0zDEADBEEF, '1'))
|
||||||
|
call assert_equal(0z01030103, filter(0z010203010203, 'v:val != 0x02'))
|
||||||
|
call assert_equal(0zADEF, filter(0zDEADBEEF, 'v:key % 2'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" map() item in blob
|
||||||
|
func Test_blob_map()
|
||||||
|
call assert_equal(0zDFAEBFF0, map(0zDEADBEEF, 'v:val + 1'))
|
||||||
|
call assert_equal(0z00010203, map(0zDEADBEEF, 'v:key'))
|
||||||
|
call assert_equal(0zDEAEC0F2, map(0zDEADBEEF, 'v:key + v:val'))
|
||||||
|
|
||||||
|
call assert_fails("call map(0z00, '[9]')", 'E978:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_index()
|
||||||
|
call assert_equal(2, index(0zDEADBEEF, 0xBE))
|
||||||
|
call assert_equal(-1, index(0zDEADBEEF, 0))
|
||||||
|
call assert_equal(2, index(0z11111111, 0x11, 2))
|
||||||
|
call assert_equal(3, index(0z11110111, 0x11, 2))
|
||||||
|
call assert_equal(2, index(0z11111111, 0x11, -2))
|
||||||
|
call assert_equal(3, index(0z11110111, 0x11, -2))
|
||||||
|
|
||||||
|
call assert_fails('call index("asdf", 0)', 'E897:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_insert()
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call insert(b, 0x33)
|
||||||
|
call assert_equal(0z33DEADBEEF, b)
|
||||||
|
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
call insert(b, 0x33, 2)
|
||||||
|
call assert_equal(0zDEAD33BEEF, b)
|
||||||
|
|
||||||
|
call assert_fails('call insert(b, -1)', 'E475:')
|
||||||
|
call assert_fails('call insert(b, 257)', 'E475:')
|
||||||
|
call assert_fails('call insert(b, 0, [9])', 'E745:')
|
||||||
|
call assert_equal(0, insert(v:_null_blob, 0x33))
|
||||||
|
|
||||||
|
" Translated from v8.2.3284
|
||||||
|
let b = 0zDEADBEEF
|
||||||
|
lockvar b
|
||||||
|
call assert_fails('call insert(b, 3)', 'E741:')
|
||||||
|
unlockvar b
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_reverse()
|
||||||
|
call assert_equal(0zEFBEADDE, reverse(0zDEADBEEF))
|
||||||
|
call assert_equal(0zBEADDE, reverse(0zDEADBE))
|
||||||
|
call assert_equal(0zADDE, reverse(0zDEAD))
|
||||||
|
call assert_equal(0zDE, reverse(0zDE))
|
||||||
|
call assert_equal(0z, reverse(v:_null_blob))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_lock()
|
||||||
|
let b = 0z112233
|
||||||
|
lockvar b
|
||||||
|
call assert_fails('let b = 0z44', 'E741:')
|
||||||
|
unlockvar b
|
||||||
|
let b = 0z44
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_blob_sort()
|
||||||
|
if has('float')
|
||||||
|
call assert_fails('call sort([1.0, 0z11], "f")', 'E975:')
|
||||||
|
else
|
||||||
|
call assert_fails('call sort(["abc", 0z11], "f")', 'E702:')
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
@ -244,18 +244,33 @@ func Test_const_with_eval_name()
|
|||||||
call assert_fails('const {s2} = "bar"', 'E995:')
|
call assert_fails('const {s2} = "bar"', 'E995:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_lock_depth_is_1()
|
func Test_lock_depth_is_2()
|
||||||
const l = [1, 2, 3]
|
" Modify list - error when changing item or adding/removing items
|
||||||
const d = {'foo': 10}
|
const l = [1, 2, [3, 4]]
|
||||||
|
call assert_fails('let l[0] = 42', 'E741:')
|
||||||
" Modify list - setting item is OK, adding/removing items not
|
call assert_fails('let l[2][0] = 42', 'E741:')
|
||||||
let l[0] = 42
|
|
||||||
call assert_fails('call add(l, 4)', 'E741:')
|
call assert_fails('call add(l, 4)', 'E741:')
|
||||||
call assert_fails('unlet l[1]', 'E741:')
|
call assert_fails('unlet l[1]', 'E741:')
|
||||||
|
|
||||||
" Modify dict - changing item is OK, adding/removing items not
|
" Modify blob - error when changing
|
||||||
let d['foo'] = 'hello'
|
const b = 0z001122
|
||||||
let d.foo = 44
|
call assert_fails('let b[0] = 42', 'E741:')
|
||||||
|
|
||||||
|
" Modify dict - error when changing item or adding/removing items
|
||||||
|
const d = {'foo': 10}
|
||||||
|
call assert_fails("let d['foo'] = 'hello'", 'E741:')
|
||||||
|
call assert_fails("let d.foo = 'hello'", 'E741:')
|
||||||
call assert_fails("let d['bar'] = 'hello'", 'E741:')
|
call assert_fails("let d['bar'] = 'hello'", 'E741:')
|
||||||
call assert_fails("unlet d['foo']", 'E741:')
|
call assert_fails("unlet d['foo']", 'E741:')
|
||||||
|
|
||||||
|
" Modifying list or dict item contents is OK.
|
||||||
|
let lvar = ['a', 'b']
|
||||||
|
let bvar = 0z1122
|
||||||
|
const l2 = [0, lvar, bvar]
|
||||||
|
let l2[1][0] = 'c'
|
||||||
|
let l2[2][1] = 0x33
|
||||||
|
call assert_equal([0, ['c', 'b'], 0z1133], l2)
|
||||||
|
|
||||||
|
const d2 = #{a: 0, b: lvar, c: 4}
|
||||||
|
let d2.b[1] = 'd'
|
||||||
endfunc
|
endfunc
|
||||||
|
@ -65,9 +65,11 @@ func Test_E963()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_for_invalid()
|
func Test_for_invalid()
|
||||||
call assert_fails("for x in 99", 'E714:')
|
" Vim gives incorrect emsg here until v8.2.3284, but the exact emsg from that
|
||||||
call assert_fails("for x in function('winnr')", 'E714:')
|
" patch cannot be used until v8.2.2658 is ported (for loop over Strings)
|
||||||
call assert_fails("for x in {'a': 9}", 'E714:')
|
call assert_fails("for x in 99", 'E897:')
|
||||||
|
call assert_fails("for x in function('winnr')", 'E897:')
|
||||||
|
call assert_fails("for x in {'a': 9}", 'E897:')
|
||||||
|
|
||||||
if 0
|
if 0
|
||||||
/1/5/2/s/\n
|
/1/5/2/s/\n
|
||||||
|
@ -81,7 +81,11 @@ func Test_filter_map_dict_expr_funcref()
|
|||||||
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_map_fails()
|
func Test_map_filter_fails()
|
||||||
call assert_fails('call map([1], "42 +")', 'E15:')
|
call assert_fails('call map([1], "42 +")', 'E15:')
|
||||||
call assert_fails('call filter([1], "42 +")', 'E15:')
|
call assert_fails('call filter([1], "42 +")', 'E15:')
|
||||||
|
call assert_fails("let l = map('abc', '\"> \" . v:val')", 'E896:')
|
||||||
|
call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -72,4 +72,8 @@ func Test_fnamemodify_er()
|
|||||||
" :e never includes the whole filename, so "a.b":e:e:e --> "b"
|
" :e never includes the whole filename, so "a.b":e:e:e --> "b"
|
||||||
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e'))
|
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e'))
|
||||||
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e'))
|
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e'))
|
||||||
|
|
||||||
|
call assert_equal('', fnamemodify(v:_null_string, v:_null_string))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -139,7 +139,7 @@ func Test_list_func_remove()
|
|||||||
call assert_fails("call remove(l, 5)", 'E684:')
|
call assert_fails("call remove(l, 5)", 'E684:')
|
||||||
call assert_fails("call remove(l, 1, 5)", 'E684:')
|
call assert_fails("call remove(l, 1, 5)", 'E684:')
|
||||||
call assert_fails("call remove(l, 3, 2)", 'E16:')
|
call assert_fails("call remove(l, 3, 2)", 'E16:')
|
||||||
call assert_fails("call remove(1, 0)", 'E712:')
|
call assert_fails("call remove(1, 0)", 'E896:')
|
||||||
call assert_fails("call remove(l, l)", 'E745:')
|
call assert_fails("call remove(l, l)", 'E745:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
@ -616,6 +616,8 @@ func Test_reverse_sort_uniq()
|
|||||||
call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
|
call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
|
||||||
call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
|
call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
|
||||||
call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
|
call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
|
||||||
|
|
||||||
|
call assert_fails('call reverse("")', 'E899:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" splitting a string to a List
|
" splitting a string to a List
|
||||||
|
@ -46,11 +46,8 @@ func Test_dict_method()
|
|||||||
call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4}))
|
call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4}))
|
||||||
call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4'))
|
call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4'))
|
||||||
call assert_equal(2, d->get('two'))
|
call assert_equal(2, d->get('two'))
|
||||||
" Nvim doesn't support Blobs yet; expect a different emsg
|
call assert_fails("let x = d->index(2)", 'E897:')
|
||||||
" call assert_fails("let x = d->index(2)", 'E897:')
|
call assert_fails("let x = d->insert(0)", 'E899:')
|
||||||
" call assert_fails("let x = d->insert(0)", 'E899:')
|
|
||||||
call assert_fails("let x = d->index(2)", 'E714:')
|
|
||||||
call assert_fails("let x = d->insert(0)", 'E686:')
|
|
||||||
call assert_true(d->has_key('two'))
|
call assert_true(d->has_key('two'))
|
||||||
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
|
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
|
||||||
call assert_fails("let x = d->join()", 'E714:')
|
call assert_fails("let x = d->join()", 'E714:')
|
||||||
@ -63,9 +60,7 @@ func Test_dict_method()
|
|||||||
call assert_equal(2, d->remove("two"))
|
call assert_equal(2, d->remove("two"))
|
||||||
let d.two = 2
|
let d.two = 2
|
||||||
call assert_fails('let x = d->repeat(2)', 'E731:')
|
call assert_fails('let x = d->repeat(2)', 'E731:')
|
||||||
" Nvim doesn't support Blobs yet; expect a different emsg
|
call assert_fails('let x = d->reverse()', 'E899:')
|
||||||
" call assert_fails('let x = d->reverse()', 'E899:')
|
|
||||||
call assert_fails('let x = d->reverse()', 'E686:')
|
|
||||||
call assert_fails('let x = d->sort()', 'E686:')
|
call assert_fails('let x = d->sort()', 'E686:')
|
||||||
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
|
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
|
||||||
call assert_equal(v:t_dict, d->type())
|
call assert_equal(v:t_dict, d->type())
|
||||||
|
@ -2221,6 +2221,10 @@ func Xproperty_tests(cchar)
|
|||||||
call g:Xsetlist([], 'a', {'context':246})
|
call g:Xsetlist([], 'a', {'context':246})
|
||||||
let d = g:Xgetlist({'context':1})
|
let d = g:Xgetlist({'context':1})
|
||||||
call assert_equal(246, d.context)
|
call assert_equal(246, d.context)
|
||||||
|
" set other Vim data types as context
|
||||||
|
call g:Xsetlist([], 'a', {'context' : v:_null_blob})
|
||||||
|
call g:Xsetlist([], 'a', {'context' : ''})
|
||||||
|
call test_garbagecollect_now()
|
||||||
if a:cchar == 'l'
|
if a:cchar == 'l'
|
||||||
" Test for copying context across two different location lists
|
" Test for copying context across two different location lists
|
||||||
new | only
|
new | only
|
||||||
|
@ -95,7 +95,6 @@ func Test_rename_copy()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_rename_fails()
|
func Test_rename_fails()
|
||||||
throw 'skipped: TODO: '
|
|
||||||
call writefile(['foo'], 'Xrenamefile')
|
call writefile(['foo'], 'Xrenamefile')
|
||||||
|
|
||||||
" Can't rename into a non-existing directory.
|
" Can't rename into a non-existing directory.
|
||||||
|
@ -168,7 +168,6 @@ func Test_swapname()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_swapfile_delete()
|
func Test_swapfile_delete()
|
||||||
throw 'skipped: need the "blob" feature for this test'
|
|
||||||
autocmd! SwapExists
|
autocmd! SwapExists
|
||||||
function s:swap_exists()
|
function s:swap_exists()
|
||||||
let v:swapchoice = s:swap_choice
|
let v:swapchoice = s:swap_choice
|
||||||
|
@ -368,7 +368,6 @@ endfunc
|
|||||||
|
|
||||||
" Check that reading a truncted undo file doesn't hang.
|
" Check that reading a truncted undo file doesn't hang.
|
||||||
func Test_undofile_truncated()
|
func Test_undofile_truncated()
|
||||||
throw 'skipped: TODO: '
|
|
||||||
new
|
new
|
||||||
call setline(1, 'hello')
|
call setline(1, 'hello')
|
||||||
set ul=100
|
set ul=100
|
||||||
|
@ -1152,6 +1152,10 @@ func Test_type()
|
|||||||
call assert_equal(v:t_float, type(0.0))
|
call assert_equal(v:t_float, type(0.0))
|
||||||
call assert_equal(v:t_bool, type(v:false))
|
call assert_equal(v:t_bool, type(v:false))
|
||||||
call assert_equal(v:t_bool, type(v:true))
|
call assert_equal(v:t_bool, type(v:true))
|
||||||
|
call assert_equal(v:t_string, type(v:_null_string))
|
||||||
|
call assert_equal(v:t_list, type(v:_null_list))
|
||||||
|
call assert_equal(v:t_dict, type(v:_null_dict))
|
||||||
|
call assert_equal(v:t_blob, type(v:_null_blob))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
"-------------------------------------------------------------------------------
|
"-------------------------------------------------------------------------------
|
||||||
|
@ -17,6 +17,8 @@ func Test_writefile()
|
|||||||
call assert_equal("morning", l[3])
|
call assert_equal("morning", l[3])
|
||||||
call assert_equal("vimmers", l[4])
|
call assert_equal("vimmers", l[4])
|
||||||
call delete(f)
|
call delete(f)
|
||||||
|
|
||||||
|
call assert_fails('call writefile("text", "Xfile")', 'E475: Invalid argument: writefile() first argument must be a List or a Blob')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_writefile_ignore_regexp_error()
|
func Test_writefile_ignore_regexp_error()
|
||||||
|
@ -102,6 +102,7 @@ typedef enum {
|
|||||||
#define VAR_TYPE_FLOAT 5
|
#define VAR_TYPE_FLOAT 5
|
||||||
#define VAR_TYPE_BOOL 6
|
#define VAR_TYPE_BOOL 6
|
||||||
#define VAR_TYPE_SPECIAL 7
|
#define VAR_TYPE_SPECIAL 7
|
||||||
|
#define VAR_TYPE_BLOB 10
|
||||||
|
|
||||||
|
|
||||||
// values for xp_context when doing command line completion
|
// values for xp_context when doing command line completion
|
||||||
|
@ -89,6 +89,9 @@ describe('channels', function()
|
|||||||
command("call chansend(id, 'howdy')")
|
command("call chansend(id, 'howdy')")
|
||||||
eq({"notification", "stdout", {id, {"[1, ['howdy'], 'stdin']"}}}, next_msg())
|
eq({"notification", "stdout", {id, {"[1, ['howdy'], 'stdin']"}}}, next_msg())
|
||||||
|
|
||||||
|
command("call chansend(id, 0z686f6c61)")
|
||||||
|
eq({"notification", "stdout", {id, {"[1, ['hola'], 'stdin']"}}}, next_msg())
|
||||||
|
|
||||||
command("call chanclose(id, 'stdin')")
|
command("call chanclose(id, 'stdin')")
|
||||||
expect_twostreams({{"notification", "stdout", {id, {"[1, [''], 'stdin']"}}},
|
expect_twostreams({{"notification", "stdout", {id, {"[1, [''], 'stdin']"}}},
|
||||||
{'notification', 'stdout', {id, {''}}}},
|
{'notification', 'stdout', {id, {''}}}},
|
||||||
@ -131,6 +134,8 @@ describe('channels', function()
|
|||||||
command("call chansend(id, 'TEXT\n')")
|
command("call chansend(id, 'TEXT\n')")
|
||||||
expect_twoline(id, "stdout", "TEXT\r", "[1, ['TEXT', ''], 'stdin']")
|
expect_twoline(id, "stdout", "TEXT\r", "[1, ['TEXT', ''], 'stdin']")
|
||||||
|
|
||||||
|
command("call chansend(id, 0z426c6f6273210a)")
|
||||||
|
expect_twoline(id, "stdout", "Blobs!\r", "[1, ['Blobs!', ''], 'stdin']")
|
||||||
|
|
||||||
command("call chansend(id, 'neovan')")
|
command("call chansend(id, 'neovan')")
|
||||||
eq({"notification", "stdout", {id, {"neovan"}}}, next_msg())
|
eq({"notification", "stdout", {id, {"neovan"}}}, next_msg())
|
||||||
|
@ -155,4 +155,13 @@ describe('eval-API', function()
|
|||||||
pcall_err(command, "sandbox call nvim_input('ievil')"))
|
pcall_err(command, "sandbox call nvim_input('ievil')"))
|
||||||
eq({''}, meths.buf_get_lines(0, 0, -1, true))
|
eq({''}, meths.buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('converts blobs to API strings', function()
|
||||||
|
command('let g:v1 = nvim__id(0z68656c6c6f)')
|
||||||
|
command('let g:v2 = nvim__id(v:_null_blob)')
|
||||||
|
eq(1, eval('type(g:v1)'))
|
||||||
|
eq(1, eval('type(g:v2)'))
|
||||||
|
eq('hello', eval('g:v1'))
|
||||||
|
eq('', eval('g:v2'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -322,16 +322,16 @@ describe('execute()', function()
|
|||||||
eq('Vim(call):E731: using Dictionary as a String', ret)
|
eq('Vim(call):E731: using Dictionary as a String', ret)
|
||||||
|
|
||||||
ret = exc_exec('call execute("echo add(1, 1)", "")')
|
ret = exc_exec('call execute("echo add(1, 1)", "")')
|
||||||
eq('Vim(echo):E714: List required', ret)
|
eq('Vim(echo):E897: List or Blob required', ret)
|
||||||
|
|
||||||
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
|
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
|
||||||
eq('Vim(echo):E714: List required', ret)
|
eq('Vim(echo):E897: List or Blob required', ret)
|
||||||
|
|
||||||
ret = exc_exec('call execute("echo add(1, 1)", "silent")')
|
ret = exc_exec('call execute("echo add(1, 1)", "silent")')
|
||||||
eq('Vim(echo):E714: List required', ret)
|
eq('Vim(echo):E897: List or Blob required', ret)
|
||||||
|
|
||||||
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
|
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
|
||||||
eq('Vim(echo):E714: List required', ret)
|
eq('Vim(echo):E897: List or Blob required', ret)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -538,6 +538,11 @@ describe('json_encode() function', function()
|
|||||||
eq('"þÿþ"', funcs.json_encode('þÿþ'))
|
eq('"þÿþ"', funcs.json_encode('þÿþ'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('dumps blobs', function()
|
||||||
|
eq('[]', eval('json_encode(0z)'))
|
||||||
|
eq('[222, 173, 190, 239]', eval('json_encode(0zDEADBEEF)'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('dumps numbers', function()
|
it('dumps numbers', function()
|
||||||
eq('0', funcs.json_encode(0))
|
eq('0', funcs.json_encode(0))
|
||||||
eq('10', funcs.json_encode(10))
|
eq('10', funcs.json_encode(10))
|
||||||
@ -769,6 +774,10 @@ describe('json_encode() function', function()
|
|||||||
eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)'))
|
eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('can dump NULL blob', function()
|
||||||
|
eq('[]', eval('json_encode(v:_null_blob)'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('can dump NULL list', function()
|
it('can dump NULL list', function()
|
||||||
eq('[]', eval('json_encode(v:_null_list)'))
|
eq('[]', eval('json_encode(v:_null_list)'))
|
||||||
end)
|
end)
|
||||||
|
@ -13,6 +13,7 @@ describe('msgpack*() functions', function()
|
|||||||
it(msg, function()
|
it(msg, function()
|
||||||
nvim('set_var', 'obj', obj)
|
nvim('set_var', 'obj', obj)
|
||||||
eq(obj, eval('msgpackparse(msgpackdump(g:obj))'))
|
eq(obj, eval('msgpackparse(msgpackdump(g:obj))'))
|
||||||
|
eq(obj, eval('msgpackparse(msgpackdump(g:obj, "B"))'))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -364,8 +365,7 @@ describe('msgpack*() functions', function()
|
|||||||
command('let dumped = ["\\xC4\\x01\\n"]')
|
command('let dumped = ["\\xC4\\x01\\n"]')
|
||||||
command('let parsed = msgpackparse(dumped)')
|
command('let parsed = msgpackparse(dumped)')
|
||||||
command('let dumped2 = msgpackdump(parsed)')
|
command('let dumped2 = msgpackdump(parsed)')
|
||||||
eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed'))
|
eq({'\000'}, eval('parsed'))
|
||||||
eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary'))
|
|
||||||
eq(1, eval('dumped ==# dumped2'))
|
eq(1, eval('dumped ==# dumped2'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -392,56 +392,61 @@ describe('msgpack*() functions', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local blobstr = function(list)
|
||||||
|
local l = {}
|
||||||
|
for i,v in ipairs(list) do
|
||||||
|
l[i] = v:gsub('\n', '\000')
|
||||||
|
end
|
||||||
|
return table.concat(l, '\n')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Test msgpackparse() with a readfile()-style list and a blob argument
|
||||||
|
local parse_eq = function(expect, list_arg)
|
||||||
|
local blob_expr = '0z' .. blobstr(list_arg):gsub('(.)', function(c)
|
||||||
|
return ('%.2x'):format(c:byte())
|
||||||
|
end)
|
||||||
|
eq(expect, funcs.msgpackparse(list_arg))
|
||||||
|
command('let g:parsed = msgpackparse(' .. blob_expr .. ')')
|
||||||
|
eq(expect, eval('g:parsed'))
|
||||||
|
end
|
||||||
|
|
||||||
describe('msgpackparse() function', function()
|
describe('msgpackparse() function', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
|
|
||||||
it('restores nil as v:null', function()
|
it('restores nil as v:null', function()
|
||||||
command('let dumped = ["\\xC0"]')
|
parse_eq(eval('[v:null]'), {'\192'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq('[v:null]', eval('string(parsed)'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores boolean false as v:false', function()
|
it('restores boolean false as v:false', function()
|
||||||
command('let dumped = ["\\xC2"]')
|
parse_eq({false}, {'\194'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({false}, eval('parsed'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores boolean true as v:true', function()
|
it('restores boolean true as v:true', function()
|
||||||
command('let dumped = ["\\xC3"]')
|
parse_eq({true}, {'\195'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({true}, eval('parsed'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores FIXSTR as special dict', function()
|
it('restores FIXSTR as special dict', function()
|
||||||
command('let dumped = ["\\xa2ab"]')
|
parse_eq({{_TYPE={}, _VAL={'ab'}}}, {'\162ab'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed'))
|
|
||||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string'))
|
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores BIN 8 as string', function()
|
it('restores BIN 8 as string', function()
|
||||||
command('let dumped = ["\\xC4\\x02ab"]')
|
parse_eq({'ab'}, {'\196\002ab'})
|
||||||
eq({'ab'}, eval('msgpackparse(dumped)'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores FIXEXT1 as special dictionary', function()
|
it('restores FIXEXT1 as special dictionary', function()
|
||||||
command('let dumped = ["\\xD4\\x10", ""]')
|
parse_eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, {'\212\016', ''})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed'))
|
|
||||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext'))
|
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores MAP with BIN key as special dictionary', function()
|
it('restores MAP with BIN key as special dictionary', function()
|
||||||
command('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]')
|
parse_eq({{_TYPE={}, _VAL={{'a', ''}}}}, {'\129\196\001a\196\n'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed'))
|
|
||||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores MAP with duplicate STR keys as special dictionary', function()
|
it('restores MAP with duplicate STR keys as special dictionary', function()
|
||||||
command('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]')
|
command('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]')
|
||||||
-- FIXME Internal error bug
|
-- FIXME Internal error bug, can't use parse_eq() here
|
||||||
command('silent! let parsed = msgpackparse(dumped)')
|
command('silent! let parsed = msgpackparse(dumped)')
|
||||||
eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''},
|
eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''},
|
||||||
{{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed'))
|
{{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed'))
|
||||||
@ -451,9 +456,7 @@ describe('msgpackparse() function', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('restores MAP with MAP key as special dictionary', function()
|
it('restores MAP with MAP key as special dictionary', function()
|
||||||
command('let dumped = ["\\x81\\x80\\xC4\\n"]')
|
parse_eq({{_TYPE={}, _VAL={{{}, ''}}}}, {'\129\128\196\n'})
|
||||||
command('let parsed = msgpackparse(dumped)')
|
|
||||||
eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed'))
|
|
||||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -478,43 +481,65 @@ describe('msgpackparse() function', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a string', function()
|
it('fails to parse a string', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse("abcdefghijklmnopqrstuvwxyz")'))
|
exc_exec('call msgpackparse("abcdefghijklmnopqrstuvwxyz")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a number', function()
|
it('fails to parse a number', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse(127)'))
|
exc_exec('call msgpackparse(127)'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a dictionary', function()
|
it('fails to parse a dictionary', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse({})'))
|
exc_exec('call msgpackparse({})'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a funcref', function()
|
it('fails to parse a funcref', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse(function("tr"))'))
|
exc_exec('call msgpackparse(function("tr"))'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a partial', function()
|
it('fails to parse a partial', function()
|
||||||
command('function T() dict\nendfunction')
|
command('function T() dict\nendfunction')
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
|
exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to parse a float', function()
|
it('fails to parse a float', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob',
|
||||||
exc_exec('call msgpackparse(0.0)'))
|
exc_exec('call msgpackparse(0.0)'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails on incomplete msgpack string', function()
|
||||||
|
local expected = 'Vim(call):E475: Invalid argument: Incomplete msgpack string'
|
||||||
|
eq(expected, exc_exec([[call msgpackparse(["\xc4"])]]))
|
||||||
|
eq(expected, exc_exec([[call msgpackparse(["\xca", "\x02\x03"])]]))
|
||||||
|
eq(expected, exc_exec('call msgpackparse(0zc4)'))
|
||||||
|
eq(expected, exc_exec('call msgpackparse(0zca0a0203)'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('fails when unable to parse msgpack string', function()
|
||||||
|
local expected = 'Vim(call):E475: Invalid argument: Failed to parse msgpack string'
|
||||||
|
eq(expected, exc_exec([[call msgpackparse(["\xc1"])]]))
|
||||||
|
eq(expected, exc_exec('call msgpackparse(0zc1)'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('msgpackdump() function', function()
|
describe('msgpackdump() function', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
|
|
||||||
|
local dump_eq = function(exp_list, arg_expr)
|
||||||
|
eq(exp_list, eval('msgpackdump(' .. arg_expr .. ')'))
|
||||||
|
eq(blobstr(exp_list), eval('msgpackdump(' .. arg_expr .. ', "B")'))
|
||||||
|
end
|
||||||
|
|
||||||
it('dumps string as BIN 8', function()
|
it('dumps string as BIN 8', function()
|
||||||
nvim('set_var', 'obj', {'Test'})
|
dump_eq({'\196\004Test'}, '["Test"]')
|
||||||
eq({"\196\004Test"}, eval('msgpackdump(obj)'))
|
end)
|
||||||
|
|
||||||
|
it('dumps blob as BIN 8', function()
|
||||||
|
dump_eq({'\196\005Bl\nb!'}, '[0z426c006221]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump generic mapping with generic mapping keys and values', function()
|
it('can dump generic mapping with generic mapping keys and values', function()
|
||||||
@ -522,56 +547,56 @@ describe('msgpackdump() function', function()
|
|||||||
command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
||||||
command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
||||||
command('call add(todump._VAL, [todumpv1, todumpv2])')
|
command('call add(todump._VAL, [todumpv1, todumpv2])')
|
||||||
eq({'\129\128\128'}, eval('msgpackdump([todump])'))
|
dump_eq({'\129\128\128'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump v:true', function()
|
it('can dump v:true', function()
|
||||||
eq({'\195'}, funcs.msgpackdump({true}))
|
dump_eq({'\195'}, '[v:true]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump v:false', function()
|
it('can dump v:false', function()
|
||||||
eq({'\194'}, funcs.msgpackdump({false}))
|
dump_eq({'\194'}, '[v:false]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can v:null', function()
|
it('can dump v:null', function()
|
||||||
command('let todump = v:null')
|
dump_eq({'\192'}, '[v:null]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special bool mapping (true)', function()
|
it('can dump special bool mapping (true)', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
|
command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
|
||||||
eq({'\195'}, eval('msgpackdump([todump])'))
|
dump_eq({'\195'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special bool mapping (false)', function()
|
it('can dump special bool mapping (false)', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
|
command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
|
||||||
eq({'\194'}, eval('msgpackdump([todump])'))
|
dump_eq({'\194'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special nil mapping', function()
|
it('can dump special nil mapping', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
|
command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
|
||||||
eq({'\192'}, eval('msgpackdump([todump])'))
|
dump_eq({'\192'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special ext mapping', function()
|
it('can dump special ext mapping', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
|
command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
|
||||||
eq({'\212\005', ''}, eval('msgpackdump([todump])'))
|
dump_eq({'\212\005', ''}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special array mapping', function()
|
it('can dump special array mapping', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
|
command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
|
||||||
eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])'))
|
dump_eq({'\146\005\145\196\n'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special UINT64_MAX mapping', function()
|
it('can dump special UINT64_MAX mapping', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.integer}')
|
command('let todump = {"_TYPE": v:msgpack_types.integer}')
|
||||||
command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
|
command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
|
||||||
eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])'))
|
dump_eq({'\207\255\255\255\255\255\255\255\255'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump special INT64_MIN mapping', function()
|
it('can dump special INT64_MIN mapping', function()
|
||||||
command('let todump = {"_TYPE": v:msgpack_types.integer}')
|
command('let todump = {"_TYPE": v:msgpack_types.integer}')
|
||||||
command('let todump._VAL = [-1, 2, 0, 0]')
|
command('let todump._VAL = [-1, 2, 0, 0]')
|
||||||
eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])'))
|
dump_eq({'\211\128\n\n\n\n\n\n\n'}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to dump a function reference', function()
|
it('fails to dump a function reference', function()
|
||||||
@ -610,13 +635,13 @@ describe('msgpackdump() function', function()
|
|||||||
it('can dump dict with two same dicts inside', function()
|
it('can dump dict with two same dicts inside', function()
|
||||||
command('let inter = {}')
|
command('let inter = {}')
|
||||||
command('let todump = {"a": inter, "b": inter}')
|
command('let todump = {"a": inter, "b": inter}')
|
||||||
eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])'))
|
dump_eq({"\130\161a\128\161b\128"}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump list with two same lists inside', function()
|
it('can dump list with two same lists inside', function()
|
||||||
command('let inter = []')
|
command('let inter = []')
|
||||||
command('let todump = [inter, inter]')
|
command('let todump = [inter, inter]')
|
||||||
eq({"\146\144\144"}, eval('msgpackdump([todump])'))
|
dump_eq({"\146\144\144"}, '[todump]')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to dump a recursive list in a special dict', function()
|
it('fails to dump a recursive list in a special dict', function()
|
||||||
@ -667,9 +692,9 @@ describe('msgpackdump() function', function()
|
|||||||
exc_exec('call msgpackdump()'))
|
exc_exec('call msgpackdump()'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails when called with two arguments', function()
|
it('fails when called with three arguments', function()
|
||||||
eq('Vim(call):E118: Too many arguments for function: msgpackdump',
|
eq('Vim(call):E118: Too many arguments for function: msgpackdump',
|
||||||
exc_exec('call msgpackdump(["", ""], 1)'))
|
exc_exec('call msgpackdump(["", ""], 1, 2)'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to dump a string', function()
|
it('fails to dump a string', function()
|
||||||
@ -711,9 +736,13 @@ describe('msgpackdump() function', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump NULL string', function()
|
it('can dump NULL string', function()
|
||||||
eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])'))
|
dump_eq({'\196\n'}, '[$XXX_UNEXISTENT_VAR_XXX]')
|
||||||
eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])'))
|
dump_eq({'\196\n'}, '[{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]')
|
||||||
eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])'))
|
dump_eq({'\160'}, '[{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can dump NULL blob', function()
|
||||||
|
eq({'\196\n'}, eval('msgpackdump([v:_null_blob])'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can dump NULL list', function()
|
it('can dump NULL list', function()
|
||||||
|
@ -44,7 +44,7 @@ describe('NULL', function()
|
|||||||
-- Incorrect behaviour
|
-- Incorrect behaviour
|
||||||
-- FIXME Should error out with different message
|
-- FIXME Should error out with different message
|
||||||
null_test('makes :unlet act as if it is not a list', ':unlet L[0]',
|
null_test('makes :unlet act as if it is not a list', ':unlet L[0]',
|
||||||
'Vim(unlet):E689: Can only index a List or Dictionary')
|
'Vim(unlet):E689: Can only index a List, Dictionary or Blob')
|
||||||
|
|
||||||
-- Subjectable behaviour
|
-- Subjectable behaviour
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ describe('writefile()', function()
|
|||||||
eq('\nE118: Too many arguments for function: writefile',
|
eq('\nE118: Too many arguments for function: writefile',
|
||||||
redir_exec(('call writefile([], "%s", "b", 1)'):format(fname)))
|
redir_exec(('call writefile([], "%s", "b", 1)'):format(fname)))
|
||||||
for _, arg in ipairs({'0', '0.0', 'function("tr")', '{}', '"test"'}) do
|
for _, arg in ipairs({'0', '0.0', 'function("tr")', '{}', '"test"'}) do
|
||||||
eq('\nE686: Argument of writefile() must be a List',
|
eq('\nE475: Invalid argument: writefile() first argument must be a List or a Blob',
|
||||||
redir_exec(('call writefile(%s, "%s", "b")'):format(arg, fname)))
|
redir_exec(('call writefile(%s, "%s", "b")'):format(arg, fname)))
|
||||||
end
|
end
|
||||||
for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do
|
for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do
|
||||||
|
@ -15,7 +15,7 @@ describe('luaeval(vim.api.…)', function()
|
|||||||
describe('nvim_buf_get_lines', function()
|
describe('nvim_buf_get_lines', function()
|
||||||
it('works', function()
|
it('works', function()
|
||||||
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
||||||
eq({{_TYPE={}, _VAL={'a\nb'}}},
|
eq({'a\000b'},
|
||||||
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)'))
|
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
@ -23,7 +23,7 @@ describe('luaeval(vim.api.…)', function()
|
|||||||
it('works', function()
|
it('works', function()
|
||||||
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
||||||
eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})'))
|
eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})'))
|
||||||
eq({'abc', {_TYPE={}, _VAL={'b\na'}}, {_TYPE={}, _VAL={'a\nb'}}, 'ttt'},
|
eq({'abc', 'b\000a', 'a\000b', 'ttt'},
|
||||||
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)'))
|
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
@ -64,15 +64,18 @@ describe('luaeval(vim.api.…)', function()
|
|||||||
it('correctly converts from API objects', function()
|
it('correctly converts from API objects', function()
|
||||||
eq(1, funcs.luaeval('vim.api.nvim_eval("1")'))
|
eq(1, funcs.luaeval('vim.api.nvim_eval("1")'))
|
||||||
eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]]))
|
eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]]))
|
||||||
|
eq('Blobby', funcs.luaeval('vim.api.nvim_eval("0z426c6f626279")'))
|
||||||
eq({}, funcs.luaeval('vim.api.nvim_eval("[]")'))
|
eq({}, funcs.luaeval('vim.api.nvim_eval("[]")'))
|
||||||
eq({}, funcs.luaeval('vim.api.nvim_eval("{}")'))
|
eq({}, funcs.luaeval('vim.api.nvim_eval("{}")'))
|
||||||
eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")'))
|
eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")'))
|
||||||
|
eq('\000', funcs.luaeval('vim.api.nvim_eval("0z00")'))
|
||||||
eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")'))
|
eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")'))
|
||||||
eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")'))
|
eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")'))
|
||||||
eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")'))
|
eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")'))
|
||||||
|
|
||||||
eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]]))
|
eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]]))
|
||||||
eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]]))
|
eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]]))
|
||||||
|
eq(1, eval([[type(luaeval('vim.api.nvim_eval("0zbeef")'))]]))
|
||||||
eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]]))
|
eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]]))
|
||||||
eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]]))
|
eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]]))
|
||||||
eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]]))
|
eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]]))
|
||||||
|
@ -63,11 +63,10 @@ describe('luaeval()', function()
|
|||||||
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
|
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
describe('strings', function()
|
describe('strings with NULs', function()
|
||||||
it('are successfully converted to special dictionaries', function()
|
it('are successfully converted to blobs', function()
|
||||||
command([[let s = luaeval('"\0"')]])
|
command([[let s = luaeval('"\0"')]])
|
||||||
eq({_TYPE={}, _VAL={'\n'}}, meths.get_var('s'))
|
eq('\000', meths.get_var('s'))
|
||||||
eq(1, funcs.eval('s._TYPE is v:msgpack_types.binary'))
|
|
||||||
end)
|
end)
|
||||||
it('are successfully converted to special dictionaries in table keys',
|
it('are successfully converted to special dictionaries in table keys',
|
||||||
function()
|
function()
|
||||||
@ -76,13 +75,10 @@ describe('luaeval()', function()
|
|||||||
eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
|
eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
|
||||||
eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
||||||
end)
|
end)
|
||||||
it('are successfully converted to special dictionaries from a list',
|
it('are successfully converted to blobs from a list',
|
||||||
function()
|
function()
|
||||||
command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
|
command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
|
||||||
eq({'abc', {_TYPE={}, _VAL={'a\nb'}}, {_TYPE={}, _VAL={'c\nd'}}, 'def'},
|
eq({'abc', 'a\000b', 'c\000d', 'def'}, meths.get_var('l'))
|
||||||
meths.get_var('l'))
|
|
||||||
eq(1, funcs.eval('l[1]._TYPE is v:msgpack_types.binary'))
|
|
||||||
eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary'))
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -100,9 +96,9 @@ describe('luaeval()', function()
|
|||||||
eq(1, eval('type(luaeval("\'test\'"))'))
|
eq(1, eval('type(luaeval("\'test\'"))'))
|
||||||
|
|
||||||
eq('', funcs.luaeval('""'))
|
eq('', funcs.luaeval('""'))
|
||||||
eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']]))
|
eq('\000', funcs.luaeval([['\0']]))
|
||||||
eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']]))
|
eq('\000\n\000', funcs.luaeval([['\0\n\0']]))
|
||||||
eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]]))
|
eq(10, eval([[type(luaeval("'\\0\\n\\0'"))]]))
|
||||||
|
|
||||||
eq(true, funcs.luaeval('true'))
|
eq(true, funcs.luaeval('true'))
|
||||||
eq(false, funcs.luaeval('false'))
|
eq(false, funcs.luaeval('false'))
|
||||||
@ -122,12 +118,11 @@ describe('luaeval()', function()
|
|||||||
local level = 30
|
local level = 30
|
||||||
eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
|
eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
|
||||||
|
|
||||||
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}},
|
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, '\000\n\000\000'}}},
|
||||||
funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]]))
|
funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]]))
|
||||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]]))
|
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]]))
|
||||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]]))
|
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]]))
|
||||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]]))
|
eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, '\000\n\000\000'}}}}},
|
||||||
eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}},
|
|
||||||
funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]]))
|
funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -175,8 +170,8 @@ describe('luaeval()', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
it('correctly passes special dictionaries', function()
|
it('correctly passes special dictionaries', function()
|
||||||
eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]')))
|
eq({0, '\000\n\000'}, luaevalarg(sp('binary', '["\\n", "\\n"]')))
|
||||||
eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]')))
|
eq({0, '\000\n\000'}, luaevalarg(sp('string', '["\\n", "\\n"]')))
|
||||||
eq({0, true}, luaevalarg(sp('boolean', 1)))
|
eq({0, true}, luaevalarg(sp('boolean', 1)))
|
||||||
eq({0, false}, luaevalarg(sp('boolean', 0)))
|
eq({0, false}, luaevalarg(sp('boolean', 0)))
|
||||||
eq({0, NIL}, luaevalarg(sp('nil', 0)))
|
eq({0, NIL}, luaevalarg(sp('nil', 0)))
|
||||||
@ -458,6 +453,9 @@ describe('v:lua', function()
|
|||||||
function mymod.crashy()
|
function mymod.crashy()
|
||||||
nonexistent()
|
nonexistent()
|
||||||
end
|
end
|
||||||
|
function mymod.whatis(value)
|
||||||
|
return type(value) .. ": " .. tostring(value)
|
||||||
|
end
|
||||||
function mymod.omni(findstart, base)
|
function mymod.omni(findstart, base)
|
||||||
if findstart == 1 then
|
if findstart == 1 then
|
||||||
return 5
|
return 5
|
||||||
@ -476,6 +474,8 @@ describe('v:lua', function()
|
|||||||
eq(true, exec_lua([[return _G.val == vim.NIL]]))
|
eq(true, exec_lua([[return _G.val == vim.NIL]]))
|
||||||
eq(NIL, eval('v:lua.mymod.noisy("eval")'))
|
eq(NIL, eval('v:lua.mymod.noisy("eval")'))
|
||||||
eq("hey eval", meths.get_current_line())
|
eq("hey eval", meths.get_current_line())
|
||||||
|
eq("string: abc", eval('v:lua.mymod.whatis(0z616263)'))
|
||||||
|
eq("string: ", eval('v:lua.mymod.whatis(v:_null_blob)'))
|
||||||
|
|
||||||
eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)",
|
eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)",
|
||||||
pcall_err(eval, 'v:lua.mymod.crashy()'))
|
pcall_err(eval, 'v:lua.mymod.crashy()'))
|
||||||
|
@ -739,7 +739,7 @@ describe('lua stdlib', function()
|
|||||||
eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]]))
|
eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]]))
|
||||||
|
|
||||||
-- error handling
|
-- error handling
|
||||||
eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
|
eq({false, 'Vim:E897: List or Blob required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('vim.fn should error when calling API function', function()
|
it('vim.fn should error when calling API function', function()
|
||||||
|
@ -342,6 +342,11 @@ describe('ShaDa error handling', function()
|
|||||||
eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable name type', exc_exec(sdrcmd()))
|
eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable name type', exc_exec(sdrcmd()))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails on variable item with BIN value and type value != VAR_TYPE_BLOB', function()
|
||||||
|
wshada('\006\000\007\147\196\001\065\196\000\000')
|
||||||
|
eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable type', exc_exec(sdrcmd()))
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails on replacement item with NIL value', function()
|
it('fails on replacement item with NIL value', function()
|
||||||
wshada('\003\000\001\192')
|
wshada('\003\000\001\192')
|
||||||
eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 is not an array', exc_exec(sdrcmd()))
|
eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 is not an array', exc_exec(sdrcmd()))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- ShaDa variables saving/reading support
|
-- ShaDa variables saving/reading support
|
||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local meths, funcs, nvim_command, eq =
|
local meths, funcs, nvim_command, eq, eval =
|
||||||
helpers.meths, helpers.funcs, helpers.command, helpers.eq
|
helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.eval
|
||||||
|
|
||||||
local shada_helpers = require('test.functional.shada.helpers')
|
local shada_helpers = require('test.functional.shada.helpers')
|
||||||
local reset, clear = shada_helpers.reset, shada_helpers.clear
|
local reset, clear = shada_helpers.reset, shada_helpers.clear
|
||||||
@ -30,10 +30,12 @@ describe('ShaDa support code', function()
|
|||||||
else
|
else
|
||||||
meths.set_var(varname, varval)
|
meths.set_var(varname, varval)
|
||||||
end
|
end
|
||||||
|
local vartype = eval('type(g:' .. varname .. ')')
|
||||||
-- Exit during `reset` is not a regular exit: it does not write shada
|
-- Exit during `reset` is not a regular exit: it does not write shada
|
||||||
-- automatically
|
-- automatically
|
||||||
nvim_command('qall')
|
nvim_command('qall')
|
||||||
reset('set shada+=!')
|
reset('set shada+=!')
|
||||||
|
eq(vartype, eval('type(g:' .. varname .. ')'))
|
||||||
eq(varval, meths.get_var(varname))
|
eq(varval, meths.get_var(varname))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
@ -47,6 +49,8 @@ describe('ShaDa support code', function()
|
|||||||
autotest('false', 'FALSEVAR', false)
|
autotest('false', 'FALSEVAR', false)
|
||||||
autotest('null', 'NULLVAR', 'v:null', true)
|
autotest('null', 'NULLVAR', 'v:null', true)
|
||||||
autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true)
|
autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true)
|
||||||
|
autotest('blob', 'BLOBVAR', '0z12ab34cd', true)
|
||||||
|
autotest('blob (with NULs)', 'BLOBVARNULS', '0z004e554c7300', true)
|
||||||
|
|
||||||
it('does not read back variables without `!` in &shada', function()
|
it('does not read back variables without `!` in &shada', function()
|
||||||
meths.set_var('STRVAR', 'foo')
|
meths.set_var('STRVAR', 'foo')
|
||||||
|
@ -70,7 +70,7 @@ local function dumplog(logfile, fn, ...)
|
|||||||
if status == false then
|
if status == false then
|
||||||
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
|
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
|
||||||
local logtail = module.read_nvim_log(logfile)
|
local logtail = module.read_nvim_log(logfile)
|
||||||
error(string.format('%s\n%s', rv, logtail))
|
error(string.format('%s\n%s', tostring(rv), logtail))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function module.eq(expected, actual, context, logfile)
|
function module.eq(expected, actual, context, logfile)
|
||||||
|
@ -2531,7 +2531,7 @@ describe('typval.c', function()
|
|||||||
value='tr',
|
value='tr',
|
||||||
dict={},
|
dict={},
|
||||||
})
|
})
|
||||||
lib.tv_item_lock(p_tv, -1, true)
|
lib.tv_item_lock(p_tv, -1, true, false)
|
||||||
eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock)
|
eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock)
|
||||||
end)
|
end)
|
||||||
itp('does not change VAR_FIXED values', function()
|
itp('does not change VAR_FIXED values', function()
|
||||||
@ -2542,14 +2542,14 @@ describe('typval.c', function()
|
|||||||
d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED
|
d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED
|
||||||
l_tv.v_lock = lib.VAR_FIXED
|
l_tv.v_lock = lib.VAR_FIXED
|
||||||
l_tv.vval.v_list.lv_lock = lib.VAR_FIXED
|
l_tv.vval.v_list.lv_lock = lib.VAR_FIXED
|
||||||
lib.tv_item_lock(d_tv, 1, true)
|
lib.tv_item_lock(d_tv, 1, true, false)
|
||||||
lib.tv_item_lock(l_tv, 1, true)
|
lib.tv_item_lock(l_tv, 1, true, false)
|
||||||
eq(lib.VAR_FIXED, d_tv.v_lock)
|
eq(lib.VAR_FIXED, d_tv.v_lock)
|
||||||
eq(lib.VAR_FIXED, l_tv.v_lock)
|
eq(lib.VAR_FIXED, l_tv.v_lock)
|
||||||
eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock)
|
eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock)
|
||||||
eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock)
|
eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock)
|
||||||
lib.tv_item_lock(d_tv, 1, false)
|
lib.tv_item_lock(d_tv, 1, false, false)
|
||||||
lib.tv_item_lock(l_tv, 1, false)
|
lib.tv_item_lock(l_tv, 1, false, false)
|
||||||
eq(lib.VAR_FIXED, d_tv.v_lock)
|
eq(lib.VAR_FIXED, d_tv.v_lock)
|
||||||
eq(lib.VAR_FIXED, l_tv.v_lock)
|
eq(lib.VAR_FIXED, l_tv.v_lock)
|
||||||
eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock)
|
eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock)
|
||||||
@ -2561,9 +2561,9 @@ describe('typval.c', function()
|
|||||||
local d_tv = lua2typvalt(null_dict)
|
local d_tv = lua2typvalt(null_dict)
|
||||||
local s_tv = lua2typvalt(null_string)
|
local s_tv = lua2typvalt(null_string)
|
||||||
alloc_log:clear()
|
alloc_log:clear()
|
||||||
lib.tv_item_lock(l_tv, 1, true)
|
lib.tv_item_lock(l_tv, 1, true, false)
|
||||||
lib.tv_item_lock(d_tv, 1, true)
|
lib.tv_item_lock(d_tv, 1, true, false)
|
||||||
lib.tv_item_lock(s_tv, 1, true)
|
lib.tv_item_lock(s_tv, 1, true, false)
|
||||||
eq(null_list, typvalt2lua(l_tv))
|
eq(null_list, typvalt2lua(l_tv))
|
||||||
eq(null_dict, typvalt2lua(d_tv))
|
eq(null_dict, typvalt2lua(d_tv))
|
||||||
eq(null_string, typvalt2lua(s_tv))
|
eq(null_string, typvalt2lua(s_tv))
|
||||||
|
Loading…
Reference in New Issue
Block a user