Compare commits

...

20 Commits

Author SHA1 Message Date
a5fe91e6dc patch 8.2.1757: Mac: default locale is lacking the encoding
Problem:    Mac: default locale is lacking the encoding.
Solution:   Add ".UTF-8 to the locale. (Yee Cheng Chin, closes #7022)
2020-09-27 16:03:15 +02:00
cfcd011fcd patch 8.2.1756: Vim9: :let will soon be disallowed
Problem:    Vim9: :let will soon be disallowed.
Solution:   Add v:disallow_let temporarily.  Fix tests.
2020-09-27 15:19:27 +02:00
c0e29010f6 patch 8.2.1755: Vim9: crash when using invalid heredoc marker
Problem:    Vim9: crash when using invalid heredoc marker. (Dhiraj Mishra)
Solution:   Check for NULL list. (closes #7027)  Fix comment character.
2020-09-27 14:22:48 +02:00
8c7ad3631a patch 8.2.1754: completion with spell checking not tested
Problem:    Completion with spell checking not tested.
Solution:   Add a test case. (Dominique Pellé, closes #7024)
2020-09-27 13:58:38 +02:00
f6a44f714a patch 8.2.1753: Vim9: crash when using import at script level
Problem:    Vim9: crash when using import at script level.
Solution:   Give a "not implemented yet" error. (closes #7026)
2020-09-27 13:51:14 +02:00
daff0fb738 patch 8.2.1752: GTK GUI: cannot map alt-? with <A-?>
Problem:    GTK GUI: cannot map alt-? with <A-?>. (Ingo Karkat)
Solution:   Adjust the characters for which the shift modifier is removed.
            (closes #7016)  Make Motif and Win32 use the same function as GTK.
2020-09-27 13:16:46 +02:00
bade44e5ca patch 8.2.1751: using 2 where bool is expected may throw an error
Problem:    Using 2 where bool is expected may throw an error.
Solution:   Make this backwards compatible.
2020-09-26 22:39:24 +02:00
3697c9bbae patch 8.2.1750: popup_setoptions() setting firstline fails if cursorline set
Problem:    Setting firstline with popup_setoptions() fails if cursorline is
            set.
Solution:   Use apply_options(). Update the popup before applying "zz".
            (closes #7010)
2020-09-26 22:03:00 +02:00
c70bdab0b8 patch 8.2.1749: Vim9: crash when closure fails in nested function
Problem:    Vim9: crash when closure fails in nested function.
Solution:   Handle function returns before dereferencing remaining closures.
            (closes #7008)
2020-09-26 19:59:38 +02:00
f3c51bbff1 patch 8.2.1748: closing split window in other tab may cause a crash
Problem:    Closing split window in other tab may cause a crash.
Solution:   Set tp_curwin properly. (Rob Pilling, closes #7018)
2020-09-26 19:11:39 +02:00
8f187fc630 patch 8.2.1747: result of expand() unexpectedly depends on 'completeslash'
Problem:    Result of expand() unexpectedly depends on 'completeslash'.
Solution:   Temporarily reset 'completeslash'. (Yasuhiro Matsumoto,
            closes #7021)
2020-09-26 18:47:11 +02:00
373863ed48 patch 8.2.1746: Vim9: cannot use "fina" for "finally"
Problem:    Vim9: Cannot use "fina" for "finally". (Naruhiko Nishino)
Solution:   Specifically check for "fina". (closes #7020)
2020-09-26 17:20:53 +02:00
d47f50b331 patch 8.2.1745: tiny version doesn't build
Problem:    Tiny version doesn't build.
Solution:   Add dummy ex_var() function.
2020-09-26 15:20:42 +02:00
30fd8204ce 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.
2020-09-26 15:09:30 +02:00
273af497ca patch 8.2.1743: cannot build without the eval feature
Problem:    Cannot build without the eval feature.
Solution:   Move shorten_dir outside of #ifdef.
2020-09-25 23:49:01 +02:00
7e9210ea53 patch 8.2.1742: test still fails without the terminal feature
Problem:    Test still fails without the terminal feature.
Solution:   Put check for terminal feature in separate function.
2020-09-25 23:12:51 +02:00
6a33ef0deb patch 8.2.1741: pathshorten() only supports using one character
Problem:    pathshorten() only supports using one character.
Solution:   Add an argument to control the length. (closes #7006)
2020-09-25 22:42:48 +02:00
58dbef330c patch 8.2.1740: test fails without the terminal feature
Problem:    Test fails without the terminal feature.
Solution:   Skip test if the terminal feature is not available.
2020-09-25 22:13:05 +02:00
9c4f55204f patch 8.2.1739: Vim9: crash when compiling a manually defined function
Problem:    Vim9: crash when compiling a manually defined function. (Antony
            Scriven)
Solution:   Check that the script ID is positive. (closes #7012)
2020-09-25 21:47:28 +02:00
509f8031b2 patch 8.2.1738: Mac: str2float() recognizes comma instead of decimal point
Problem:    Mac: str2float() recognizes comma instead of decimal point.
Solution:   Set LC_NUMERIC to "C". (closes #7003)
2020-09-24 23:08:19 +02:00
44 changed files with 1212 additions and 695 deletions

View File

@ -2661,7 +2661,7 @@ mzeval({expr}) any evaluate |MzScheme| expression
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
nr2char({expr} [, {utf8}]) String single char with ASCII/UTF8 value {expr}
or({expr}, {expr}) Number bitwise OR
pathshorten({expr}) String shorten directory names in a path
pathshorten({expr} [, {len}]) String shorten directory names in a path
perleval({expr}) any evaluate |Perl| expression
popup_atcursor({what}, {options}) Number create popup window near the cursor
popup_beval({what}, {options}) Number create popup window for 'ballooneval'
@ -7656,13 +7656,17 @@ or({expr}, {expr}) *or()*
:let bits = bits->or(0x80)
pathshorten({expr}) *pathshorten()*
pathshorten({expr} [, {len}]) *pathshorten()*
Shorten directory names in the path {expr} and return the
result. The tail, the file name, is kept as-is. The other
components in the path are reduced to single letters. Leading
'~' and '.' characters are kept. Example: >
components in the path are reduced to {len} letters in length.
If {len} is omitted or smaller than 1 then 1 is used (single
letters). Leading '~' and '.' characters are kept. Examples: >
:echo pathshorten('~/.vim/autoload/myfile.vim')
< ~/.v/a/myfile.vim ~
>
:echo pathshorten('~/.vim/autoload/myfile.vim', 2)
< ~/.vi/au/myfile.vim ~
It doesn't matter if the path exists or not.
Can also be used as a |method|: >

View File

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

View File

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

View File

@ -270,4 +270,10 @@ 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"));
EXTERN char e_cannot_use_let_in_vim9_script[]
INIT(= N_("E1126: Cannot use :let in Vim9 script"));
#endif

View File

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

View File

@ -779,7 +779,7 @@ static funcentry_T global_functions[] =
{"nextnonblank", 1, 1, FEARG_1, ret_number, f_nextnonblank},
{"nr2char", 1, 2, FEARG_1, ret_string, f_nr2char},
{"or", 2, 2, FEARG_1, ret_number, f_or},
{"pathshorten", 1, 1, FEARG_1, ret_string, f_pathshorten},
{"pathshorten", 1, 2, FEARG_1, ret_string, f_pathshorten},
{"perleval", 1, 1, FEARG_1, ret_any,
#ifdef FEAT_PERL
f_perleval
@ -1982,7 +1982,7 @@ f_deepcopy(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
noref = (int)tv_get_bool_chk(&argvars[1], NULL);
if (noref < 0 || noref > 1)
emsg(_(e_invarg));
semsg(_(e_using_number_as_bool_nr), noref);
else
{
copyID = get_copyID();
@ -2436,6 +2436,12 @@ f_expand(typval_T *argvars, typval_T *rettv)
expand_T xpc;
int error = FALSE;
char_u *result;
#ifdef BACKSLASH_IN_FILENAME
char_u *p_csl_save = p_csl;
// avoid using 'completeslash' here
p_csl = empty_option;
#endif
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN
@ -2488,6 +2494,9 @@ f_expand(typval_T *argvars, typval_T *rettv)
else
rettv->vval.v_string = NULL;
}
#ifdef BACKSLASH_IN_FILENAME
p_csl = p_csl_save;
#endif
}
/*
@ -8184,7 +8193,7 @@ f_strchars(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
skipcc = (int)tv_get_bool(&argvars[1]);
if (skipcc < 0 || skipcc > 1)
emsg(_(e_invarg));
semsg(_(e_using_number_as_bool_nr), skipcc);
else
{
func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;

View File

@ -146,6 +146,7 @@ static struct vimvar
{VV_NAME("echospace", VAR_NUMBER), VV_RO},
{VV_NAME("argv", VAR_LIST), VV_RO},
{VV_NAME("collate", VAR_STRING), VV_RO},
{VV_NAME("disallow_let", VAR_NUMBER), 0}, // TODO: remove
};
// shorthand
@ -558,6 +559,7 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
int text_indent_len = 0;
char_u *text_indent = NULL;
char_u dot[] = ".";
int comment_char = in_vim9script() ? '#' : '"';
if (eap->getline == NULL)
{
@ -585,11 +587,11 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
}
// The marker is the next word.
if (*cmd != NUL && *cmd != '"')
if (*cmd != NUL && *cmd != comment_char)
{
marker = skipwhite(cmd);
p = skiptowhite(marker);
if (*skipwhite(p) != NUL && *skipwhite(p) != '"')
if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char)
{
semsg(_(e_trailing_arg), p);
return NULL;
@ -668,6 +670,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 +704,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 +726,28 @@ 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 (get_vim_var_nr(VV_DISALLOW_LET)
&& eap->cmdidx == CMD_let && vim9script)
{
emsg(_(e_cannot_use_let_in_vim9_script));
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 +825,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 +898,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 +1253,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 +1265,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 +1275,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 +1327,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 +1411,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;
@ -2456,6 +2494,11 @@ eval_variable(
rettv->vval.v_string = vim_strsave(import->imp_funcname);
}
}
else if (import->imp_all)
{
emsg("Sorry, 'import * as X' not implemented yet");
ret = FAIL;
}
else
{
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
@ -2926,7 +2969,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 +2983,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 +3003,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 +3033,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 +3041,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 +3137,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 +3156,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 +3175,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.

View File

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

View File

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

View File

@ -291,6 +291,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
# define ex_function ex_ni
# define ex_if ex_ni
# define ex_let ex_ni
# define ex_var ex_ni
# define ex_lockvar ex_ni
# define ex_oldfiles ex_ni
# define ex_options ex_ni
@ -2421,6 +2422,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 +2444,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 +2462,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 +3247,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 +3271,7 @@ find_ex_command(
}
if (p > eap->cmd && *skipwhite(p) == '=')
{
eap->cmdidx = CMD_let;
eap->cmdidx = CMD_var;
return eap->cmd;
}
}
@ -3287,7 +3290,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;
}
}
@ -3417,6 +3420,10 @@ find_ex_command(
eap->cmdidx = CMD_SIZE;
}
// ":fina" means ":finally" for backwards compatibility.
if (eap->cmdidx == CMD_final && p - eap->cmd == 4)
eap->cmdidx = CMD_finally;
return p;
}

View File

@ -710,6 +710,69 @@ repeat:
return valid;
}
/*
* Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
* "trim_len" specifies how many characters to keep for each directory.
* Must be 1 or more.
* It's done in-place.
*/
static void
shorten_dir_len(char_u *str, int trim_len)
{
char_u *tail, *s, *d;
int skip = FALSE;
int dirchunk_len = 0;
tail = gettail(str);
d = str;
for (s = str; ; ++s)
{
if (s >= tail) // copy the whole tail
{
*d++ = *s;
if (*s == NUL)
break;
}
else if (vim_ispathsep(*s)) // copy '/' and next char
{
*d++ = *s;
skip = FALSE;
dirchunk_len = 0;
}
else if (!skip)
{
*d++ = *s; // copy next char
if (*s != '~' && *s != '.') // and leading "~" and "."
{
++dirchunk_len; // only count word chars for the size
// keep copying chars until we have our preferred length (or
// until the above if/else branches move us along)
if (dirchunk_len >= trim_len)
skip = TRUE;
}
if (has_mbyte)
{
int l = mb_ptr2len(s);
while (--l > 0)
*d++ = *++s;
}
}
}
}
/*
* Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
* It's done in-place.
*/
void
shorten_dir(char_u *str)
{
shorten_dir_len(str, 1);
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*
@ -1358,9 +1421,18 @@ f_mkdir(typval_T *argvars, typval_T *rettv)
f_pathshorten(typval_T *argvars, typval_T *rettv)
{
char_u *p;
int trim_len = 1;
if (argvars[1].v_type != VAR_UNKNOWN)
{
trim_len = (int)tv_get_number(&argvars[1]);
if (trim_len < 1)
trim_len = 1;
}
rettv->v_type = VAR_STRING;
p = tv_get_string_chk(&argvars[0]);
if (p == NULL)
rettv->vval.v_string = NULL;
else
@ -1368,7 +1440,7 @@ f_pathshorten(typval_T *argvars, typval_T *rettv)
p = vim_strsave(p);
rettv->vval.v_string = p;
if (p != NULL)
shorten_dir(p);
shorten_dir_len(p, trim_len);
}
}
@ -2706,47 +2778,6 @@ vim_ispathsep_nocolon(int c)
;
}
/*
* Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
* It's done in-place.
*/
void
shorten_dir(char_u *str)
{
char_u *tail, *s, *d;
int skip = FALSE;
tail = gettail(str);
d = str;
for (s = str; ; ++s)
{
if (s >= tail) // copy the whole tail
{
*d++ = *s;
if (*s == NUL)
break;
}
else if (vim_ispathsep(*s)) // copy '/' and next char
{
*d++ = *s;
skip = FALSE;
}
else if (!skip)
{
*d++ = *s; // copy next char
if (*s != '~' && *s != '.') // and leading "~" and "."
skip = TRUE;
if (has_mbyte)
{
int l = mb_ptr2len(s);
while (--l > 0)
*d++ = *++s;
}
}
}
}
/*
* Return TRUE if the directory of "fname" exists, FALSE otherwise.
* Also returns TRUE if there is no directory name.

View File

@ -842,8 +842,7 @@ _OnSysChar(
ch = simplify_key(ch, &modifiers);
// remove the SHIFT modifier for keys where it's already included, e.g.,
// '(' and '*'
if (ch < 0x100 && !isalpha(ch) && isprint(ch))
modifiers &= ~MOD_MASK_SHIFT;
modifiers = may_remove_shift_modifier(modifiers, ch);
// Unify modifiers somewhat. No longer use ALT to set the 8th bit.
ch = extract_modifiers(ch, &modifiers, FALSE, NULL);

View File

@ -958,8 +958,7 @@ gui_x11_key_hit_cb(
// Remove the SHIFT modifier for keys where it's already included,
// e.g., '(', '!' and '*'.
if (!ASCII_ISALPHA(key) && key > 0x20 && key < 0x7f)
modifiers &= ~MOD_MASK_SHIFT;
modifiers = may_remove_shift_modifier(modifiers, key);
}
if (modifiers != 0)

View File

@ -2950,6 +2950,7 @@ find_special_key(
* Some keys already have Shift included, pass them as normal keys.
* Not when Ctrl is also used, because <C-H> and <C-S-H> are different.
* Also for <A-S-a> and <M-S-a>.
* This includes all printable ASCII characters except numbers and a-z.
*/
int
may_remove_shift_modifier(int modifiers, int key)
@ -2957,8 +2958,9 @@ may_remove_shift_modifier(int modifiers, int key)
if ((modifiers == MOD_MASK_SHIFT
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_ALT)
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_META))
&& ((key >= '@' && key <= 'Z')
|| key == '^' || key == '_'
&& ((key >= '!' && key <= '/')
|| (key >= ':' && key <= 'Z')
|| (key >= '[' && key <= '`')
|| (key >= '{' && key <= '~')))
return modifiers & ~MOD_MASK_SHIFT;
return modifiers;

View File

@ -2144,6 +2144,10 @@ scroll_cursor_halfway(int atend)
linenr_T old_topline = curwin->w_topline;
#endif
#ifdef FEAT_PROP_POPUP
// if the width changed this needs to be updated first
may_update_popup_position();
#endif
loff.lnum = boff.lnum = curwin->w_cursor.lnum;
#ifdef FEAT_FOLDING
(void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);

View File

@ -570,15 +570,24 @@ mac_lang_init(void)
{
if (mch_getenv((char_u *)"LANG") == NULL)
{
char buf[20];
char buf[50];
// $LANG is not set, either because it was unset or Vim was started
// from the Dock. Query the system locale.
if (LocaleRefGetPartString(NULL,
kLocaleLanguageMask | kLocaleLanguageVariantMask |
kLocaleRegionMask | kLocaleRegionVariantMask,
sizeof buf, buf) == noErr && *buf)
sizeof(buf) - 10, buf) == noErr && *buf)
{
if (strcasestr(buf, "utf-8") == NULL)
strcat(buf, ".UTF-8");
vim_setenv((char_u *)"LANG", (char_u *)buf);
# ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "");
# endif
# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
// Make sure strtod() uses a decimal point, not a comma.
setlocale(LC_NUMERIC, "C");
# endif
}
}

View File

@ -579,7 +579,7 @@ popup_show_curline(win_T *wp)
if (wp->w_cursor.lnum < wp->w_topline)
wp->w_topline = wp->w_cursor.lnum;
else if (wp->w_cursor.lnum >= wp->w_botline
&& (curwin->w_valid & VALID_BOTLINE))
&& (wp->w_valid & VALID_BOTLINE))
{
wp->w_topline = wp->w_cursor.lnum - wp->w_height + 1;
if (wp->w_topline < 1)
@ -931,16 +931,17 @@ apply_general_options(win_T *wp, dict_T *dict)
/*
* Go through the options in "dict" and apply them to popup window "wp".
* Only used when creating a new popup window.
* "create" is TRUE when creating a new popup window.
*/
static void
apply_options(win_T *wp, dict_T *dict)
apply_options(win_T *wp, dict_T *dict, int create)
{
int nr;
apply_move_options(wp, dict);
set_string_option_direct_in_win(wp, (char_u *)"signcolumn", -1,
if (create)
set_string_option_direct_in_win(wp, (char_u *)"signcolumn", -1,
(char_u *)"no", OPT_FREE|OPT_LOCAL, 0);
apply_general_options(wp, dict);
@ -949,16 +950,17 @@ apply_options(win_T *wp, dict_T *dict)
if (nr > 0)
wp->w_popup_flags |= POPF_HIDDEN;
// when "firstline" and "cursorline" are both set move the cursor to the
// "firstline".
// when "firstline" and "cursorline" are both set and the cursor would be
// above or below the displayed lines, move the cursor to "firstline".
if (wp->w_firstline > 0 && (wp->w_popup_flags & POPF_CURSORLINE))
{
if (wp->w_firstline > wp->w_buffer->b_ml.ml_line_count)
wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
else
else if (wp->w_cursor.lnum < wp->w_firstline
|| wp->w_cursor.lnum >= wp->w_firstline + wp->w_height)
wp->w_cursor.lnum = wp->w_firstline;
wp->w_topline = wp->w_cursor.lnum;
curwin->w_valid &= ~VALID_BOTLINE;
wp->w_topline = wp->w_firstline;
wp->w_valid &= ~VALID_BOTLINE;
}
popup_mask_refresh = TRUE;
@ -2106,7 +2108,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
if (d != NULL)
// Deal with options.
apply_options(wp, d);
apply_options(wp, d, TRUE);
#ifdef FEAT_TIMERS
if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL)
@ -2762,13 +2764,10 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
dict = argvars[1].vval.v_dict;
old_firstline = wp->w_firstline;
apply_move_options(wp, dict);
apply_general_options(wp, dict);
apply_options(wp, dict, FALSE);
if (old_firstline != wp->w_firstline)
redraw_win_later(wp, NOT_VALID);
popup_mask_refresh = TRUE;
popup_highlight_curline(wp);
popup_adjust_position(wp);
}
@ -3467,13 +3466,14 @@ popup_need_position_adjust(win_T *wp)
{
if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
return TRUE;
if (win_valid(wp->w_popup_prop_win))
return wp->w_popup_prop_changedtick
!= CHANGEDTICK(wp->w_popup_prop_win->w_buffer)
|| wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline
|| ((wp->w_popup_flags & POPF_CURSORLINE)
&& wp->w_cursor.lnum != wp->w_popup_last_curline);
return FALSE;
if (win_valid(wp->w_popup_prop_win)
&& (wp->w_popup_prop_changedtick
!= CHANGEDTICK(wp->w_popup_prop_win->w_buffer)
|| wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline))
return TRUE;
// May need to adjust the width if the cursor moved.
return wp->w_cursor.lnum != wp->w_popup_last_curline;
}
/*
@ -3646,6 +3646,17 @@ may_update_popup_mask(int type)
}
}
/*
* If the current window is a popup and something relevant changed, recompute
* the position and size.
*/
void
may_update_popup_position(void)
{
if (popup_is_popup(curwin) && popup_need_position_adjust(curwin))
popup_adjust_position(curwin);
}
/*
* Return a string of "len" spaces in IObuff.
*/

View File

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

View File

@ -21,6 +21,7 @@ void f_glob2regpat(typval_T *argvars, typval_T *rettv);
void f_globpath(typval_T *argvars, typval_T *rettv);
void f_isdirectory(typval_T *argvars, typval_T *rettv);
void f_mkdir(typval_T *argvars, typval_T *rettv);
void shorten_dir(char_u *str);
void f_pathshorten(typval_T *argvars, typval_T *rettv);
void f_readdir(typval_T *argvars, typval_T *rettv);
void f_readdirex(typval_T *argvars, typval_T *rettv);
@ -40,7 +41,6 @@ char_u *getnextcomp(char_u *fname);
char_u *get_past_head(char_u *path);
int vim_ispathsep(int c);
int vim_ispathsep_nocolon(int c);
void shorten_dir(char_u *str);
int dir_of_file_exists(char_u *fname);
int vim_fnamecmp(char_u *x, char_u *y);
int vim_fnamencmp(char_u *x, char_u *y, size_t len);

View File

@ -50,6 +50,7 @@ int popup_do_filter(int c);
int popup_no_mapping(void);
void popup_check_cursor_pos(void);
void may_update_popup_mask(int type);
void may_update_popup_position(void);
void update_popups(void (*win_update)(win_T *wp));
int set_ref_in_popups(int copyID);
int popup_is_popup(win_T *wp);

View File

@ -0,0 +1,16 @@
> +0&#ffffff0@74
|~+0#4040ff13&| @73
|~| @25|1+0#0000001#e0e0e08|0| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255@1| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|2| @17| +0#0000000#0000001| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|3| @17| +0#0000000#0000001| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|4| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|5| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|6| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|7| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|8| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|9| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|2+0#0000001#ffd7ff255|0| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @73
|~| @73
| +0#0000000&@56|0|,|0|-|1| @8|A|l@1|

View File

@ -0,0 +1,16 @@
> +0&#ffffff0@74
|~+0#4040ff13&| @73
|~| @25|5+0#0000001#ffd7ff255| @18| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|6+0#0000001#ffd7ff255| @18| +0#0000000#0000001| +0#4040ff13#ffffff0@26
|~| @25|7+0#0000001#ffd7ff255| @18| +0#0000000#0000001| +0#4040ff13#ffffff0@26
|~| @25|8+0#0000001#ffd7ff255| @18| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|9+0#0000001#ffd7ff255| @18| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#e0e0e08|0| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255@1| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|2| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|3| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|4| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @25|1+0#0000001#ffd7ff255|5| @17| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@26
|~| @73
|~| @73
|:+0#0000000&| @55|0|,|0|-|1| @8|A|l@1|

View File

@ -1,14 +1,14 @@
> +0&#ffffff0@74
|~+0#4040ff13&| @73
|~| @34|1+0#0000001#ffd7ff255|3| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#ffd7ff255|4| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#ffd7ff255|5| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#ffd7ff255|6| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#e0e0e08|7| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#e0e0e08|7| +0#0000000#0000001| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#ffd7ff255|8| +0#0000000#0000001| +0#4040ff13#ffffff0@35
|~| @34|1+0#0000001#ffd7ff255|9| +0#0000000#0000001| +0#4040ff13#ffffff0@35
|~| @34|2+0#0000001#ffd7ff255|0| +0#0000000#0000001| +0#4040ff13#ffffff0@35
|~| @34|2+0#0000001#ffd7ff255|0| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|2+0#0000001#ffd7ff255|1| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @34|2+0#0000001#ffd7ff255@1| +0#0000000#a8a8a8255| +0#4040ff13#ffffff0@35
|~| @73
|~| @73
|:+0#0000000&| @55|0|,|0|-|1| @8|A|l@1|

View File

@ -2,6 +2,8 @@
scriptencoding utf-8
source check.vim
func Test_environ()
unlet! $TESTENV
call assert_equal(0, has_key(environ(), 'TESTENV'))
@ -45,4 +47,23 @@ func Test_external_env()
call assert_equal('', result)
endfunc
func Test_mac_locale()
CheckFeature osxdarwin
" If $LANG is not set then the system locale will be used.
" Run Vim after unsetting all the locale environmental vars, and capture the
" output of :lang.
let lang_results = system("unset LANG; unset LC_MESSAGES; " ..
\ shellescape(v:progpath) ..
\ " --clean -esX -c 'redir @a' -c 'lang' -c 'put a' -c 'print' -c 'qa!' ")
" Check that:
" 1. The locale is the form of <locale>.UTF-8.
" 2. Check that fourth item (LC_NUMERIC) is properly set to "C".
" Example match: "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8"
call assert_match('"\([a-zA-Z_]\+\.UTF-8/\)\{3}C\(/[a-zA-Z_]\+\.UTF-8\)\{2}"',
\ lang_results,
\ "Default locale should have UTF-8 encoding set, and LC_NUMERIC set to 'C'")
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -500,6 +500,24 @@ func Test_pathshorten()
call assert_equal('.~f/bar', pathshorten('.~foo/bar'))
call assert_equal('~/f/bar', pathshorten('~/foo/bar'))
call assert_fails('call pathshorten([])', 'E730:')
" test pathshorten with optional variable to set preferred size of shortening
call assert_equal('', pathshorten('', 2))
call assert_equal('foo', pathshorten('foo', 2))
call assert_equal('/foo', pathshorten('/foo', 2))
call assert_equal('fo/', pathshorten('foo/', 2))
call assert_equal('fo/bar', pathshorten('foo/bar', 2))
call assert_equal('fo/ba/foobar', pathshorten('foo/bar/foobar', 2))
call assert_equal('/fo/ba/foobar', pathshorten('/foo/bar/foobar', 2))
call assert_equal('.fo/bar', pathshorten('.foo/bar', 2))
call assert_equal('~fo/bar', pathshorten('~foo/bar', 2))
call assert_equal('~.fo/bar', pathshorten('~.foo/bar', 2))
call assert_equal('.~fo/bar', pathshorten('.~foo/bar', 2))
call assert_equal('~/fo/bar', pathshorten('~/foo/bar', 2))
call assert_fails('call pathshorten([],2)', 'E730:')
call assert_notequal('~/fo/bar', pathshorten('~/foo/bar', 3))
call assert_equal('~/foo/bar', pathshorten('~/foo/bar', 3))
call assert_equal('~/f/bar', pathshorten('~/foo/bar', 0))
endfunc
func Test_strpart()

View File

@ -363,12 +363,12 @@ endfunc
" Test for insert path completion with completeslash option
func Test_ins_completeslash()
CheckMSWindows
call mkdir('Xdir')
let orig_shellslash = &shellslash
set cpt&
new
set noshellslash
set completeslash=
@ -654,4 +654,17 @@ func Test_complete_cmdline()
close!
endfunc
func Test_issue_7021()
CheckMSWindows
let orig_shellslash = &shellslash
set noshellslash
set completeslash=slash
call assert_false(expand('~') =~ '/')
let &shellslash = orig_shellslash
set completeslash=
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -1581,6 +1581,34 @@ func Test_popup_filter_win_execute()
call delete('XtestPopupWinExecute')
endfunc
func Test_popup_set_firstline()
CheckScreendump
let lines =<< trim END
let lines = range(1, 50)->map({_, v -> string(v)})
let g:id = popup_create(lines, #{
\ minwidth: 20,
\ maxwidth: 20,
\ minheight: &lines - 5,
\ maxheight: &lines - 5,
\ cursorline: 1,
\ })
call popup_setoptions(g:id, #{firstline: 10})
redraw
END
call writefile(lines, 'XtestPopupWinSetFirstline')
let buf = RunVimInTerminal('-S XtestPopupWinSetFirstline', #{rows: 16})
call VerifyScreenDump(buf, 'Test_popupwin_set_firstline_1', {})
call term_sendkeys(buf, ":call popup_setoptions(g:id, #{firstline: 5})\<CR>")
call term_sendkeys(buf, ":\<CR>")
call VerifyScreenDump(buf, 'Test_popupwin_set_firstline_2', {})
call StopVimInTerminal(buf)
call delete('XtestPopupWinSetFirstline')
endfunc
" this tests that we don't get stuck with an error in "win_execute()"
func Test_popup_filter_win_execute_error()
CheckScreendump

View File

@ -290,6 +290,9 @@ func Test_searchpair()
new
call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
" should not give an error for using "42"
call assert_equal(0, searchpair('a', 'b', 'c', '', 42))
4
call assert_equal(3, searchpair('\[', '', ']', 'bW'))
call assert_equal([0, 3, 2, 0], getpos('.'))

View File

@ -120,6 +120,43 @@ foobar/?
set spell&
endfunc
func Test_spelllang_inv_region()
set spell spelllang=en_xx
let messages = GetMessages()
call assert_equal('Warning: region xx not supported', messages[-1])
set spell& spelllang&
endfunc
func Test_compl_with_CTRL_X_CTRL_K_using_spell()
" When spell checking is enabled and 'dictionary' is empty,
" CTRL-X CTRL-K in insert mode completes using the spelling dictionary.
new
set spell spelllang=en dictionary=
set ignorecase
call feedkeys("Senglis\<c-x>\<c-k>\<esc>", 'tnx')
call assert_equal(['English'], getline(1, '$'))
call feedkeys("SEnglis\<c-x>\<c-k>\<esc>", 'tnx')
call assert_equal(['English'], getline(1, '$'))
set noignorecase
call feedkeys("Senglis\<c-x>\<c-k>\<esc>", 'tnx')
call assert_equal(['englis'], getline(1, '$'))
call feedkeys("SEnglis\<c-x>\<c-k>\<esc>", 'tnx')
call assert_equal(['English'], getline(1, '$'))
set spelllang=en_us
call feedkeys("Stheat\<c-x>\<c-k>\<esc>", 'tnx')
call assert_equal(['theater'], getline(1, '$'))
set spelllang=en_gb
call feedkeys("Stheat\<c-x>\<c-k>\<esc>", 'tnx')
" FIXME: commented out, expected theatre bug got theater. See issue #7025.
" call assert_equal(['theatre'], getline(1, '$'))
bwipe!
set spell& spelllang& dictionary& ignorecase&
endfunc
func Test_spellreall()
new
set spell

View File

@ -2123,6 +2123,20 @@ func Test_mapping_works_with_shift_alt()
call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4)
endfunc
func Test_mapping_works_with_alt_and_shift()
new
set timeoutlen=10
" mapping <A-?> works even though the code is A-S-?
for c in ['!', '$', '+', ':', '?', '^', '~']
call RunTest_mapping_mods('<A-' .. c .. '>', c, function('GetEscCodeCSI27'), 4)
call RunTest_mapping_mods('<A-' .. c .. '>', c, function('GetEscCodeCSIu'), 4)
endfor
bwipe!
set timeoutlen&
endfunc
func Test_mapping_works_with_ctrl_alt()
call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7)
call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7)

View File

@ -560,8 +560,8 @@ func Test_term_gettty()
endif
endif
call assert_fails('call term_gettty(buf, 2)', 'E1023:')
call assert_fails('call term_gettty(buf, -1)', 'E1023:')
call assert_fails('call term_gettty(buf, 2)', 'E475:')
call assert_fails('call term_gettty(buf, -1)', 'E475:')
call assert_equal('', term_gettty(buf + 1))

View File

@ -37,7 +37,7 @@ func T25_F()
if loops == 2
try
Xpath 'f' . loops
finally
final
Xpath 'g' . loops
endtry
endif
@ -49,19 +49,20 @@ func T25_F()
Xpath 'i'
endfunc
" Also try using "fina" and "final" and "finall" as abbraviations.
func T25_G()
if 1
try
Xpath 'A'
call T25_F()
Xpath 'B'
finally
fina
Xpath 'C'
endtry
else
try
Xpath 'D'
finally
finall
Xpath 'E'
endtry
endif

View File

@ -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,16 +720,26 @@ 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)
enddef
def Test_heredoc()
var lines =<< trim END # comment
text
END
assert_equal(['text'], lines)
CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:')
CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -1370,6 +1370,20 @@ def Test_double_closure_fails()
CheckScriptSuccess(lines)
enddef
def Test_nested_closure_fails()
let lines =<< trim END
vim9script
def FuncA()
FuncB(0)
enddef
def FuncB(n: number): list<string>
return map([0], {_, v -> n})
enddef
FuncA()
END
CheckScriptFailure(lines, 'E1012:')
enddef
def Test_sort_return_type()
let res: list<number>
res = [1, 2, 3]->sort()

File diff suppressed because it is too large Load Diff

View File

@ -192,7 +192,23 @@ func Test_tabwin_close()
call win_execute(l:wid, 'close')
" Should not crash.
call assert_true(v:true)
%bwipe!
" This tests closing a window in another tab, while leaving the tab open
" i.e. two windows in another tab.
tabedit
let w:this_win = 42
new
let othertab_wid = win_getid()
tabprevious
call win_execute(othertab_wid, 'q')
" drawing the tabline helps check that the other tab's windows and buffers
" are still valid
redrawtabline
" but to be certain, ensure we can focus the other tab too
tabnext
call assert_equal(42, w:this_win)
bwipe!
endfunc
" Test when closing a split window (above/below) restores space to the window

View File

@ -177,7 +177,7 @@ tv_get_bool_or_number_chk(typval_T *varp, int *denote, int want_bool)
switch (varp->v_type)
{
case VAR_NUMBER:
if (want_bool && varp->vval.v_number != 0
if (in_vim9script() && want_bool && varp->vval.v_number != 0
&& varp->vval.v_number != 1)
{
semsg(_(e_using_number_as_bool_nr), varp->vval.v_number);

View File

@ -3214,19 +3214,20 @@ def_function(exarg_T *eap, char_u *name_arg)
is_heredoc = TRUE;
}
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
// Check for ":cmd v =<< [trim] EOF"
// and ":cmd [a, b] =<< [trim] EOF"
// Where "cmd" can be "let", "var", "final" or "const".
arg = skipwhite(skiptowhite(p));
if (*arg == '[')
arg = vim_strchr(arg, ']');
if (arg != NULL)
{
arg = skipwhite(skiptowhite(arg));
if ( arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
&& ((p[0] == 'l'
&& p[1] == 'e'
&& (!ASCII_ISALNUM(p[2])
|| (p[2] == 't' && !ASCII_ISALNUM(p[3]))))))
if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
&& (checkforcmd(&p, "let", 2)
|| checkforcmd(&p, "var", 3)
|| checkforcmd(&p, "final", 5)
|| checkforcmd(&p, "const", 5)))
{
p = skipwhite(arg + 3);
if (STRNCMP(p, "trim", 4) == 0)

View File

@ -750,6 +750,46 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1757,
/**/
1756,
/**/
1755,
/**/
1754,
/**/
1753,
/**/
1752,
/**/
1751,
/**/
1750,
/**/
1749,
/**/
1748,
/**/
1747,
/**/
1746,
/**/
1745,
/**/
1744,
/**/
1743,
/**/
1742,
/**/
1741,
/**/
1740,
/**/
1739,
/**/
1738,
/**/
1737,
/**/

View File

@ -1992,7 +1992,8 @@ typedef int sock_T;
#define VV_ECHOSPACE 93
#define VV_ARGV 94
#define VV_COLLATE 95
#define VV_LEN 96 // number of v: vars
#define VV_DISALLOW_LET 96 // TODO: remove again
#define VV_LEN 97 // number of v: vars
// used for v_number in VAR_BOOL and VAR_SPECIAL
#define VVAL_FALSE 0L // VAR_BOOL
@ -2135,9 +2136,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

View File

@ -277,9 +277,12 @@ script_is_vim9()
lookup_script(char_u *name, size_t len, int vim9script)
{
int cc;
hashtab_T *ht = &SCRIPT_VARS(current_sctx.sc_sid);
hashtab_T *ht;
dictitem_T *di;
if (current_sctx.sc_sid <= 0)
return FAIL;
ht = &SCRIPT_VARS(current_sctx.sc_sid);
if (vim9script && !script_is_vim9())
return FAIL;
cc = name[len];
@ -4559,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.
*/
@ -4585,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);
@ -4624,6 +4632,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
eap->getline = exarg_getline;
eap->cookie = cctx;
l = heredoc_get(eap, op + 3, FALSE);
if (l == NULL)
return NULL;
if (cctx->ctx_skip != SKIP_YES)
{
@ -4726,7 +4736,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;
@ -4965,7 +4975,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;
@ -5116,6 +5126,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));
@ -5280,9 +5295,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)
@ -6912,7 +6927,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;
}
@ -6964,6 +6979,14 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
break;
case CMD_let:
if (get_vim_var_nr(VV_DISALLOW_LET))
{
emsg(_(e_cannot_use_let_in_vim9_script));
break;
}
// FALLTHROUGH
case CMD_var:
case CMD_final:
case CMD_const:
line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
if (line == p)

View File

@ -310,9 +310,12 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
// Check if any created closure is still in use.
for (idx = 0; idx < closure_count; ++idx)
{
partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
- closure_count + idx];
partial_T *pt;
int off = gap->ga_len - closure_count + idx;
if (off < 0)
continue; // count is off or already done
pt = ((partial_T **)gap->ga_data)[off];
if (pt->pt_refcount > 1)
{
int refcount = pt->pt_refcount;
@ -729,7 +732,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();
}
@ -2734,14 +2737,14 @@ done:
ret = OK;
failed:
// Also deal with closures when failed, they may already be in use
// somewhere.
handle_closure_in_use(&ectx, FALSE);
// When failed need to unwind the call stack.
while (ectx.ec_frame_idx != initial_frame_idx)
func_return(&ectx);
// Deal with any remaining closures, they may be in use somewhere.
if (ectx.ec_funcrefs.ga_len > 0)
handle_closure_in_use(&ectx, FALSE);
estack_pop();
current_sctx = save_current_sctx;

View File

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

View File

@ -2745,6 +2745,7 @@ win_free_mem(
{
frame_T *frp;
win_T *wp;
tabpage_T *win_tp = tp == NULL ? curtab : tp;
// Remove the window and its frame from the tree of frames.
frp = win->w_frame;
@ -2752,10 +2753,10 @@ win_free_mem(
vim_free(frp);
win_free(win, tp);
// When deleting the current window of another tab page select a new
// current window.
if (tp != NULL && win == tp->tp_curwin)
tp->tp_curwin = wp;
// When deleting the current window in the tab, select a new current
// window.
if (win == win_tp->tp_curwin)
win_tp->tp_curwin = wp;
return wp;
}