mirror of https://github.com/vim/vim.git
patch 8.2.1744: Vim9: using ":const!" is weird
Problem: Vim9: using ":const!" is weird. Solution: Use "var" - "final" - "const" like Dart. "let" still works for now.
This commit is contained in:
parent
273af497ca
commit
30fd8204ce
|
@ -1,4 +1,4 @@
|
|||
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 17
|
||||
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 26
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
|
@ -70,7 +70,7 @@ Comments starting with # ~
|
|||
In legacy Vim script comments start with double quote. In Vim9 script
|
||||
comments start with #. >
|
||||
# declarations
|
||||
let count = 0 # number of occurrences
|
||||
var count = 0 # number of occurrences
|
||||
|
||||
The reason is that a double quote can also be the start of a string. In many
|
||||
places, especially halfway through an expression with a line break, it's hard
|
||||
|
@ -154,31 +154,32 @@ Vim9 script script-local functions are defined once when the script is sourced
|
|||
and cannot be deleted or replaced.
|
||||
|
||||
|
||||
Variable declarations with :let and :const ~
|
||||
Variable declarations with :var, :final and :const ~
|
||||
*vim9-declaration*
|
||||
Local variables need to be declared with `:let`. Local constants need to be
|
||||
declared with `:const`. We refer to both as "variables".
|
||||
Local variables need to be declared with `:var`. Local constants need to be
|
||||
declared with `:final` or `:const`. We refer to both as "variables" in this
|
||||
section.
|
||||
|
||||
Variables can be local to a script, function or code block: >
|
||||
vim9script
|
||||
let script_var = 123
|
||||
var script_var = 123
|
||||
def SomeFunc()
|
||||
let func_var = script_var
|
||||
var func_var = script_var
|
||||
if cond
|
||||
let block_var = func_var
|
||||
var block_var = func_var
|
||||
...
|
||||
|
||||
The variables are only visible in the block where they are defined and nested
|
||||
blocks. Once the block ends the variable is no longer accessible: >
|
||||
if cond
|
||||
let inner = 5
|
||||
var inner = 5
|
||||
else
|
||||
let inner = 0
|
||||
var inner = 0
|
||||
endif
|
||||
echo inner # Error!
|
||||
|
||||
The declaration must be done earlier: >
|
||||
let inner: number
|
||||
var inner: number
|
||||
if cond
|
||||
inner = 5
|
||||
else
|
||||
|
@ -186,10 +187,10 @@ The declaration must be done earlier: >
|
|||
endif
|
||||
echo inner
|
||||
|
||||
To intentionally avoid a variable being available later, a block can be used:
|
||||
>
|
||||
To intentionally hide a variable from code that follows, a block can be
|
||||
used: >
|
||||
{
|
||||
let temp = 'temp'
|
||||
var temp = 'temp'
|
||||
...
|
||||
}
|
||||
echo temp # Error!
|
||||
|
@ -197,9 +198,9 @@ To intentionally avoid a variable being available later, a block can be used:
|
|||
Declaring a variable with a type but without an initializer will initialize to
|
||||
zero, false or empty.
|
||||
|
||||
An existing variable cannot be assigned to with `:let`, since that implies a
|
||||
declaration. Global, window, tab, buffer and Vim variables can only be used
|
||||
without `:let`, because they are not really declared, they can also be deleted
|
||||
In Vim9 script `:let` cannot be used. An existing variable is assigned to
|
||||
without any command. The same for global, window, tab, buffer and Vim
|
||||
variables, because they are not really declared. They can also be deleted
|
||||
with `:unlet`.
|
||||
|
||||
Variables and functions cannot shadow previously defined or imported variables
|
||||
|
@ -209,51 +210,50 @@ Variables may shadow Ex commands, rename the variable if needed.
|
|||
Global variables and user defined functions must be prefixed with "g:", also
|
||||
at the script level. >
|
||||
vim9script
|
||||
let script_local = 'text'
|
||||
var script_local = 'text'
|
||||
g:global = 'value'
|
||||
let Funcref = g:ThatFunction
|
||||
var Funcref = g:ThatFunction
|
||||
|
||||
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
|
||||
Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be
|
||||
used to repeat a `:substitute` command.
|
||||
*vim9-const*
|
||||
In legacy Vim script "const list = []" would make the variable "list"
|
||||
immutable and also the value. Thus you cannot add items to the list. This
|
||||
differs from what many languages do. Vim9 script does it like TypeScript: only
|
||||
"list" is immutable, the value can be changed.
|
||||
|
||||
One can use `:const!` to make both the variable and the value immutable. Use
|
||||
|
||||
Constants ~
|
||||
*vim9-const* *vim9-final*
|
||||
How constants work varies between languages. Some consider a variable that
|
||||
can't be assigned another value a constant. JavaScript is an example. Others
|
||||
also make the value immutable, thus when a constant uses a list, the list
|
||||
cannot be changed. In Vim9 we can use both.
|
||||
|
||||
`:const` is used for making both the variable and the value a constant. Use
|
||||
this for composite structures that you want to make sure will not be modified.
|
||||
Example: >
|
||||
const myList = [1, 2]
|
||||
myList = [3, 4] # Error!
|
||||
myList[0] = 9 # Error!
|
||||
muList->add(3) # Error!
|
||||
|
||||
How this works: >
|
||||
vim9script
|
||||
const list = [1, 2]
|
||||
list = [3, 4] # Error!
|
||||
list[0] = 2 # OK
|
||||
`:final` is used for making only the variable a constant, the value can be
|
||||
changed. This is well known from Java. Example: >
|
||||
final myList = [1, 2]
|
||||
myList = [3, 4] # Error!
|
||||
myList[0] = 9 # OK
|
||||
muList->add(3) # OK
|
||||
|
||||
const! LIST = [1, 2]
|
||||
LIST = [3, 4] # Error!
|
||||
LIST[0] = 2 # Error!
|
||||
It is common to write constants as ALL_CAPS, but you don't have to.
|
||||
|
||||
The constant only applies to the value itself, not what it refers to. >
|
||||
cont females = ["Mary"]
|
||||
const! NAMES = [["John", "Peter"], females]
|
||||
final females = ["Mary"]
|
||||
const NAMES = [["John", "Peter"], females]
|
||||
NAMES[0] = ["Jack"] # Error!
|
||||
NAMES[0][0] = ["Jack"] # Error!
|
||||
NAMES[0][0] = "Jack" # Error!
|
||||
NAMES[1] = ["Emma"] # Error!
|
||||
Names[1][0] = "Emma" # OK, now females[0] == "Emma"
|
||||
|
||||
Rationale: TypeScript has no way to make the value immutable. One can use
|
||||
immutable types, but that quickly gets complicated for nested values. And
|
||||
with a type cast the value can be made mutable again, which means there is no
|
||||
guarantee the value won't change. Vim supports immutable values, in legacy
|
||||
script this was done with `:lockvar`. But that is an extra statement and also
|
||||
applies to nested values. Therefore the solution to use `:const!`.
|
||||
|
||||
*E1092*
|
||||
< *E1092*
|
||||
Declaring more than one variable at a time, using the unpack notation, is
|
||||
currently not supported: >
|
||||
let [v1, v2] = GetValues() # Error!
|
||||
var [v1, v2] = GetValues() # Error!
|
||||
That is because the type needs to be inferred from the list item type, which
|
||||
isn't that easy.
|
||||
|
||||
|
@ -296,7 +296,7 @@ A user defined function can be used as a function reference in an expression
|
|||
without `function()`. The argument types and return type will then be checked.
|
||||
The function must already have been defined. >
|
||||
|
||||
let Funcref = MyFunction
|
||||
var Funcref = MyFunction
|
||||
|
||||
When using `function()` the resulting type is "func", a function with any
|
||||
number of arguments and any return type. The function can be defined later.
|
||||
|
@ -307,53 +307,53 @@ Automatic line continuation ~
|
|||
In many cases it is obvious that an expression continues on the next line. In
|
||||
those cases there is no need to prefix the line with a backslash
|
||||
|line-continuation|. For example, when a list spans multiple lines: >
|
||||
let mylist = [
|
||||
var mylist = [
|
||||
'one',
|
||||
'two',
|
||||
]
|
||||
And when a dict spans multiple lines: >
|
||||
let mydict = #{
|
||||
var mydict = #{
|
||||
one: 1,
|
||||
two: 2,
|
||||
}
|
||||
Function call: >
|
||||
let result = Func(
|
||||
var result = Func(
|
||||
arg1,
|
||||
arg2
|
||||
)
|
||||
|
||||
For binary operators in expressions not in [], {} or () a line break is
|
||||
possible just before or after the operator. For example: >
|
||||
let text = lead
|
||||
var text = lead
|
||||
.. middle
|
||||
.. end
|
||||
let total = start +
|
||||
var total = start +
|
||||
end -
|
||||
correction
|
||||
let result = positive
|
||||
var result = positive
|
||||
? PosFunc(arg)
|
||||
: NegFunc(arg)
|
||||
|
||||
For a method call using "->" and a member using a dot, a line break is allowed
|
||||
before it: >
|
||||
let result = GetBuilder()
|
||||
var result = GetBuilder()
|
||||
->BuilderSetWidth(333)
|
||||
->BuilderSetHeight(777)
|
||||
->BuilderBuild()
|
||||
let result = MyDict
|
||||
var result = MyDict
|
||||
.member
|
||||
|
||||
< *E1050*
|
||||
To make it possible for the operator at the start of the line to be
|
||||
recognized, it is required to put a colon before a range. This will add
|
||||
"start" and print: >
|
||||
let result = start
|
||||
var result = start
|
||||
+ print
|
||||
Like this: >
|
||||
let result = start + print
|
||||
var result = start + print
|
||||
|
||||
This will assign "start" and print a line: >
|
||||
let result = start
|
||||
var result = start
|
||||
:+ print
|
||||
|
||||
It is also possible to split a function header over multiple lines, in between
|
||||
|
@ -411,15 +411,15 @@ The 'ignorecase' option is not used for comparators that use strings.
|
|||
White space ~
|
||||
|
||||
Vim9 script enforces proper use of white space. This is no longer allowed: >
|
||||
let var=234 # Error!
|
||||
let var= 234 # Error!
|
||||
let var =234 # Error!
|
||||
var name=234 # Error!
|
||||
var name= 234 # Error!
|
||||
var name =234 # Error!
|
||||
There must be white space before and after the "=": >
|
||||
let var = 234 # OK
|
||||
var name = 234 # OK
|
||||
White space must also be put before the # that starts a comment after a
|
||||
command: >
|
||||
let var = 234# Error!
|
||||
let var = 234 # OK
|
||||
var name = 234# Error!
|
||||
var name = 234 # OK
|
||||
|
||||
White space is required around most operators.
|
||||
|
||||
|
@ -440,7 +440,7 @@ Conditions and expressions ~
|
|||
|
||||
Conditions and expressions are mostly working like they do in JavaScript. A
|
||||
difference is made where JavaScript does not work like most people expect.
|
||||
Specifically, an empty list is falsey.
|
||||
Specifically, an empty list is falsy.
|
||||
|
||||
Any type of variable can be used as a condition, there is no error, not even
|
||||
for using a list or job. This is very much like JavaScript, but there are a
|
||||
|
@ -582,9 +582,10 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|||
It is possible to nest `:def` inside another `:def` or
|
||||
`:function` up to about 50 levels deep.
|
||||
|
||||
[!] is used as with `:function`. Note that in Vim9
|
||||
script script-local functions cannot be deleted or
|
||||
redefined later in the same script.
|
||||
[!] is used as with `:function`. Note that
|
||||
script-local functions cannot be deleted or redefined
|
||||
later in Vim9 script. They can only be removed by
|
||||
reloading the same script.
|
||||
|
||||
*:enddef*
|
||||
:enddef End of a function defined with `:def`. It should be on
|
||||
|
@ -612,14 +613,14 @@ Limitations ~
|
|||
|
||||
Local variables will not be visible to string evaluation. For example: >
|
||||
def EvalString(): list<string>
|
||||
let list = ['aa', 'bb', 'cc', 'dd']
|
||||
var list = ['aa', 'bb', 'cc', 'dd']
|
||||
return range(1, 2)->map('list[v:val]')
|
||||
enddef
|
||||
|
||||
The map argument is a string expression, which is evaluated without the
|
||||
function scope. Instead, use a lambda: >
|
||||
def EvalString(): list<string>
|
||||
let list = ['aa', 'bb', 'cc', 'dd']
|
||||
var list = ['aa', 'bb', 'cc', 'dd']
|
||||
return range(1, 2)->map({ _, v -> list[v] })
|
||||
enddef
|
||||
|
||||
|
@ -690,23 +691,23 @@ builtin types added later, similarly to user functions.
|
|||
|
||||
And classes and interfaces can be used as types: >
|
||||
:class MyClass
|
||||
:let mine: MyClass
|
||||
:var mine: MyClass
|
||||
|
||||
:interface MyInterface
|
||||
:let mine: MyInterface
|
||||
:var mine: MyInterface
|
||||
|
||||
:class MyTemplate<Targ>
|
||||
:let mine: MyTemplate<number>
|
||||
:let mine: MyTemplate<string>
|
||||
:var mine: MyTemplate<number>
|
||||
:var mine: MyTemplate<string>
|
||||
|
||||
:class MyInterface<Targ>
|
||||
:let mine: MyInterface<number>
|
||||
:let mine: MyInterface<string>
|
||||
:var mine: MyInterface<number>
|
||||
:var mine: MyInterface<string>
|
||||
{not implemented yet}
|
||||
|
||||
|
||||
Variable types and type casting *variable-types*
|
||||
|
||||
Variable types and type casting ~
|
||||
*variable-types*
|
||||
Variables declared in Vim9 script or in a `:def` function have a type, either
|
||||
specified explicitly or inferred from the initialization.
|
||||
|
||||
|
@ -716,10 +717,10 @@ compiled code the "any" type is assumed.
|
|||
|
||||
This can be a problem when the "any" type is undesired and the actual type is
|
||||
expected to always be the same. For example, when declaring a list: >
|
||||
let l: list<number> = [1, g:two]
|
||||
var l: list<number> = [1, g:two]
|
||||
This will give an error, because "g:two" has type "any". To avoid this, use a
|
||||
type cast: >
|
||||
let l: list<number> = [1, <number>g:two]
|
||||
var l: list<number> = [1, <number>g:two]
|
||||
< *type-casting*
|
||||
The compiled code will then check that "g:two" is a number at runtime and give
|
||||
an error if it isn't. This is called type casting.
|
||||
|
@ -734,12 +735,12 @@ it to a string, use the |string()| function. Or use |str2nr()| to convert a
|
|||
string to a number.
|
||||
|
||||
|
||||
Type inference *type-inference*
|
||||
|
||||
Type inference ~
|
||||
*type-inference*
|
||||
In general: Whenever the type is clear it can be omitted. For example, when
|
||||
declaring a variable and giving it a value: >
|
||||
let var = 0 # infers number type
|
||||
let var = 'hello' # infers string type
|
||||
var name = 0 # infers number type
|
||||
var name = 'hello' # infers string type
|
||||
|
||||
The type of a list and dictionary comes from the common type of the values.
|
||||
If the values all have the same type, that type is used for the list or
|
||||
|
@ -749,8 +750,8 @@ dictionary. If there is a mix of types, the "any" type is used. >
|
|||
[1, 'x', 3] list<any>
|
||||
|
||||
|
||||
Stricter type checking *type-checking*
|
||||
|
||||
Stricter type checking ~
|
||||
*type-checking*
|
||||
In legacy Vim script, where a number was expected, a string would be
|
||||
automatically converted to a number. This was convenient for an actual number
|
||||
such as "123", but leads to unexpected problems (but no error message) if the
|
||||
|
@ -766,7 +767,7 @@ an error, thus breaking backwards compatibility. For example:
|
|||
|
||||
==============================================================================
|
||||
|
||||
5. Namespace, Import and Export
|
||||
5. Namespace, Import and Export
|
||||
*vim9script* *vim9-export* *vim9-import*
|
||||
|
||||
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
||||
|
@ -786,7 +787,7 @@ appear as the first statement in the file. It tells Vim to interpret the
|
|||
script in its own namespace, instead of the global namespace. If a file
|
||||
starts with: >
|
||||
vim9script
|
||||
let myvar = 'yes'
|
||||
var myvar = 'yes'
|
||||
Then "myvar" will only exist in this file. While without `vim9script` it would
|
||||
be available as `g:myvar` from any other script and function.
|
||||
|
||||
|
@ -809,7 +810,9 @@ Export ~
|
|||
*:export* *:exp*
|
||||
Exporting an item can be written as: >
|
||||
export const EXPORTED_CONST = 1234
|
||||
export let someValue = ...
|
||||
export var someValue = ...
|
||||
export final someValue = ...
|
||||
export const someValue = ...
|
||||
export def MyFunc() ...
|
||||
export class MyClass ...
|
||||
|
||||
|
@ -880,7 +883,7 @@ actually needed. A recommended mechanism:
|
|||
vim9script
|
||||
import FilterFunc from "../import/someother.vim"
|
||||
def searchfor#Stuff(arg: string)
|
||||
let filtered = FilterFunc(arg)
|
||||
var filtered = FilterFunc(arg)
|
||||
...
|
||||
< This goes in .../autoload/searchfor.vim. "searchfor" in the file name
|
||||
must be exactly the same as the prefix for the function name, that is how
|
||||
|
@ -889,7 +892,7 @@ actually needed. A recommended mechanism:
|
|||
3. Other functionality, possibly shared between plugins, contains the exported
|
||||
items and any private items. >
|
||||
vim9script
|
||||
let localVar = 'local'
|
||||
var localVar = 'local'
|
||||
export def FilterFunc(arg: string): string
|
||||
...
|
||||
< This goes in .../import/someother.vim.
|
||||
|
@ -909,7 +912,7 @@ namespace will be used for the imported item, even when "s:" is not specified.
|
|||
6. Future work: classes *vim9-classes*
|
||||
|
||||
Above "class" was mentioned a few times, but it has not been implemented yet.
|
||||
Most of Vim9 script can be created without this funcionality, and since
|
||||
Most of Vim9 script can be created without this functionality, and since
|
||||
implementing classes is going to be a lot of work, it is left for the future.
|
||||
For now we'll just make sure classes can be added later.
|
||||
|
||||
|
@ -941,7 +944,7 @@ invoke callbacks and handle timeouts and errors.
|
|||
|
||||
The :def command ~
|
||||
|
||||
Plugin writers have asked for a much faster Vim script. Investigations have
|
||||
Plugin writers have asked for much faster Vim script. Investigations have
|
||||
shown that keeping the existing semantics of function calls make this close to
|
||||
impossible, because of the overhead involved with calling a function, setting
|
||||
up the local function scope and executing lines. There are many details that
|
||||
|
@ -952,7 +955,7 @@ much overhead that cannot be avoided.
|
|||
Therefore the `:def` method to define a new-style function had to be added,
|
||||
which allows for a function with different semantics. Most things still work
|
||||
as before, but some parts do not. A new way to define a function was
|
||||
considered the best way to separate the old-style code from Vim9 script code.
|
||||
considered the best way to separate the legacy style code from Vim9 style code.
|
||||
|
||||
Using "def" to define a function comes from Python. Other languages use
|
||||
"function" which clashes with legacy Vim script.
|
||||
|
@ -968,12 +971,12 @@ instruction, at execution time the instruction would have to inspect the type
|
|||
of the arguments and decide what kind of addition to do. And when the
|
||||
type is dictionary throw an error. If the types are known to be numbers then
|
||||
an "add number" instruction can be used, which is faster. The error can be
|
||||
given at compile time, no error handling is needed at runtime, adding two
|
||||
numbers cannot fail.
|
||||
given at compile time, no error handling is needed at runtime, since adding
|
||||
two numbers cannot fail.
|
||||
|
||||
The syntax for types is similar to Java, since it is easy to understand and
|
||||
widely used. The type names are what were used in Vim before, with some
|
||||
additions such as "void" and "bool".
|
||||
The syntax for types, using <type> for compound types, is similar to Java. It
|
||||
is easy to understand and widely used. The type names are what were used in
|
||||
Vim before, with some additions such as "void" and "bool".
|
||||
|
||||
|
||||
Removing clutter and weirdness ~
|
||||
|
@ -981,10 +984,10 @@ Removing clutter and weirdness ~
|
|||
Once decided that `:def` functions have different syntax than legacy functions,
|
||||
we are free to add improvements to make the code more familiar for users who
|
||||
know popular programming languages. In other words: remove weird things that
|
||||
only Vim uses.
|
||||
only Vim does.
|
||||
|
||||
We can also remove clutter, mainly things that were done to make Vim script
|
||||
backwards compatible with good old Vi commands.
|
||||
backwards compatible with the good old Vi commands.
|
||||
|
||||
Examples:
|
||||
- Drop `:call` for calling a function and `:eval` for manipulating data.
|
||||
|
@ -993,44 +996,26 @@ Examples:
|
|||
|
||||
However, this does require that some things need to change:
|
||||
- Comments start with # instead of ", to avoid confusing them with strings.
|
||||
This is good anyway, it is known from several popular languages.
|
||||
- Ex command ranges need to be prefixed with a colon, to avoid confusion with
|
||||
expressions (single quote can be a string or a mark, "/" can be divide or a
|
||||
search command, etc.).
|
||||
|
||||
Goal is to limit the differences. A good criteria is that when the old syntax
|
||||
is used you are very likely to get an error message.
|
||||
is accidentally used you are very likely to get an error message.
|
||||
|
||||
|
||||
TypeScript syntax and semantics ~
|
||||
Syntax and semantics from popular languages ~
|
||||
|
||||
Script writers have complained that the Vim script syntax is unexpectedly
|
||||
different from what they are used to. To reduce this complaint popular
|
||||
languages are used as an example. At the same time, we do not want to abandon
|
||||
the well-known parts of legacy Vim script.
|
||||
|
||||
Since Vim already uses `:let` and `:const` and optional type checking is
|
||||
desirable, the JavaScript/TypeScript syntax fits best for variable
|
||||
declarations: >
|
||||
const greeting = 'hello' # string type is inferred
|
||||
let name: string
|
||||
...
|
||||
name = 'John'
|
||||
|
||||
Expression evaluation was already close to what JavaScript and other languages
|
||||
are doing. Some details are unexpected and can be fixed. For example how the
|
||||
|| and && operators work. Legacy Vim script: >
|
||||
let value = 44
|
||||
...
|
||||
let result = value || 0 # result == 1
|
||||
|
||||
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
||||
let value = 44
|
||||
...
|
||||
let result = value || 0 # result == 44
|
||||
|
||||
Another reason why TypeScript can be used as an example for Vim9 script is the
|
||||
For many things TypeScript is followed. It's a recent language that is
|
||||
gaining popularity and has similarities with Vim script. It also has a
|
||||
mix of static typing (a variable always has a known value type) and dynamic
|
||||
typing (a variable can have different types, this hanges at runtime). Since
|
||||
typing (a variable can have different types, this changes at runtime). Since
|
||||
legacy Vim script is dynamically typed and a lot of existing functionality
|
||||
(esp. builtin functions) depends on that, while static typing allows for much
|
||||
faster execution, we need to have this mix in Vim9 script.
|
||||
|
@ -1054,7 +1039,7 @@ Specific items from TypeScript we avoid:
|
|||
- TypeScript can use an expression like "99 || 'yes'" in a condition, but
|
||||
cannot assign the value to a boolean. That is inconsistent and can be
|
||||
annoying. Vim recognizes an expression with && or || and allows using the
|
||||
result as a bool.
|
||||
result as a bool. TODO: to be reconsidered
|
||||
- TypeScript considers an empty string as Falsy, but an empty list or dict as
|
||||
Truthy. That is inconsistent. In Vim an empty list and dict are also
|
||||
Falsy.
|
||||
|
@ -1063,6 +1048,80 @@ Specific items from TypeScript we avoid:
|
|||
which is more flexible, but is only checked at runtime.
|
||||
|
||||
|
||||
Declarations ~
|
||||
|
||||
Legacy Vim script uses `:let` for every assignment, while in Vim9 declarations
|
||||
are used. That is different, thus it's good to use a different command:
|
||||
`:var`. This is used in many languages. The semantics might be slightly
|
||||
different, but it's easily recognized as a declaration.
|
||||
|
||||
Using `:const` for constants is common, but the semantics vary. Some
|
||||
languages only make the variable immutable, others also make the value
|
||||
immutable. Since "final" is well known from Java for only making the variable
|
||||
immutable we decided to use that. And then `:const` can be used for making
|
||||
both immutable. This was also used in legacy Vim script and the meaning is
|
||||
almost the same.
|
||||
|
||||
What we end up with is very similar to Dart: >
|
||||
:var name # mutable variable and value
|
||||
:final name # immutable variable, mutable value
|
||||
:const name # immutable variable and value
|
||||
|
||||
Since legacy and Vim9 script will be mixed and global variables will be
|
||||
shared, optional type checking is desirable. Also, type inference will avoid
|
||||
the need for specifying the type in many cases. The TypeScript syntax fits
|
||||
best for adding types to declarations: >
|
||||
var name: string # string type is specified
|
||||
...
|
||||
name = 'John'
|
||||
const greeting = 'hello' # string type is inferred
|
||||
|
||||
This is how we put types in a declaration: >
|
||||
var mylist: list<string>
|
||||
final mylist: list<string> = ['foo']
|
||||
def Func(arg1: number, arg2: string): bool
|
||||
|
||||
Two alternatives were considered:
|
||||
1. Put the type before the name, like Dart: >
|
||||
var list<string> mylist
|
||||
final list<string> mylist = ['foo']
|
||||
def Func(number arg1, string arg2) bool
|
||||
2. Put the type after the variable name, but do not use a colon, like Go: >
|
||||
var mylist list<string>
|
||||
final mylist list<string> = ['foo']
|
||||
def Func(arg1 number, arg2 string) bool
|
||||
|
||||
The first is more familiar for anyone used to C or Java. The second one
|
||||
doesn't really has an advantage over the first, so let's discard the second.
|
||||
|
||||
Since we use type inference the type can be left out when it can be inferred
|
||||
from the value. This means that after `var` we don't know if a type or a name
|
||||
follows. That makes parsing harder, not only for Vim but also for humans.
|
||||
Also, it will not be allowed to use a variable name that could be a type name,
|
||||
using `var string string` is too confusing.
|
||||
|
||||
The chosen syntax, using a colon to separate the name from the type, adds
|
||||
punctuation, but it actually makes it easier to recognize the parts of a
|
||||
declaration.
|
||||
|
||||
|
||||
Expressions ~
|
||||
|
||||
Expression evaluation was already close to what JavaScript and other languages
|
||||
are doing. Some details are unexpected and can be fixed. For example how the
|
||||
|| and && operators work. Legacy Vim script: >
|
||||
var value = 44
|
||||
...
|
||||
var result = value || 0 # result == 1
|
||||
|
||||
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
||||
var value = 44
|
||||
...
|
||||
var result = value || 0 # result == 44
|
||||
|
||||
TODO: the semantics of || and && need to be reconsidered.
|
||||
|
||||
|
||||
Import and Export ~
|
||||
|
||||
A problem of legacy Vim script is that by default all functions and variables
|
||||
|
@ -1122,7 +1181,7 @@ only reported then. In case these errors should be found early, e.g. when
|
|||
testing, the `:defcompile` command will help out.
|
||||
|
||||
|
||||
Why not use an embeded language? ~
|
||||
Why not use an embedded language? ~
|
||||
|
||||
Vim supports interfaces to Perl, Python, Lua, Tcl and a few others. But
|
||||
these interfaces have never become widely used, for various reasons. When
|
||||
|
|
|
@ -1513,8 +1513,10 @@ set_one_cmd_context(
|
|||
break;
|
||||
#endif
|
||||
#ifdef FEAT_EVAL
|
||||
case CMD_final:
|
||||
case CMD_const:
|
||||
case CMD_let:
|
||||
case CMD_var:
|
||||
case CMD_if:
|
||||
case CMD_elseif:
|
||||
case CMD_while:
|
||||
|
|
|
@ -270,4 +270,8 @@ EXTERN char e_variable_is_locked_str[]
|
|||
INIT(= N_("E1122: Variable is locked: %s"));
|
||||
EXTERN char e_missing_comma_before_argument_str[]
|
||||
INIT(= N_("E1123: Missing comma before argument: %s"));
|
||||
EXTERN char e_str_cannot_be_used_in_legacy_vim_script[]
|
||||
INIT(= N_("E1124: \"%s\" cannot be used in legacy Vim script"));
|
||||
EXTERN char e_final_requires_a_value[]
|
||||
INIT(= N_("E1125: Final requires a value"));
|
||||
#endif
|
||||
|
|
22
src/eval.c
22
src/eval.c
|
@ -1213,7 +1213,7 @@ set_var_lval(
|
|||
char_u *endp,
|
||||
typval_T *rettv,
|
||||
int copy,
|
||||
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
|
||||
char_u *op)
|
||||
{
|
||||
int cc;
|
||||
|
@ -1281,7 +1281,7 @@ set_var_lval(
|
|||
{
|
||||
typval_T tv;
|
||||
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_(e_cannot_mod));
|
||||
*endp = cc;
|
||||
|
@ -1319,7 +1319,7 @@ set_var_lval(
|
|||
listitem_T *ll_li = lp->ll_li;
|
||||
int ll_n1 = lp->ll_n1;
|
||||
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock a range"));
|
||||
return;
|
||||
|
@ -1378,7 +1378,7 @@ set_var_lval(
|
|||
/*
|
||||
* Assign to a List or Dictionary item.
|
||||
*/
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock a list or dict"));
|
||||
return;
|
||||
|
@ -1688,7 +1688,7 @@ next_for_item(void *fi_void, char_u *arg)
|
|||
{
|
||||
forinfo_T *fi = (forinfo_T *)fi_void;
|
||||
int result;
|
||||
int flag = in_vim9script() ? LET_NO_COMMAND : 0;
|
||||
int flag = in_vim9script() ? ASSIGN_NO_DECL : 0;
|
||||
listitem_T *item;
|
||||
|
||||
if (fi->fi_blob != NULL)
|
||||
|
@ -1741,11 +1741,12 @@ set_context_for_expression(
|
|||
char_u *arg,
|
||||
cmdidx_T cmdidx)
|
||||
{
|
||||
int got_eq = FALSE;
|
||||
int has_expr = cmdidx != CMD_let && cmdidx != CMD_var;
|
||||
int c;
|
||||
char_u *p;
|
||||
|
||||
if (cmdidx == CMD_let || cmdidx == CMD_const)
|
||||
if (cmdidx == CMD_let || cmdidx == CMD_var
|
||||
|| cmdidx == CMD_const || cmdidx == CMD_final)
|
||||
{
|
||||
xp->xp_context = EXPAND_USER_VARS;
|
||||
if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL)
|
||||
|
@ -1774,8 +1775,7 @@ set_context_for_expression(
|
|||
if (c == '&')
|
||||
{
|
||||
++xp->xp_pattern;
|
||||
xp->xp_context = cmdidx != CMD_let || got_eq
|
||||
? EXPAND_EXPRESSION : EXPAND_NOTHING;
|
||||
xp->xp_context = has_expr ? EXPAND_EXPRESSION : EXPAND_NOTHING;
|
||||
}
|
||||
else if (c != ' ')
|
||||
{
|
||||
|
@ -1792,7 +1792,7 @@ set_context_for_expression(
|
|||
}
|
||||
else if (c == '=')
|
||||
{
|
||||
got_eq = TRUE;
|
||||
has_expr = TRUE;
|
||||
xp->xp_context = EXPAND_EXPRESSION;
|
||||
}
|
||||
else if (c == '#'
|
||||
|
@ -1808,7 +1808,7 @@ set_context_for_expression(
|
|||
// Function name can start with "<SNR>" and contain '#'.
|
||||
break;
|
||||
}
|
||||
else if (cmdidx != CMD_let || got_eq)
|
||||
else if (has_expr)
|
||||
{
|
||||
if (c == '"') // string
|
||||
{
|
||||
|
|
|
@ -668,6 +668,25 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
|
|||
return l;
|
||||
}
|
||||
|
||||
/*
|
||||
* Vim9 variable declaration:
|
||||
* ":var name"
|
||||
* ":var name: type"
|
||||
* ":var name = expr"
|
||||
* ":var name: type = expr"
|
||||
* etc.
|
||||
*/
|
||||
void
|
||||
ex_var(exarg_T *eap)
|
||||
{
|
||||
if (!in_vim9script())
|
||||
{
|
||||
semsg(_(e_str_cannot_be_used_in_legacy_vim_script), ":var");
|
||||
return;
|
||||
}
|
||||
ex_let(eap);
|
||||
}
|
||||
|
||||
/*
|
||||
* ":let" list all variable values
|
||||
* ":let var1 var2" list variable values
|
||||
|
@ -683,6 +702,9 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
|
|||
* ":let var =<< ..." heredoc
|
||||
* ":let var: string" Vim9 declaration
|
||||
*
|
||||
* ":final var = expr" assignment command.
|
||||
* ":final [var1, var2] = expr" unpack list.
|
||||
*
|
||||
* ":const" list all variable values
|
||||
* ":const var1 var2" list variable values
|
||||
* ":const var = expr" assignment command.
|
||||
|
@ -702,14 +724,22 @@ ex_let(exarg_T *eap)
|
|||
int first = TRUE;
|
||||
int concat;
|
||||
int has_assign;
|
||||
int flags = eap->cmdidx == CMD_const ? LET_IS_CONST : 0;
|
||||
int flags = eap->cmdidx == CMD_const ? ASSIGN_CONST : 0;
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
if (eap->cmdidx == CMD_final && !vim9script)
|
||||
{
|
||||
// In legacy Vim script ":final" is short for ":finally".
|
||||
ex_finally(eap);
|
||||
return;
|
||||
}
|
||||
if (eap->cmdidx == CMD_const && !vim9script && !eap->forceit)
|
||||
// In legacy Vim script ":const" works like ":final".
|
||||
eap->cmdidx = CMD_final;
|
||||
|
||||
// detect Vim9 assignment without ":let" or ":const"
|
||||
if (eap->arg == eap->cmd)
|
||||
flags |= LET_NO_COMMAND;
|
||||
if (eap->forceit)
|
||||
flags |= LET_FORCEIT;
|
||||
flags |= ASSIGN_NO_DECL;
|
||||
|
||||
argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
|
||||
if (argend == NULL)
|
||||
|
@ -787,7 +817,7 @@ ex_let(exarg_T *eap)
|
|||
op[1] = NUL;
|
||||
if (*expr != '=')
|
||||
{
|
||||
if (vim9script && (flags & LET_NO_COMMAND) == 0)
|
||||
if (vim9script && (flags & ASSIGN_NO_DECL) == 0)
|
||||
{
|
||||
// +=, /=, etc. require an existing variable
|
||||
semsg(_(e_cannot_use_operator_on_new_variable), eap->arg);
|
||||
|
@ -860,7 +890,7 @@ ex_let_vars(
|
|||
int copy, // copy values from "tv", don't move
|
||||
int semicolon, // from skip_var_list()
|
||||
int var_count, // from skip_var_list()
|
||||
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
|
||||
char_u *op)
|
||||
{
|
||||
char_u *arg = arg_start;
|
||||
|
@ -1215,7 +1245,7 @@ ex_let_one(
|
|||
char_u *arg, // points to variable name
|
||||
typval_T *tv, // value to assign to variable
|
||||
int copy, // copy value from "tv"
|
||||
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
|
||||
char_u *endchars, // valid chars after variable name or NULL
|
||||
char_u *op) // "+", "-", "." or NULL
|
||||
{
|
||||
|
@ -1227,7 +1257,7 @@ ex_let_one(
|
|||
int opt_flags;
|
||||
char_u *tofree = NULL;
|
||||
|
||||
if (in_vim9script() && (flags & LET_NO_COMMAND) == 0
|
||||
if (in_vim9script() && (flags & ASSIGN_NO_DECL) == 0
|
||||
&& vim_strchr((char_u *)"$@&", *arg) != NULL)
|
||||
{
|
||||
vim9_declare_error(arg);
|
||||
|
@ -1237,7 +1267,7 @@ ex_let_one(
|
|||
// ":let $VAR = expr": Set environment variable.
|
||||
if (*arg == '$')
|
||||
{
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock an environment variable"));
|
||||
return NULL;
|
||||
|
@ -1289,7 +1319,7 @@ ex_let_one(
|
|||
// ":let &g:option = expr": Set global option value.
|
||||
else if (*arg == '&')
|
||||
{
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_(e_const_option));
|
||||
return NULL;
|
||||
|
@ -1373,7 +1403,7 @@ ex_let_one(
|
|||
// ":let @r = expr": Set register contents.
|
||||
else if (*arg == '@')
|
||||
{
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock a register"));
|
||||
return NULL;
|
||||
|
@ -2926,7 +2956,7 @@ set_var(
|
|||
typval_T *tv,
|
||||
int copy) // make copy of value in "tv"
|
||||
{
|
||||
set_var_const(name, NULL, tv, copy, LET_NO_COMMAND);
|
||||
set_var_const(name, NULL, tv, copy, ASSIGN_NO_DECL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2940,7 +2970,7 @@ set_var_const(
|
|||
type_T *type,
|
||||
typval_T *tv_arg,
|
||||
int copy, // make copy of value in "tv"
|
||||
int flags) // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||
int flags) // ASSIGN_CONST, ASSIGN_NO_DECL
|
||||
{
|
||||
typval_T *tv = tv_arg;
|
||||
typval_T bool_tv;
|
||||
|
@ -2960,7 +2990,7 @@ set_var_const(
|
|||
|
||||
if (vim9script
|
||||
&& !is_script_local
|
||||
&& (flags & LET_NO_COMMAND) == 0
|
||||
&& (flags & ASSIGN_NO_DECL) == 0
|
||||
&& name[1] == ':')
|
||||
{
|
||||
vim9_declare_error(name);
|
||||
|
@ -2990,7 +3020,7 @@ set_var_const(
|
|||
{
|
||||
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
|
||||
{
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_(e_cannot_mod));
|
||||
goto failed;
|
||||
|
@ -2998,7 +3028,7 @@ set_var_const(
|
|||
|
||||
if (is_script_local && vim9script)
|
||||
{
|
||||
if ((flags & LET_NO_COMMAND) == 0)
|
||||
if ((flags & ASSIGN_NO_DECL) == 0)
|
||||
{
|
||||
semsg(_(e_redefining_script_item_str), name);
|
||||
goto failed;
|
||||
|
@ -3094,7 +3124,7 @@ set_var_const(
|
|||
goto failed;
|
||||
}
|
||||
di->di_flags = DI_FLAGS_ALLOC;
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
di->di_flags |= DI_FLAGS_LOCK;
|
||||
|
||||
if (is_script_local && vim9script)
|
||||
|
@ -3113,7 +3143,7 @@ set_var_const(
|
|||
sv->sv_type = typval2type(tv, &si->sn_type_list);
|
||||
else
|
||||
sv->sv_type = type;
|
||||
sv->sv_const = (flags & LET_IS_CONST);
|
||||
sv->sv_const = (flags & ASSIGN_CONST);
|
||||
sv->sv_export = is_export;
|
||||
++si->sn_var_vals.ga_len;
|
||||
|
||||
|
@ -3132,8 +3162,8 @@ set_var_const(
|
|||
init_tv(tv);
|
||||
}
|
||||
|
||||
// ":const var = val" locks the value; in Vim9 script only with ":const!"
|
||||
if ((flags & LET_IS_CONST) && (!vim9script || (flags & LET_FORCEIT)))
|
||||
// ":const var = val" locks the value
|
||||
if (flags & ASSIGN_CONST)
|
||||
// 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.
|
||||
|
|
|
@ -11,26 +11,26 @@ static const unsigned short cmdidxs1[26] =
|
|||
/* d */ 108,
|
||||
/* e */ 133,
|
||||
/* f */ 156,
|
||||
/* g */ 172,
|
||||
/* h */ 178,
|
||||
/* i */ 187,
|
||||
/* j */ 206,
|
||||
/* k */ 208,
|
||||
/* l */ 213,
|
||||
/* m */ 275,
|
||||
/* n */ 293,
|
||||
/* o */ 313,
|
||||
/* p */ 325,
|
||||
/* q */ 364,
|
||||
/* r */ 367,
|
||||
/* s */ 387,
|
||||
/* t */ 456,
|
||||
/* u */ 501,
|
||||
/* v */ 512,
|
||||
/* w */ 531,
|
||||
/* x */ 545,
|
||||
/* y */ 555,
|
||||
/* z */ 556
|
||||
/* g */ 173,
|
||||
/* h */ 179,
|
||||
/* i */ 188,
|
||||
/* j */ 207,
|
||||
/* k */ 209,
|
||||
/* l */ 214,
|
||||
/* m */ 276,
|
||||
/* n */ 294,
|
||||
/* o */ 314,
|
||||
/* p */ 326,
|
||||
/* q */ 365,
|
||||
/* r */ 368,
|
||||
/* s */ 388,
|
||||
/* t */ 457,
|
||||
/* u */ 502,
|
||||
/* v */ 513,
|
||||
/* w */ 533,
|
||||
/* x */ 547,
|
||||
/* y */ 557,
|
||||
/* z */ 558
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -46,7 +46,7 @@ static const unsigned char cmdidxs2[26][26] =
|
|||
/* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 37, 40, 46, 56, 58, 59, 60, 0, 62, 0, 65, 0, 0, 0 },
|
||||
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 19, 0, 0, 20, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 17, 0, 18, 0, 0 },
|
||||
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0 },
|
||||
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 },
|
||||
/* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 },
|
||||
/* h */ { 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* i */ { 1, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0, 0, 0, 0, 0, 14, 0, 16, 0, 0, 0, 0, 0 },
|
||||
|
@ -62,11 +62,11 @@ static const unsigned char cmdidxs2[26][26] =
|
|||
/* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 50, 0, 51, 0, 63, 64, 0, 65, 0 },
|
||||
/* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 0, 0 },
|
||||
/* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 10, 13, 0, 0, 0, 0, 16, 0, 17, 0, 0, 0, 0, 0 },
|
||||
/* v */ { 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 11, 14, 0, 0, 0, 0, 17, 0, 18, 0, 0, 0, 0, 0 },
|
||||
/* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 12, 13, 0, 0, 0, 0 },
|
||||
/* x */ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 7, 0, 0, 8, 0, 0, 0, 0, 0 },
|
||||
/* y */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static const int command_count = 569;
|
||||
static const int command_count = 571;
|
||||
|
|
|
@ -592,6 +592,9 @@ EXCMD(CMD_filter, "filter", ex_wrongmodifier,
|
|||
EXCMD(CMD_find, "find", ex_find,
|
||||
EX_RANGE|EX_BANG|EX_FILE1|EX_CMDARG|EX_ARGOPT|EX_TRLBAR|EX_NEEDARG,
|
||||
ADDR_OTHER),
|
||||
EXCMD(CMD_final, "final", ex_let,
|
||||
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
EXCMD(CMD_finally, "finally", ex_finally,
|
||||
EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
|
@ -1648,6 +1651,9 @@ EXCMD(CMD_update, "update", ex_update,
|
|||
EXCMD(CMD_vglobal, "vglobal", ex_global,
|
||||
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_LINES),
|
||||
EXCMD(CMD_var, "var", ex_var,
|
||||
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
EXCMD(CMD_version, "version", ex_version,
|
||||
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
|
|
|
@ -2421,6 +2421,7 @@ do_one_cmd(
|
|||
case CMD_eval:
|
||||
case CMD_execute:
|
||||
case CMD_filter:
|
||||
case CMD_final:
|
||||
case CMD_help:
|
||||
case CMD_hide:
|
||||
case CMD_ijump:
|
||||
|
@ -2442,9 +2443,9 @@ do_one_cmd(
|
|||
case CMD_noswapfile:
|
||||
case CMD_perl:
|
||||
case CMD_psearch:
|
||||
case CMD_python:
|
||||
case CMD_py3:
|
||||
case CMD_python3:
|
||||
case CMD_python:
|
||||
case CMD_return:
|
||||
case CMD_rightbelow:
|
||||
case CMD_ruby:
|
||||
|
@ -2460,6 +2461,7 @@ do_one_cmd(
|
|||
case CMD_topleft:
|
||||
case CMD_unlet:
|
||||
case CMD_unlockvar:
|
||||
case CMD_var:
|
||||
case CMD_verbose:
|
||||
case CMD_vertical:
|
||||
case CMD_wincmd:
|
||||
|
@ -3244,7 +3246,7 @@ find_ex_command(
|
|||
if (skip_expr(&after) == OK
|
||||
&& (*after == '='
|
||||
|| (*after != NUL && after[1] == '=')))
|
||||
eap->cmdidx = CMD_let;
|
||||
eap->cmdidx = CMD_var;
|
||||
else
|
||||
eap->cmdidx = CMD_eval;
|
||||
--emsg_silent;
|
||||
|
@ -3268,7 +3270,7 @@ find_ex_command(
|
|||
}
|
||||
if (p > eap->cmd && *skipwhite(p) == '=')
|
||||
{
|
||||
eap->cmdidx = CMD_let;
|
||||
eap->cmdidx = CMD_var;
|
||||
return eap->cmd;
|
||||
}
|
||||
}
|
||||
|
@ -3287,7 +3289,7 @@ find_ex_command(
|
|||
|| *eap->cmd == '@'
|
||||
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
|
||||
{
|
||||
eap->cmdidx = CMD_let;
|
||||
eap->cmdidx = CMD_var;
|
||||
return eap->cmd;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ int get_spellword(list_T *list, char_u **pp);
|
|||
void prepare_vimvar(int idx, typval_T *save_tv);
|
||||
void restore_vimvar(int idx, typval_T *save_tv);
|
||||
list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get);
|
||||
void ex_var(exarg_T *eap);
|
||||
void ex_let(exarg_T *eap);
|
||||
int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int flags, char_u *op);
|
||||
char_u *skip_var_list(char_u *arg, int include_type, int *var_count, int *semicolon, int silent);
|
||||
|
|
|
@ -12,30 +12,30 @@ let g:alist = [7]
|
|||
let g:astring = 'text'
|
||||
|
||||
def Test_assignment_bool()
|
||||
let bool1: bool = true
|
||||
var bool1: bool = true
|
||||
assert_equal(v:true, bool1)
|
||||
let bool2: bool = false
|
||||
var bool2: bool = false
|
||||
assert_equal(v:false, bool2)
|
||||
|
||||
let bool3: bool = 0
|
||||
var bool3: bool = 0
|
||||
assert_equal(false, bool3)
|
||||
let bool4: bool = 1
|
||||
var bool4: bool = 1
|
||||
assert_equal(true, bool4)
|
||||
|
||||
let bool5: bool = 'yes' && 'no'
|
||||
var bool5: bool = 'yes' && 'no'
|
||||
assert_equal(true, bool5)
|
||||
let bool6: bool = [] && 99
|
||||
var bool6: bool = [] && 99
|
||||
assert_equal(false, bool6)
|
||||
let bool7: bool = [] || #{a: 1} && 99
|
||||
var bool7: bool = [] || #{a: 1} && 99
|
||||
assert_equal(true, bool7)
|
||||
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def GetFlag(): bool
|
||||
let flag: bool = 1
|
||||
var flag: bool = 1
|
||||
return flag
|
||||
enddef
|
||||
let flag: bool = GetFlag()
|
||||
var flag: bool = GetFlag()
|
||||
assert_equal(true, flag)
|
||||
flag = 0
|
||||
assert_equal(false, flag)
|
||||
|
@ -47,41 +47,42 @@ def Test_assignment_bool()
|
|||
assert_equal(false, flag)
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
CheckDefAndScriptFailure(['let x: bool = 2'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['let x: bool = -1'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['let x: bool = [1]'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['let x: bool = {}'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['let x: bool = "x"'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['var x: bool = 2'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['var x: bool = -1'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['var x: bool = [1]'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['var x: bool = {}'], 'E1012:')
|
||||
CheckDefAndScriptFailure(['var x: bool = "x"'], 'E1012:')
|
||||
enddef
|
||||
|
||||
def Test_syntax()
|
||||
let var = 234
|
||||
let other: list<string> = ['asdf']
|
||||
var var = 234
|
||||
var other: list<string> = ['asdf']
|
||||
enddef
|
||||
|
||||
def Test_assignment()
|
||||
CheckDefFailure(['let x:string'], 'E1069:')
|
||||
CheckDefFailure(['let x:string = "x"'], 'E1069:')
|
||||
CheckDefFailure(['let a:string = "x"'], 'E1069:')
|
||||
CheckDefFailure(['let lambda = {-> "lambda"}'], 'E704:')
|
||||
CheckDefFailure(['var x:string'], 'E1069:')
|
||||
CheckDefFailure(['var x:string = "x"'], 'E1069:')
|
||||
CheckDefFailure(['var a:string = "x"'], 'E1069:')
|
||||
CheckDefFailure(['var lambda = {-> "lambda"}'], 'E704:')
|
||||
CheckScriptFailure(['var x = "x"'], 'E1124:')
|
||||
|
||||
let nr: number = 1234
|
||||
CheckDefFailure(['let nr: number = "asdf"'], 'E1012:')
|
||||
var nr: number = 1234
|
||||
CheckDefFailure(['var nr: number = "asdf"'], 'E1012:')
|
||||
|
||||
let a: number = 6 #comment
|
||||
var a: number = 6 #comment
|
||||
assert_equal(6, a)
|
||||
|
||||
if has('channel')
|
||||
let chan1: channel
|
||||
let job1: job
|
||||
let job2: job = job_start('willfail')
|
||||
var chan1: channel
|
||||
var job1: job
|
||||
var job2: job = job_start('willfail')
|
||||
endif
|
||||
if has('float')
|
||||
let float1: float = 3.4
|
||||
var float1: float = 3.4
|
||||
endif
|
||||
let Funky1: func
|
||||
let Funky2: func = function('len')
|
||||
let Party2: func = funcref('g:Test_syntax')
|
||||
var Funky1: func
|
||||
var Funky2: func = function('len')
|
||||
var Party2: func = funcref('g:Test_syntax')
|
||||
|
||||
g:newvar = 'new' #comment
|
||||
assert_equal('new', g:newvar)
|
||||
|
@ -97,7 +98,7 @@ def Test_assignment()
|
|||
assert_equal('foobar', $ENVVAR)
|
||||
$ENVVAR = ''
|
||||
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
$ENVVAR = 'barfoo'
|
||||
assert_equal('barfoo', $ENVVAR)
|
||||
|
@ -126,15 +127,15 @@ def Test_assignment()
|
|||
assert_equal(2, &ts)
|
||||
|
||||
if has('float')
|
||||
let f100: float = 100.0
|
||||
var f100: float = 100.0
|
||||
f100 /= 5
|
||||
assert_equal(20.0, f100)
|
||||
|
||||
let f200: float = 200.0
|
||||
var f200: float = 200.0
|
||||
f200 /= 5.0
|
||||
assert_equal(40.0, f200)
|
||||
|
||||
CheckDefFailure(['let nr: number = 200', 'nr /= 5.0'], 'E1012:')
|
||||
CheckDefFailure(['var nr: number = 200', 'nr /= 5.0'], 'E1012:')
|
||||
endif
|
||||
|
||||
lines =<< trim END
|
||||
|
@ -163,11 +164,11 @@ def Test_assignment()
|
|||
CheckDefFailure(['&path += 3'], 'E1012:')
|
||||
CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
|
||||
# test freeing ISN_STOREOPT
|
||||
CheckDefFailure(['&ts = 3', 'let asdf'], 'E1022:')
|
||||
CheckDefFailure(['&ts = 3', 'var asdf'], 'E1022:')
|
||||
&ts = 8
|
||||
|
||||
lines =<< trim END
|
||||
let save_TI = &t_TI
|
||||
var save_TI = &t_TI
|
||||
&t_TI = ''
|
||||
assert_equal('', &t_TI)
|
||||
&t_TI = 'xxx'
|
||||
|
@ -179,8 +180,8 @@ def Test_assignment()
|
|||
CheckDefFailure(['&t_TI = 123'], 'E1012:')
|
||||
CheckScriptFailure(['vim9script', '&t_TI = 123'], 'E928:')
|
||||
|
||||
CheckDefFailure(['let s:var = 123'], 'E1101:')
|
||||
CheckDefFailure(['let s:var: number'], 'E1101:')
|
||||
CheckDefFailure(['var s:var = 123'], 'E1101:')
|
||||
CheckDefFailure(['var s:var: number'], 'E1101:')
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
@ -217,20 +218,20 @@ def Test_assignment()
|
|||
|
||||
# this should not leak
|
||||
if 0
|
||||
let text =<< trim END
|
||||
var text =<< trim END
|
||||
some text
|
||||
END
|
||||
endif
|
||||
enddef
|
||||
|
||||
def Test_extend_list()
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
let l: list<number>
|
||||
var l: list<number>
|
||||
l += [123]
|
||||
assert_equal([123], l)
|
||||
|
||||
let d: dict<number>
|
||||
var d: dict<number>
|
||||
d['one'] = 1
|
||||
assert_equal(#{one: 1}, d)
|
||||
END
|
||||
|
@ -239,41 +240,41 @@ enddef
|
|||
|
||||
def Test_single_letter_vars()
|
||||
# single letter variables
|
||||
let a: number = 123
|
||||
var a: number = 123
|
||||
a = 123
|
||||
assert_equal(123, a)
|
||||
let b: number
|
||||
var b: number
|
||||
b = 123
|
||||
assert_equal(123, b)
|
||||
let g: number
|
||||
var g: number
|
||||
g = 123
|
||||
assert_equal(123, g)
|
||||
let s: number
|
||||
var s: number
|
||||
s = 123
|
||||
assert_equal(123, s)
|
||||
let t: number
|
||||
var t: number
|
||||
t = 123
|
||||
assert_equal(123, t)
|
||||
let v: number
|
||||
var v: number
|
||||
v = 123
|
||||
assert_equal(123, v)
|
||||
let w: number
|
||||
var w: number
|
||||
w = 123
|
||||
assert_equal(123, w)
|
||||
enddef
|
||||
|
||||
def Test_vim9_single_char_vars()
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
|
||||
# single character variable declarations work
|
||||
let a: string
|
||||
let b: number
|
||||
let l: list<any>
|
||||
let s: string
|
||||
let t: number
|
||||
let v: number
|
||||
let w: number
|
||||
var a: string
|
||||
var b: number
|
||||
var l: list<any>
|
||||
var s: string
|
||||
var t: number
|
||||
var v: number
|
||||
var w: number
|
||||
|
||||
# script-local variables can be used without s: prefix
|
||||
a = 'script-a'
|
||||
|
@ -298,14 +299,14 @@ def Test_vim9_single_char_vars()
|
|||
enddef
|
||||
|
||||
def Test_assignment_list()
|
||||
let list1: list<bool> = [false, true, false]
|
||||
let list2: list<number> = [1, 2, 3]
|
||||
let list3: list<string> = ['sdf', 'asdf']
|
||||
let list4: list<any> = ['yes', true, 1234]
|
||||
let list5: list<blob> = [0z01, 0z02]
|
||||
var list1: list<bool> = [false, true, false]
|
||||
var list2: list<number> = [1, 2, 3]
|
||||
var list3: list<string> = ['sdf', 'asdf']
|
||||
var list4: list<any> = ['yes', true, 1234]
|
||||
var list5: list<blob> = [0z01, 0z02]
|
||||
|
||||
let listS: list<string> = []
|
||||
let listN: list<number> = []
|
||||
var listS: list<string> = []
|
||||
var listN: list<number> = []
|
||||
|
||||
assert_equal([1, 2, 3], list2)
|
||||
list2[-1] = 99
|
||||
|
@ -320,19 +321,19 @@ def Test_assignment_list()
|
|||
list3 += ['end']
|
||||
assert_equal(['sdf', 'asdf', 'end'], list3)
|
||||
|
||||
CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
|
||||
CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:')
|
||||
CheckDefExecFailure(['var ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
|
||||
CheckDefExecFailure(['var [v1, v2] = [1, 2]'], 'E1092:')
|
||||
|
||||
# type becomes list<any>
|
||||
let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
|
||||
var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
|
||||
enddef
|
||||
|
||||
def Test_assignment_list_vim9script()
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
let v1: number
|
||||
let v2: number
|
||||
let v3: number
|
||||
var v1: number
|
||||
var v2: number
|
||||
var v3: number
|
||||
[v1, v2, v3] = [1, 2, 3]
|
||||
assert_equal([1, 2, 3], [v1, v2, v3])
|
||||
END
|
||||
|
@ -340,27 +341,27 @@ def Test_assignment_list_vim9script()
|
|||
enddef
|
||||
|
||||
def Test_assignment_dict()
|
||||
let dict1: dict<bool> = #{one: false, two: true}
|
||||
let dict2: dict<number> = #{one: 1, two: 2}
|
||||
let dict3: dict<string> = #{key: 'value'}
|
||||
let dict4: dict<any> = #{one: 1, two: '2'}
|
||||
let dict5: dict<blob> = #{one: 0z01, two: 0z02}
|
||||
var dict1: dict<bool> = #{one: false, two: true}
|
||||
var dict2: dict<number> = #{one: 1, two: 2}
|
||||
var dict3: dict<string> = #{key: 'value'}
|
||||
var dict4: dict<any> = #{one: 1, two: '2'}
|
||||
var dict5: dict<blob> = #{one: 0z01, two: 0z02}
|
||||
|
||||
# overwrite
|
||||
dict3['key'] = 'another'
|
||||
|
||||
# empty key can be used
|
||||
let dd = {}
|
||||
var dd = {}
|
||||
dd[""] = 6
|
||||
assert_equal({'': 6}, dd)
|
||||
|
||||
# type becomes dict<any>
|
||||
let somedict = rand() > 0 ? #{a: 1, b: 2} : #{a: 'a', b: 'b'}
|
||||
var somedict = rand() > 0 ? #{a: 1, b: 2} : #{a: 'a', b: 'b'}
|
||||
|
||||
# assignment to script-local dict
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
let test: dict<any> = {}
|
||||
var test: dict<any> = {}
|
||||
def FillDict(): dict<any>
|
||||
test['a'] = 43
|
||||
return test
|
||||
|
@ -371,7 +372,7 @@ def Test_assignment_dict()
|
|||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
let test: dict<any>
|
||||
var test: dict<any>
|
||||
def FillDict(): dict<any>
|
||||
test['a'] = 43
|
||||
return test
|
||||
|
@ -408,7 +409,7 @@ enddef
|
|||
def Test_assignment_local()
|
||||
# Test in a separated file in order not to the current buffer/window/tab is
|
||||
# changed.
|
||||
let script_lines: list<string> =<< trim END
|
||||
var script_lines: list<string> =<< trim END
|
||||
let b:existing = 'yes'
|
||||
let w:existing = 'yes'
|
||||
let t:existing = 'yes'
|
||||
|
@ -446,37 +447,37 @@ enddef
|
|||
def Test_assignment_default()
|
||||
|
||||
# Test default values.
|
||||
let thebool: bool
|
||||
var thebool: bool
|
||||
assert_equal(v:false, thebool)
|
||||
|
||||
let thenumber: number
|
||||
var thenumber: number
|
||||
assert_equal(0, thenumber)
|
||||
|
||||
if has('float')
|
||||
let thefloat: float
|
||||
var thefloat: float
|
||||
assert_equal(0.0, thefloat)
|
||||
endif
|
||||
|
||||
let thestring: string
|
||||
var thestring: string
|
||||
assert_equal('', thestring)
|
||||
|
||||
let theblob: blob
|
||||
var theblob: blob
|
||||
assert_equal(0z, theblob)
|
||||
|
||||
let Thefunc: func
|
||||
var Thefunc: func
|
||||
assert_equal(test_null_function(), Thefunc)
|
||||
|
||||
let thelist: list<any>
|
||||
var thelist: list<any>
|
||||
assert_equal([], thelist)
|
||||
|
||||
let thedict: dict<any>
|
||||
var thedict: dict<any>
|
||||
assert_equal({}, thedict)
|
||||
|
||||
if has('channel')
|
||||
let thejob: job
|
||||
var thejob: job
|
||||
assert_equal(test_null_job(), thejob)
|
||||
|
||||
let thechannel: channel
|
||||
var thechannel: channel
|
||||
assert_equal(test_null_channel(), thechannel)
|
||||
|
||||
if has('unix') && executable('cat')
|
||||
|
@ -487,14 +488,14 @@ def Test_assignment_default()
|
|||
endif
|
||||
endif
|
||||
|
||||
let nr = 1234 | nr = 5678
|
||||
var nr = 1234 | nr = 5678
|
||||
assert_equal(5678, nr)
|
||||
enddef
|
||||
|
||||
def Test_assignment_var_list()
|
||||
let v1: string
|
||||
let v2: string
|
||||
let vrem: list<string>
|
||||
var v1: string
|
||||
var v2: string
|
||||
var vrem: list<string>
|
||||
[v1] = ['aaa']
|
||||
assert_equal('aaa', v1)
|
||||
|
||||
|
@ -519,18 +520,18 @@ def Test_assignment_var_list()
|
|||
enddef
|
||||
|
||||
def Test_assignment_vim9script()
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Func(): list<number>
|
||||
return [1, 2]
|
||||
enddef
|
||||
let var1: number
|
||||
let var2: number
|
||||
var var1: number
|
||||
var var2: number
|
||||
[var1, var2] =
|
||||
Func()
|
||||
assert_equal(1, var1)
|
||||
assert_equal(2, var2)
|
||||
let ll =
|
||||
var ll =
|
||||
Func()
|
||||
assert_equal([1, 2], ll)
|
||||
|
||||
|
@ -551,15 +552,15 @@ def Test_assignment_vim9script()
|
|||
assert_equal('plus', @+)
|
||||
endif
|
||||
|
||||
let a: number = 123
|
||||
var a: number = 123
|
||||
assert_equal(123, a)
|
||||
let s: string = 'yes'
|
||||
var s: string = 'yes'
|
||||
assert_equal('yes', s)
|
||||
let b: number = 42
|
||||
var b: number = 42
|
||||
assert_equal(42, b)
|
||||
let w: number = 43
|
||||
var w: number = 43
|
||||
assert_equal(43, w)
|
||||
let t: number = 44
|
||||
var t: number = 44
|
||||
assert_equal(44, t)
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
|
@ -571,80 +572,80 @@ def Mess(): string
|
|||
enddef
|
||||
|
||||
def Test_assignment_failure()
|
||||
CheckDefFailure(['let var=234'], 'E1004:')
|
||||
CheckDefFailure(['let var =234'], 'E1004:')
|
||||
CheckDefFailure(['let var= 234'], 'E1004:')
|
||||
CheckDefFailure(['var var=234'], 'E1004:')
|
||||
CheckDefFailure(['var var =234'], 'E1004:')
|
||||
CheckDefFailure(['var var= 234'], 'E1004:')
|
||||
|
||||
CheckScriptFailure(['vim9script', 'let var=234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'let var=234'], "before and after '='")
|
||||
CheckScriptFailure(['vim9script', 'let var =234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'let var= 234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'let var = 234', 'var+=234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'let var = 234', 'var+=234'], "before and after '+='")
|
||||
CheckScriptFailure(['vim9script', 'let var = "x"', 'var..="y"'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'let var = "x"', 'var..="y"'], "before and after '..='")
|
||||
CheckScriptFailure(['vim9script', 'var var=234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'var var=234'], "before and after '='")
|
||||
CheckScriptFailure(['vim9script', 'var var =234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'var var= 234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'var var = 234', 'var+=234'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'var var = 234', 'var+=234'], "before and after '+='")
|
||||
CheckScriptFailure(['vim9script', 'var var = "x"', 'var..="y"'], 'E1004:')
|
||||
CheckScriptFailure(['vim9script', 'var var = "x"', 'var..="y"'], "before and after '..='")
|
||||
|
||||
CheckDefFailure(['let true = 1'], 'E1034:')
|
||||
CheckDefFailure(['let false = 1'], 'E1034:')
|
||||
CheckDefFailure(['var true = 1'], 'E1034:')
|
||||
CheckDefFailure(['var false = 1'], 'E1034:')
|
||||
|
||||
CheckDefFailure(['[a; b; c] = g:list'], 'E452:')
|
||||
CheckDefExecFailure(['let a: number',
|
||||
CheckDefExecFailure(['var a: number',
|
||||
'[a] = test_null_list()'], 'E1093:')
|
||||
CheckDefExecFailure(['let a: number',
|
||||
CheckDefExecFailure(['var a: number',
|
||||
'[a] = []'], 'E1093:')
|
||||
CheckDefExecFailure(['let x: number',
|
||||
'let y: number',
|
||||
CheckDefExecFailure(['var x: number',
|
||||
'var y: number',
|
||||
'[x, y] = [1]'], 'E1093:')
|
||||
CheckDefExecFailure(['let x: number',
|
||||
'let y: number',
|
||||
'let z: list<number>',
|
||||
CheckDefExecFailure(['var x: number',
|
||||
'var y: number',
|
||||
'var z: list<number>',
|
||||
'[x, y; z] = [1]'], 'E1093:')
|
||||
|
||||
CheckDefFailure(['let somevar'], "E1022:")
|
||||
CheckDefFailure(['let &tabstop = 4'], 'E1052:')
|
||||
CheckDefFailure(['var somevar'], "E1022:")
|
||||
CheckDefFailure(['var &tabstop = 4'], 'E1052:')
|
||||
CheckDefFailure(['&g:option = 5'], 'E113:')
|
||||
CheckScriptFailure(['vim9script', 'let &tabstop = 4'], 'E1052:')
|
||||
CheckScriptFailure(['vim9script', 'var &tabstop = 4'], 'E1052:')
|
||||
|
||||
CheckDefFailure(['let $VAR = 5'], 'E1016: Cannot declare an environment variable:')
|
||||
CheckScriptFailure(['vim9script', 'let $ENV = "xxx"'], 'E1016:')
|
||||
CheckDefFailure(['var $VAR = 5'], 'E1016: Cannot declare an environment variable:')
|
||||
CheckScriptFailure(['vim9script', 'var $ENV = "xxx"'], 'E1016:')
|
||||
|
||||
if has('dnd')
|
||||
CheckDefFailure(['let @~ = 5'], 'E1066:')
|
||||
CheckDefFailure(['var @~ = 5'], 'E1066:')
|
||||
else
|
||||
CheckDefFailure(['let @~ = 5'], 'E354:')
|
||||
CheckDefFailure(['var @~ = 5'], 'E354:')
|
||||
CheckDefFailure(['@~ = 5'], 'E354:')
|
||||
endif
|
||||
CheckDefFailure(['let @a = 5'], 'E1066:')
|
||||
CheckDefFailure(['let @/ = "x"'], 'E1066:')
|
||||
CheckScriptFailure(['vim9script', 'let @a = "abc"'], 'E1066:')
|
||||
CheckDefFailure(['var @a = 5'], 'E1066:')
|
||||
CheckDefFailure(['var @/ = "x"'], 'E1066:')
|
||||
CheckScriptFailure(['vim9script', 'var @a = "abc"'], 'E1066:')
|
||||
|
||||
CheckDefFailure(['let g:var = 5'], 'E1016: Cannot declare a global variable:')
|
||||
CheckDefFailure(['let w:var = 5'], 'E1016: Cannot declare a window variable:')
|
||||
CheckDefFailure(['let b:var = 5'], 'E1016: Cannot declare a buffer variable:')
|
||||
CheckDefFailure(['let t:var = 5'], 'E1016: Cannot declare a tab variable:')
|
||||
CheckDefFailure(['var g:var = 5'], 'E1016: Cannot declare a global variable:')
|
||||
CheckDefFailure(['var w:var = 5'], 'E1016: Cannot declare a window variable:')
|
||||
CheckDefFailure(['var b:var = 5'], 'E1016: Cannot declare a buffer variable:')
|
||||
CheckDefFailure(['var t:var = 5'], 'E1016: Cannot declare a tab variable:')
|
||||
|
||||
CheckDefFailure(['let anr = 4', 'anr ..= "text"'], 'E1019:')
|
||||
CheckDefFailure(['let xnr += 4'], 'E1020:', 1)
|
||||
CheckScriptFailure(['vim9script', 'let xnr += 4'], 'E1020:')
|
||||
CheckDefFailure(["let xnr = xnr + 1"], 'E1001:', 1)
|
||||
CheckScriptFailure(['vim9script', 'let xnr = xnr + 4'], 'E121:')
|
||||
CheckDefFailure(['var anr = 4', 'anr ..= "text"'], 'E1019:')
|
||||
CheckDefFailure(['var xnr += 4'], 'E1020:', 1)
|
||||
CheckScriptFailure(['vim9script', 'var xnr += 4'], 'E1020:')
|
||||
CheckDefFailure(["var xnr = xnr + 1"], 'E1001:', 1)
|
||||
CheckScriptFailure(['vim9script', 'var xnr = xnr + 4'], 'E121:')
|
||||
|
||||
CheckScriptFailure(['vim9script', 'def Func()', 'let dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:')
|
||||
CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:')
|
||||
|
||||
CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
|
||||
CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
|
||||
CheckDefFailure(['var var: list<string> = [123]'], 'expected list<string> but got list<number>')
|
||||
CheckDefFailure(['var var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
|
||||
|
||||
CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
|
||||
CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
|
||||
CheckDefFailure(['var var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
|
||||
CheckDefFailure(['var var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
|
||||
|
||||
CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
|
||||
CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
|
||||
CheckDefFailure(['var var = feedkeys("0")'], 'E1031:')
|
||||
CheckDefFailure(['var var: number = feedkeys("0")'], 'expected number but got void')
|
||||
|
||||
CheckDefFailure(['let var: dict <number>'], 'E1068:')
|
||||
CheckDefFailure(['let var: dict<number'], 'E1009:')
|
||||
CheckDefFailure(['var var: dict <number>'], 'E1068:')
|
||||
CheckDefFailure(['var var: dict<number'], 'E1009:')
|
||||
|
||||
assert_fails('s/^/\=Mess()/n', 'E794:')
|
||||
CheckDefFailure(['let var: dict<number'], 'E1009:')
|
||||
CheckDefFailure(['var var: dict<number'], 'E1009:')
|
||||
|
||||
CheckDefFailure(['w:foo: number = 10'],
|
||||
'E488: Trailing characters: : number = 1')
|
||||
|
@ -657,7 +658,7 @@ def Test_assignment_failure()
|
|||
enddef
|
||||
|
||||
def Test_assign_list()
|
||||
let l: list<string> = []
|
||||
var l: list<string> = []
|
||||
l[0] = 'value'
|
||||
assert_equal('value', l[0])
|
||||
|
||||
|
@ -667,7 +668,7 @@ def Test_assign_list()
|
|||
assert_equal('asdf', l[-1])
|
||||
assert_equal('value', l[-2])
|
||||
|
||||
let nrl: list<number> = []
|
||||
var nrl: list<number> = []
|
||||
for i in range(5)
|
||||
nrl[i] = i
|
||||
endfor
|
||||
|
@ -675,7 +676,7 @@ def Test_assign_list()
|
|||
enddef
|
||||
|
||||
def Test_assign_dict()
|
||||
let d: dict<string> = {}
|
||||
var d: dict<string> = {}
|
||||
d['key'] = 'value'
|
||||
assert_equal('value', d['key'])
|
||||
|
||||
|
@ -683,7 +684,7 @@ def Test_assign_dict()
|
|||
assert_equal('qwerty', d[123])
|
||||
assert_equal('qwerty', d['123'])
|
||||
|
||||
let nrd: dict<number> = {}
|
||||
var nrd: dict<number> = {}
|
||||
for i in range(3)
|
||||
nrd[i] = i
|
||||
endfor
|
||||
|
@ -691,12 +692,12 @@ def Test_assign_dict()
|
|||
enddef
|
||||
|
||||
def Test_assign_dict_unknown_type()
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
let mylist = []
|
||||
var mylist = []
|
||||
mylist += [#{one: 'one'}]
|
||||
def Func()
|
||||
let dd = mylist[0]
|
||||
var dd = mylist[0]
|
||||
assert_equal('one', dd.one)
|
||||
enddef
|
||||
Func()
|
||||
|
@ -706,10 +707,10 @@ def Test_assign_dict_unknown_type()
|
|||
# doesn't work yet
|
||||
#lines =<< trim END
|
||||
# vim9script
|
||||
# let mylist = [[]]
|
||||
# var mylist = [[]]
|
||||
# mylist[0] += [#{one: 'one'}]
|
||||
# def Func()
|
||||
# let dd = mylist[0][0]
|
||||
# var dd = mylist[0][0]
|
||||
# assert_equal('one', dd.one)
|
||||
# enddef
|
||||
# Func()
|
||||
|
@ -719,13 +720,13 @@ enddef
|
|||
|
||||
def Test_assign_lambda()
|
||||
# check if assign a lambda to a variable which type is func or any.
|
||||
let lines =<< trim END
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
let FuncRef = {->123}
|
||||
var FuncRef = {->123}
|
||||
assert_equal(123, FuncRef())
|
||||
let FuncRef_Func: func = {->123}
|
||||
var FuncRef_Func: func = {->123}
|
||||
assert_equal(123, FuncRef_Func())
|
||||
let FuncRef_Any: any = {->123}
|
||||
var FuncRef_Any: any = {->123}
|
||||
assert_equal(123, FuncRef_Any())
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
|
|
|
@ -193,21 +193,21 @@ def Test_wrong_type()
|
|||
enddef
|
||||
|
||||
def Test_const()
|
||||
CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
|
||||
CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
|
||||
CheckDefFailure(['const list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
|
||||
CheckDefFailure(['const two'], 'E1021:')
|
||||
CheckDefFailure(['const &option'], 'E996:')
|
||||
CheckDefFailure(['final var = 234', 'var = 99'], 'E1018:')
|
||||
CheckDefFailure(['final one = 234', 'let one = 99'], 'E1017:')
|
||||
CheckDefFailure(['final list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
|
||||
CheckDefFailure(['final two'], 'E1125:')
|
||||
CheckDefFailure(['final &option'], 'E996:')
|
||||
|
||||
let lines =<< trim END
|
||||
const list = [1, 2, 3]
|
||||
final list = [1, 2, 3]
|
||||
list[0] = 4
|
||||
list->assert_equal([4, 2, 3])
|
||||
const! other = [5, 6, 7]
|
||||
const other = [5, 6, 7]
|
||||
other->assert_equal([5, 6, 7])
|
||||
|
||||
let varlist = [7, 8]
|
||||
const! constlist = [1, varlist, 3]
|
||||
const constlist = [1, varlist, 3]
|
||||
varlist[0] = 77
|
||||
# TODO: does not work yet
|
||||
# constlist[1][1] = 88
|
||||
|
@ -216,7 +216,7 @@ def Test_const()
|
|||
constlist->assert_equal([1, [77, 88], 3])
|
||||
|
||||
let vardict = #{five: 5, six: 6}
|
||||
const! constdict = #{one: 1, two: vardict, three: 3}
|
||||
const constdict = #{one: 1, two: vardict, three: 3}
|
||||
vardict['five'] = 55
|
||||
# TODO: does not work yet
|
||||
# constdict['two']['six'] = 66
|
||||
|
@ -229,35 +229,35 @@ enddef
|
|||
|
||||
def Test_const_bang()
|
||||
let lines =<< trim END
|
||||
const! var = 234
|
||||
const var = 234
|
||||
var = 99
|
||||
END
|
||||
CheckDefExecFailure(lines, 'E1018:', 2)
|
||||
CheckScriptFailure(['vim9script'] + lines, 'E46:', 3)
|
||||
|
||||
lines =<< trim END
|
||||
const! ll = [2, 3, 4]
|
||||
const ll = [2, 3, 4]
|
||||
ll[0] = 99
|
||||
END
|
||||
CheckDefExecFailure(lines, 'E1119:', 2)
|
||||
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
|
||||
|
||||
lines =<< trim END
|
||||
const! ll = [2, 3, 4]
|
||||
const ll = [2, 3, 4]
|
||||
ll[3] = 99
|
||||
END
|
||||
CheckDefExecFailure(lines, 'E1118:', 2)
|
||||
CheckScriptFailure(['vim9script'] + lines, 'E684:', 3)
|
||||
|
||||
lines =<< trim END
|
||||
const! dd = #{one: 1, two: 2}
|
||||
const dd = #{one: 1, two: 2}
|
||||
dd["one"] = 99
|
||||
END
|
||||
CheckDefExecFailure(lines, 'E1121:', 2)
|
||||
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
|
||||
|
||||
lines =<< trim END
|
||||
const! dd = #{one: 1, two: 2}
|
||||
const dd = #{one: 1, two: 2}
|
||||
dd["three"] = 99
|
||||
END
|
||||
CheckDefExecFailure(lines, 'E1120:')
|
||||
|
@ -2531,6 +2531,12 @@ enddef
|
|||
|
||||
def Test_let_declaration_fails()
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
final var: string
|
||||
END
|
||||
CheckScriptFailure(lines, 'E1125:')
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
const var: string
|
||||
END
|
||||
|
|
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1744,
|
||||
/**/
|
||||
1743,
|
||||
/**/
|
||||
|
|
|
@ -2135,9 +2135,9 @@ typedef enum {
|
|||
} estack_arg_T;
|
||||
|
||||
// Flags for assignment functions.
|
||||
#define LET_IS_CONST 1 // ":const"
|
||||
#define LET_FORCEIT 2 // ":const!" (LET_IS_CONST is also set)
|
||||
#define LET_NO_COMMAND 4 // "var = expr" without ":let" or ":const"
|
||||
#define ASSIGN_FINAL 1 // ":final"
|
||||
#define ASSIGN_CONST 2 // ":const"
|
||||
#define ASSIGN_NO_DECL 4 // "name = expr" without ":let" or ":const"
|
||||
|
||||
#include "ex_cmds.h" // Ex command defines
|
||||
#include "spell.h" // spell checking stuff
|
||||
|
|
|
@ -4562,8 +4562,12 @@ vim9_declare_error(char_u *name)
|
|||
|
||||
/*
|
||||
* Compile declaration and assignment:
|
||||
* "let var", "let var = expr", "const var = expr" and "var = expr"
|
||||
* "arg" points to "var".
|
||||
* "let name"
|
||||
* "var name = expr"
|
||||
* "final name = expr"
|
||||
* "const name = expr"
|
||||
* "name = expr"
|
||||
* "arg" points to "name".
|
||||
* Return NULL for an error.
|
||||
* Return "arg" if it does not look like a variable list.
|
||||
*/
|
||||
|
@ -4588,7 +4592,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||
type_T *member_type = &t_any;
|
||||
char_u *name = NULL;
|
||||
char_u *sp;
|
||||
int is_decl = cmdidx == CMD_let || cmdidx == CMD_const;
|
||||
int is_decl = cmdidx == CMD_let || cmdidx == CMD_var
|
||||
|| cmdidx == CMD_final || cmdidx == CMD_const;
|
||||
|
||||
// Skip over the "var" or "[var, var]" to get to any "=".
|
||||
p = skip_var_list(arg, TRUE, &var_count, &semicolon, TRUE);
|
||||
|
@ -4729,7 +4734,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||
long numval;
|
||||
|
||||
dest = dest_option;
|
||||
if (cmdidx == CMD_const)
|
||||
if (cmdidx == CMD_final || cmdidx == CMD_const)
|
||||
{
|
||||
emsg(_(e_const_option));
|
||||
goto theend;
|
||||
|
@ -4968,7 +4973,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||
&& var_wrong_func_name(name, TRUE))
|
||||
goto theend;
|
||||
lvar = reserve_local(cctx, var_start, varlen,
|
||||
cmdidx == CMD_const, type);
|
||||
cmdidx == CMD_final || cmdidx == CMD_const, type);
|
||||
if (lvar == NULL)
|
||||
goto theend;
|
||||
new_local = TRUE;
|
||||
|
@ -5119,6 +5124,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||
cctx, FALSE) == FAIL)
|
||||
goto theend;
|
||||
}
|
||||
else if (cmdidx == CMD_final)
|
||||
{
|
||||
emsg(_(e_final_requires_a_value));
|
||||
goto theend;
|
||||
}
|
||||
else if (cmdidx == CMD_const)
|
||||
{
|
||||
emsg(_(e_const_requires_a_value));
|
||||
|
@ -5283,9 +5293,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (is_decl && eap->forceit && cmdidx == CMD_const
|
||||
&& (dest == dest_script || dest == dest_local))
|
||||
// ":const! var": lock the value, but not referenced variables
|
||||
if (is_decl && cmdidx == CMD_const
|
||||
&& (dest == dest_script || dest == dest_local))
|
||||
// ":const var": lock the value, but not referenced variables
|
||||
generate_LOCKCONST(cctx);
|
||||
|
||||
switch (dest)
|
||||
|
@ -6915,7 +6925,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
|||
// Expression or function call.
|
||||
if (ea.cmdidx != CMD_eval)
|
||||
{
|
||||
// CMD_let cannot happen, compile_assignment() above is used
|
||||
// CMD_var cannot happen, compile_assignment() above is used
|
||||
iemsg("Command from find_ex_command() not handled");
|
||||
goto erret;
|
||||
}
|
||||
|
@ -6967,6 +6977,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
|||
break;
|
||||
|
||||
case CMD_let:
|
||||
case CMD_var:
|
||||
case CMD_final:
|
||||
case CMD_const:
|
||||
line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
|
||||
if (line == p)
|
||||
|
|
|
@ -729,7 +729,7 @@ store_var(char_u *name, typval_T *tv)
|
|||
funccal_entry_T entry;
|
||||
|
||||
save_funccal(&entry);
|
||||
set_var_const(name, NULL, tv, FALSE, LET_NO_COMMAND);
|
||||
set_var_const(name, NULL, tv, FALSE, ASSIGN_NO_DECL);
|
||||
restore_funccal();
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ ex_export(exarg_T *eap)
|
|||
switch (eap->cmdidx)
|
||||
{
|
||||
case CMD_let:
|
||||
case CMD_var:
|
||||
case CMD_final:
|
||||
case CMD_const:
|
||||
case CMD_def:
|
||||
// case CMD_class:
|
||||
|
@ -508,9 +510,12 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
|
|||
int called_emsg_before = called_emsg;
|
||||
typval_T init_tv;
|
||||
|
||||
if (eap->cmdidx == CMD_const)
|
||||
if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const)
|
||||
{
|
||||
emsg(_(e_const_requires_a_value));
|
||||
if (eap->cmdidx == CMD_final)
|
||||
emsg(_(e_final_requires_a_value));
|
||||
else
|
||||
emsg(_(e_const_requires_a_value));
|
||||
return arg + STRLEN(arg);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue