mirror of
https://github.com/vim/vim
synced 2025-07-29 18:01:41 +00:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
a5fe91e6dc | |||
cfcd011fcd | |||
c0e29010f6 | |||
8c7ad3631a | |||
f6a44f714a | |||
daff0fb738 | |||
bade44e5ca | |||
3697c9bbae | |||
c70bdab0b8 | |||
f3c51bbff1 | |||
8f187fc630 | |||
373863ed48 | |||
d47f50b331 | |||
30fd8204ce | |||
273af497ca | |||
7e9210ea53 | |||
6a33ef0deb | |||
58dbef330c | |||
9c4f55204f | |||
509f8031b2 |
@ -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|: >
|
||||
|
@ -1,4 +1,4 @@
|
||||
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 17
|
||||
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 26
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@ -70,7 +70,7 @@ Comments starting with # ~
|
||||
In legacy Vim script comments start with double quote. In Vim9 script
|
||||
comments start with #. >
|
||||
# declarations
|
||||
let count = 0 # number of occurrences
|
||||
var count = 0 # number of occurrences
|
||||
|
||||
The reason is that a double quote can also be the start of a string. In many
|
||||
places, especially halfway through an expression with a line break, it's hard
|
||||
@ -154,31 +154,32 @@ Vim9 script script-local functions are defined once when the script is sourced
|
||||
and cannot be deleted or replaced.
|
||||
|
||||
|
||||
Variable declarations with :let and :const ~
|
||||
Variable declarations with :var, :final and :const ~
|
||||
*vim9-declaration*
|
||||
Local variables need to be declared with `:let`. Local constants need to be
|
||||
declared with `:const`. We refer to both as "variables".
|
||||
Local variables need to be declared with `:var`. Local constants need to be
|
||||
declared with `:final` or `:const`. We refer to both as "variables" in this
|
||||
section.
|
||||
|
||||
Variables can be local to a script, function or code block: >
|
||||
vim9script
|
||||
let script_var = 123
|
||||
var script_var = 123
|
||||
def SomeFunc()
|
||||
let func_var = script_var
|
||||
var func_var = script_var
|
||||
if cond
|
||||
let block_var = func_var
|
||||
var block_var = func_var
|
||||
...
|
||||
|
||||
The variables are only visible in the block where they are defined and nested
|
||||
blocks. Once the block ends the variable is no longer accessible: >
|
||||
if cond
|
||||
let inner = 5
|
||||
var inner = 5
|
||||
else
|
||||
let inner = 0
|
||||
var inner = 0
|
||||
endif
|
||||
echo inner # Error!
|
||||
|
||||
The declaration must be done earlier: >
|
||||
let inner: number
|
||||
var inner: number
|
||||
if cond
|
||||
inner = 5
|
||||
else
|
||||
@ -186,10 +187,10 @@ The declaration must be done earlier: >
|
||||
endif
|
||||
echo inner
|
||||
|
||||
To intentionally avoid a variable being available later, a block can be used:
|
||||
>
|
||||
To intentionally hide a variable from code that follows, a block can be
|
||||
used: >
|
||||
{
|
||||
let temp = 'temp'
|
||||
var temp = 'temp'
|
||||
...
|
||||
}
|
||||
echo temp # Error!
|
||||
@ -197,9 +198,9 @@ To intentionally avoid a variable being available later, a block can be used:
|
||||
Declaring a variable with a type but without an initializer will initialize to
|
||||
zero, false or empty.
|
||||
|
||||
An existing variable cannot be assigned to with `:let`, since that implies a
|
||||
declaration. Global, window, tab, buffer and Vim variables can only be used
|
||||
without `:let`, because they are not really declared, they can also be deleted
|
||||
In Vim9 script `:let` cannot be used. An existing variable is assigned to
|
||||
without any command. The same for global, window, tab, buffer and Vim
|
||||
variables, because they are not really declared. They can also be deleted
|
||||
with `:unlet`.
|
||||
|
||||
Variables and functions cannot shadow previously defined or imported variables
|
||||
@ -209,51 +210,50 @@ Variables may shadow Ex commands, rename the variable if needed.
|
||||
Global variables and user defined functions must be prefixed with "g:", also
|
||||
at the script level. >
|
||||
vim9script
|
||||
let script_local = 'text'
|
||||
var script_local = 'text'
|
||||
g:global = 'value'
|
||||
let Funcref = g:ThatFunction
|
||||
var Funcref = g:ThatFunction
|
||||
|
||||
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
|
||||
Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be
|
||||
used to repeat a `:substitute` command.
|
||||
*vim9-const*
|
||||
In legacy Vim script "const list = []" would make the variable "list"
|
||||
immutable and also the value. Thus you cannot add items to the list. This
|
||||
differs from what many languages do. Vim9 script does it like TypeScript: only
|
||||
"list" is immutable, the value can be changed.
|
||||
|
||||
One can use `:const!` to make both the variable and the value immutable. Use
|
||||
|
||||
Constants ~
|
||||
*vim9-const* *vim9-final*
|
||||
How constants work varies between languages. Some consider a variable that
|
||||
can't be assigned another value a constant. JavaScript is an example. Others
|
||||
also make the value immutable, thus when a constant uses a list, the list
|
||||
cannot be changed. In Vim9 we can use both.
|
||||
|
||||
`:const` is used for making both the variable and the value a constant. Use
|
||||
this for composite structures that you want to make sure will not be modified.
|
||||
Example: >
|
||||
const myList = [1, 2]
|
||||
myList = [3, 4] # Error!
|
||||
myList[0] = 9 # Error!
|
||||
muList->add(3) # Error!
|
||||
|
||||
How this works: >
|
||||
vim9script
|
||||
const list = [1, 2]
|
||||
list = [3, 4] # Error!
|
||||
list[0] = 2 # OK
|
||||
`:final` is used for making only the variable a constant, the value can be
|
||||
changed. This is well known from Java. Example: >
|
||||
final myList = [1, 2]
|
||||
myList = [3, 4] # Error!
|
||||
myList[0] = 9 # OK
|
||||
muList->add(3) # OK
|
||||
|
||||
const! LIST = [1, 2]
|
||||
LIST = [3, 4] # Error!
|
||||
LIST[0] = 2 # Error!
|
||||
It is common to write constants as ALL_CAPS, but you don't have to.
|
||||
|
||||
The constant only applies to the value itself, not what it refers to. >
|
||||
cont females = ["Mary"]
|
||||
const! NAMES = [["John", "Peter"], females]
|
||||
final females = ["Mary"]
|
||||
const NAMES = [["John", "Peter"], females]
|
||||
NAMES[0] = ["Jack"] # Error!
|
||||
NAMES[0][0] = ["Jack"] # Error!
|
||||
NAMES[0][0] = "Jack" # Error!
|
||||
NAMES[1] = ["Emma"] # Error!
|
||||
Names[1][0] = "Emma" # OK, now females[0] == "Emma"
|
||||
|
||||
Rationale: TypeScript has no way to make the value immutable. One can use
|
||||
immutable types, but that quickly gets complicated for nested values. And
|
||||
with a type cast the value can be made mutable again, which means there is no
|
||||
guarantee the value won't change. Vim supports immutable values, in legacy
|
||||
script this was done with `:lockvar`. But that is an extra statement and also
|
||||
applies to nested values. Therefore the solution to use `:const!`.
|
||||
|
||||
*E1092*
|
||||
< *E1092*
|
||||
Declaring more than one variable at a time, using the unpack notation, is
|
||||
currently not supported: >
|
||||
let [v1, v2] = GetValues() # Error!
|
||||
var [v1, v2] = GetValues() # Error!
|
||||
That is because the type needs to be inferred from the list item type, which
|
||||
isn't that easy.
|
||||
|
||||
@ -296,7 +296,7 @@ A user defined function can be used as a function reference in an expression
|
||||
without `function()`. The argument types and return type will then be checked.
|
||||
The function must already have been defined. >
|
||||
|
||||
let Funcref = MyFunction
|
||||
var Funcref = MyFunction
|
||||
|
||||
When using `function()` the resulting type is "func", a function with any
|
||||
number of arguments and any return type. The function can be defined later.
|
||||
@ -307,53 +307,53 @@ Automatic line continuation ~
|
||||
In many cases it is obvious that an expression continues on the next line. In
|
||||
those cases there is no need to prefix the line with a backslash
|
||||
|line-continuation|. For example, when a list spans multiple lines: >
|
||||
let mylist = [
|
||||
var mylist = [
|
||||
'one',
|
||||
'two',
|
||||
]
|
||||
And when a dict spans multiple lines: >
|
||||
let mydict = #{
|
||||
var mydict = #{
|
||||
one: 1,
|
||||
two: 2,
|
||||
}
|
||||
Function call: >
|
||||
let result = Func(
|
||||
var result = Func(
|
||||
arg1,
|
||||
arg2
|
||||
)
|
||||
|
||||
For binary operators in expressions not in [], {} or () a line break is
|
||||
possible just before or after the operator. For example: >
|
||||
let text = lead
|
||||
var text = lead
|
||||
.. middle
|
||||
.. end
|
||||
let total = start +
|
||||
var total = start +
|
||||
end -
|
||||
correction
|
||||
let result = positive
|
||||
var result = positive
|
||||
? PosFunc(arg)
|
||||
: NegFunc(arg)
|
||||
|
||||
For a method call using "->" and a member using a dot, a line break is allowed
|
||||
before it: >
|
||||
let result = GetBuilder()
|
||||
var result = GetBuilder()
|
||||
->BuilderSetWidth(333)
|
||||
->BuilderSetHeight(777)
|
||||
->BuilderBuild()
|
||||
let result = MyDict
|
||||
var result = MyDict
|
||||
.member
|
||||
|
||||
< *E1050*
|
||||
To make it possible for the operator at the start of the line to be
|
||||
recognized, it is required to put a colon before a range. This will add
|
||||
"start" and print: >
|
||||
let result = start
|
||||
var result = start
|
||||
+ print
|
||||
Like this: >
|
||||
let result = start + print
|
||||
var result = start + print
|
||||
|
||||
This will assign "start" and print a line: >
|
||||
let result = start
|
||||
var result = start
|
||||
:+ print
|
||||
|
||||
It is also possible to split a function header over multiple lines, in between
|
||||
@ -411,15 +411,15 @@ The 'ignorecase' option is not used for comparators that use strings.
|
||||
White space ~
|
||||
|
||||
Vim9 script enforces proper use of white space. This is no longer allowed: >
|
||||
let var=234 # Error!
|
||||
let var= 234 # Error!
|
||||
let var =234 # Error!
|
||||
var name=234 # Error!
|
||||
var name= 234 # Error!
|
||||
var name =234 # Error!
|
||||
There must be white space before and after the "=": >
|
||||
let var = 234 # OK
|
||||
var name = 234 # OK
|
||||
White space must also be put before the # that starts a comment after a
|
||||
command: >
|
||||
let var = 234# Error!
|
||||
let var = 234 # OK
|
||||
var name = 234# Error!
|
||||
var name = 234 # OK
|
||||
|
||||
White space is required around most operators.
|
||||
|
||||
@ -440,7 +440,7 @@ Conditions and expressions ~
|
||||
|
||||
Conditions and expressions are mostly working like they do in JavaScript. A
|
||||
difference is made where JavaScript does not work like most people expect.
|
||||
Specifically, an empty list is falsey.
|
||||
Specifically, an empty list is falsy.
|
||||
|
||||
Any type of variable can be used as a condition, there is no error, not even
|
||||
for using a list or job. This is very much like JavaScript, but there are a
|
||||
@ -582,9 +582,10 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
||||
It is possible to nest `:def` inside another `:def` or
|
||||
`:function` up to about 50 levels deep.
|
||||
|
||||
[!] is used as with `:function`. Note that in Vim9
|
||||
script script-local functions cannot be deleted or
|
||||
redefined later in the same script.
|
||||
[!] is used as with `:function`. Note that
|
||||
script-local functions cannot be deleted or redefined
|
||||
later in Vim9 script. They can only be removed by
|
||||
reloading the same script.
|
||||
|
||||
*:enddef*
|
||||
:enddef End of a function defined with `:def`. It should be on
|
||||
@ -612,14 +613,14 @@ Limitations ~
|
||||
|
||||
Local variables will not be visible to string evaluation. For example: >
|
||||
def EvalString(): list<string>
|
||||
let list = ['aa', 'bb', 'cc', 'dd']
|
||||
var list = ['aa', 'bb', 'cc', 'dd']
|
||||
return range(1, 2)->map('list[v:val]')
|
||||
enddef
|
||||
|
||||
The map argument is a string expression, which is evaluated without the
|
||||
function scope. Instead, use a lambda: >
|
||||
def EvalString(): list<string>
|
||||
let list = ['aa', 'bb', 'cc', 'dd']
|
||||
var list = ['aa', 'bb', 'cc', 'dd']
|
||||
return range(1, 2)->map({ _, v -> list[v] })
|
||||
enddef
|
||||
|
||||
@ -690,23 +691,23 @@ builtin types added later, similarly to user functions.
|
||||
|
||||
And classes and interfaces can be used as types: >
|
||||
:class MyClass
|
||||
:let mine: MyClass
|
||||
:var mine: MyClass
|
||||
|
||||
:interface MyInterface
|
||||
:let mine: MyInterface
|
||||
:var mine: MyInterface
|
||||
|
||||
:class MyTemplate<Targ>
|
||||
:let mine: MyTemplate<number>
|
||||
:let mine: MyTemplate<string>
|
||||
:var mine: MyTemplate<number>
|
||||
:var mine: MyTemplate<string>
|
||||
|
||||
:class MyInterface<Targ>
|
||||
:let mine: MyInterface<number>
|
||||
:let mine: MyInterface<string>
|
||||
:var mine: MyInterface<number>
|
||||
:var mine: MyInterface<string>
|
||||
{not implemented yet}
|
||||
|
||||
|
||||
Variable types and type casting *variable-types*
|
||||
|
||||
Variable types and type casting ~
|
||||
*variable-types*
|
||||
Variables declared in Vim9 script or in a `:def` function have a type, either
|
||||
specified explicitly or inferred from the initialization.
|
||||
|
||||
@ -716,10 +717,10 @@ compiled code the "any" type is assumed.
|
||||
|
||||
This can be a problem when the "any" type is undesired and the actual type is
|
||||
expected to always be the same. For example, when declaring a list: >
|
||||
let l: list<number> = [1, g:two]
|
||||
var l: list<number> = [1, g:two]
|
||||
This will give an error, because "g:two" has type "any". To avoid this, use a
|
||||
type cast: >
|
||||
let l: list<number> = [1, <number>g:two]
|
||||
var l: list<number> = [1, <number>g:two]
|
||||
< *type-casting*
|
||||
The compiled code will then check that "g:two" is a number at runtime and give
|
||||
an error if it isn't. This is called type casting.
|
||||
@ -734,12 +735,12 @@ it to a string, use the |string()| function. Or use |str2nr()| to convert a
|
||||
string to a number.
|
||||
|
||||
|
||||
Type inference *type-inference*
|
||||
|
||||
Type inference ~
|
||||
*type-inference*
|
||||
In general: Whenever the type is clear it can be omitted. For example, when
|
||||
declaring a variable and giving it a value: >
|
||||
let var = 0 # infers number type
|
||||
let var = 'hello' # infers string type
|
||||
var name = 0 # infers number type
|
||||
var name = 'hello' # infers string type
|
||||
|
||||
The type of a list and dictionary comes from the common type of the values.
|
||||
If the values all have the same type, that type is used for the list or
|
||||
@ -749,8 +750,8 @@ dictionary. If there is a mix of types, the "any" type is used. >
|
||||
[1, 'x', 3] list<any>
|
||||
|
||||
|
||||
Stricter type checking *type-checking*
|
||||
|
||||
Stricter type checking ~
|
||||
*type-checking*
|
||||
In legacy Vim script, where a number was expected, a string would be
|
||||
automatically converted to a number. This was convenient for an actual number
|
||||
such as "123", but leads to unexpected problems (but no error message) if the
|
||||
@ -766,7 +767,7 @@ an error, thus breaking backwards compatibility. For example:
|
||||
|
||||
==============================================================================
|
||||
|
||||
5. Namespace, Import and Export
|
||||
5. Namespace, Import and Export
|
||||
*vim9script* *vim9-export* *vim9-import*
|
||||
|
||||
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
||||
@ -786,7 +787,7 @@ appear as the first statement in the file. It tells Vim to interpret the
|
||||
script in its own namespace, instead of the global namespace. If a file
|
||||
starts with: >
|
||||
vim9script
|
||||
let myvar = 'yes'
|
||||
var myvar = 'yes'
|
||||
Then "myvar" will only exist in this file. While without `vim9script` it would
|
||||
be available as `g:myvar` from any other script and function.
|
||||
|
||||
@ -809,7 +810,9 @@ Export ~
|
||||
*:export* *:exp*
|
||||
Exporting an item can be written as: >
|
||||
export const EXPORTED_CONST = 1234
|
||||
export let someValue = ...
|
||||
export var someValue = ...
|
||||
export final someValue = ...
|
||||
export const someValue = ...
|
||||
export def MyFunc() ...
|
||||
export class MyClass ...
|
||||
|
||||
@ -880,7 +883,7 @@ actually needed. A recommended mechanism:
|
||||
vim9script
|
||||
import FilterFunc from "../import/someother.vim"
|
||||
def searchfor#Stuff(arg: string)
|
||||
let filtered = FilterFunc(arg)
|
||||
var filtered = FilterFunc(arg)
|
||||
...
|
||||
< This goes in .../autoload/searchfor.vim. "searchfor" in the file name
|
||||
must be exactly the same as the prefix for the function name, that is how
|
||||
@ -889,7 +892,7 @@ actually needed. A recommended mechanism:
|
||||
3. Other functionality, possibly shared between plugins, contains the exported
|
||||
items and any private items. >
|
||||
vim9script
|
||||
let localVar = 'local'
|
||||
var localVar = 'local'
|
||||
export def FilterFunc(arg: string): string
|
||||
...
|
||||
< This goes in .../import/someother.vim.
|
||||
@ -909,7 +912,7 @@ namespace will be used for the imported item, even when "s:" is not specified.
|
||||
6. Future work: classes *vim9-classes*
|
||||
|
||||
Above "class" was mentioned a few times, but it has not been implemented yet.
|
||||
Most of Vim9 script can be created without this funcionality, and since
|
||||
Most of Vim9 script can be created without this functionality, and since
|
||||
implementing classes is going to be a lot of work, it is left for the future.
|
||||
For now we'll just make sure classes can be added later.
|
||||
|
||||
@ -941,7 +944,7 @@ invoke callbacks and handle timeouts and errors.
|
||||
|
||||
The :def command ~
|
||||
|
||||
Plugin writers have asked for a much faster Vim script. Investigations have
|
||||
Plugin writers have asked for much faster Vim script. Investigations have
|
||||
shown that keeping the existing semantics of function calls make this close to
|
||||
impossible, because of the overhead involved with calling a function, setting
|
||||
up the local function scope and executing lines. There are many details that
|
||||
@ -952,7 +955,7 @@ much overhead that cannot be avoided.
|
||||
Therefore the `:def` method to define a new-style function had to be added,
|
||||
which allows for a function with different semantics. Most things still work
|
||||
as before, but some parts do not. A new way to define a function was
|
||||
considered the best way to separate the old-style code from Vim9 script code.
|
||||
considered the best way to separate the legacy style code from Vim9 style code.
|
||||
|
||||
Using "def" to define a function comes from Python. Other languages use
|
||||
"function" which clashes with legacy Vim script.
|
||||
@ -968,12 +971,12 @@ instruction, at execution time the instruction would have to inspect the type
|
||||
of the arguments and decide what kind of addition to do. And when the
|
||||
type is dictionary throw an error. If the types are known to be numbers then
|
||||
an "add number" instruction can be used, which is faster. The error can be
|
||||
given at compile time, no error handling is needed at runtime, adding two
|
||||
numbers cannot fail.
|
||||
given at compile time, no error handling is needed at runtime, since adding
|
||||
two numbers cannot fail.
|
||||
|
||||
The syntax for types is similar to Java, since it is easy to understand and
|
||||
widely used. The type names are what were used in Vim before, with some
|
||||
additions such as "void" and "bool".
|
||||
The syntax for types, using <type> for compound types, is similar to Java. It
|
||||
is easy to understand and widely used. The type names are what were used in
|
||||
Vim before, with some additions such as "void" and "bool".
|
||||
|
||||
|
||||
Removing clutter and weirdness ~
|
||||
@ -981,10 +984,10 @@ Removing clutter and weirdness ~
|
||||
Once decided that `:def` functions have different syntax than legacy functions,
|
||||
we are free to add improvements to make the code more familiar for users who
|
||||
know popular programming languages. In other words: remove weird things that
|
||||
only Vim uses.
|
||||
only Vim does.
|
||||
|
||||
We can also remove clutter, mainly things that were done to make Vim script
|
||||
backwards compatible with good old Vi commands.
|
||||
backwards compatible with the good old Vi commands.
|
||||
|
||||
Examples:
|
||||
- Drop `:call` for calling a function and `:eval` for manipulating data.
|
||||
@ -993,44 +996,26 @@ Examples:
|
||||
|
||||
However, this does require that some things need to change:
|
||||
- Comments start with # instead of ", to avoid confusing them with strings.
|
||||
This is good anyway, it is known from several popular languages.
|
||||
- Ex command ranges need to be prefixed with a colon, to avoid confusion with
|
||||
expressions (single quote can be a string or a mark, "/" can be divide or a
|
||||
search command, etc.).
|
||||
|
||||
Goal is to limit the differences. A good criteria is that when the old syntax
|
||||
is used you are very likely to get an error message.
|
||||
is accidentally used you are very likely to get an error message.
|
||||
|
||||
|
||||
TypeScript syntax and semantics ~
|
||||
Syntax and semantics from popular languages ~
|
||||
|
||||
Script writers have complained that the Vim script syntax is unexpectedly
|
||||
different from what they are used to. To reduce this complaint popular
|
||||
languages are used as an example. At the same time, we do not want to abandon
|
||||
the well-known parts of legacy Vim script.
|
||||
|
||||
Since Vim already uses `:let` and `:const` and optional type checking is
|
||||
desirable, the JavaScript/TypeScript syntax fits best for variable
|
||||
declarations: >
|
||||
const greeting = 'hello' # string type is inferred
|
||||
let name: string
|
||||
...
|
||||
name = 'John'
|
||||
|
||||
Expression evaluation was already close to what JavaScript and other languages
|
||||
are doing. Some details are unexpected and can be fixed. For example how the
|
||||
|| and && operators work. Legacy Vim script: >
|
||||
let value = 44
|
||||
...
|
||||
let result = value || 0 # result == 1
|
||||
|
||||
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
||||
let value = 44
|
||||
...
|
||||
let result = value || 0 # result == 44
|
||||
|
||||
Another reason why TypeScript can be used as an example for Vim9 script is the
|
||||
For many things TypeScript is followed. It's a recent language that is
|
||||
gaining popularity and has similarities with Vim script. It also has a
|
||||
mix of static typing (a variable always has a known value type) and dynamic
|
||||
typing (a variable can have different types, this hanges at runtime). Since
|
||||
typing (a variable can have different types, this changes at runtime). Since
|
||||
legacy Vim script is dynamically typed and a lot of existing functionality
|
||||
(esp. builtin functions) depends on that, while static typing allows for much
|
||||
faster execution, we need to have this mix in Vim9 script.
|
||||
@ -1054,7 +1039,7 @@ Specific items from TypeScript we avoid:
|
||||
- TypeScript can use an expression like "99 || 'yes'" in a condition, but
|
||||
cannot assign the value to a boolean. That is inconsistent and can be
|
||||
annoying. Vim recognizes an expression with && or || and allows using the
|
||||
result as a bool.
|
||||
result as a bool. TODO: to be reconsidered
|
||||
- TypeScript considers an empty string as Falsy, but an empty list or dict as
|
||||
Truthy. That is inconsistent. In Vim an empty list and dict are also
|
||||
Falsy.
|
||||
@ -1063,6 +1048,80 @@ Specific items from TypeScript we avoid:
|
||||
which is more flexible, but is only checked at runtime.
|
||||
|
||||
|
||||
Declarations ~
|
||||
|
||||
Legacy Vim script uses `:let` for every assignment, while in Vim9 declarations
|
||||
are used. That is different, thus it's good to use a different command:
|
||||
`:var`. This is used in many languages. The semantics might be slightly
|
||||
different, but it's easily recognized as a declaration.
|
||||
|
||||
Using `:const` for constants is common, but the semantics vary. Some
|
||||
languages only make the variable immutable, others also make the value
|
||||
immutable. Since "final" is well known from Java for only making the variable
|
||||
immutable we decided to use that. And then `:const` can be used for making
|
||||
both immutable. This was also used in legacy Vim script and the meaning is
|
||||
almost the same.
|
||||
|
||||
What we end up with is very similar to Dart: >
|
||||
:var name # mutable variable and value
|
||||
:final name # immutable variable, mutable value
|
||||
:const name # immutable variable and value
|
||||
|
||||
Since legacy and Vim9 script will be mixed and global variables will be
|
||||
shared, optional type checking is desirable. Also, type inference will avoid
|
||||
the need for specifying the type in many cases. The TypeScript syntax fits
|
||||
best for adding types to declarations: >
|
||||
var name: string # string type is specified
|
||||
...
|
||||
name = 'John'
|
||||
const greeting = 'hello' # string type is inferred
|
||||
|
||||
This is how we put types in a declaration: >
|
||||
var mylist: list<string>
|
||||
final mylist: list<string> = ['foo']
|
||||
def Func(arg1: number, arg2: string): bool
|
||||
|
||||
Two alternatives were considered:
|
||||
1. Put the type before the name, like Dart: >
|
||||
var list<string> mylist
|
||||
final list<string> mylist = ['foo']
|
||||
def Func(number arg1, string arg2) bool
|
||||
2. Put the type after the variable name, but do not use a colon, like Go: >
|
||||
var mylist list<string>
|
||||
final mylist list<string> = ['foo']
|
||||
def Func(arg1 number, arg2 string) bool
|
||||
|
||||
The first is more familiar for anyone used to C or Java. The second one
|
||||
doesn't really has an advantage over the first, so let's discard the second.
|
||||
|
||||
Since we use type inference the type can be left out when it can be inferred
|
||||
from the value. This means that after `var` we don't know if a type or a name
|
||||
follows. That makes parsing harder, not only for Vim but also for humans.
|
||||
Also, it will not be allowed to use a variable name that could be a type name,
|
||||
using `var string string` is too confusing.
|
||||
|
||||
The chosen syntax, using a colon to separate the name from the type, adds
|
||||
punctuation, but it actually makes it easier to recognize the parts of a
|
||||
declaration.
|
||||
|
||||
|
||||
Expressions ~
|
||||
|
||||
Expression evaluation was already close to what JavaScript and other languages
|
||||
are doing. Some details are unexpected and can be fixed. For example how the
|
||||
|| and && operators work. Legacy Vim script: >
|
||||
var value = 44
|
||||
...
|
||||
var result = value || 0 # result == 1
|
||||
|
||||
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
||||
var value = 44
|
||||
...
|
||||
var result = value || 0 # result == 44
|
||||
|
||||
TODO: the semantics of || and && need to be reconsidered.
|
||||
|
||||
|
||||
Import and Export ~
|
||||
|
||||
A problem of legacy Vim script is that by default all functions and variables
|
||||
@ -1122,7 +1181,7 @@ only reported then. In case these errors should be found early, e.g. when
|
||||
testing, the `:defcompile` command will help out.
|
||||
|
||||
|
||||
Why not use an embeded language? ~
|
||||
Why not use an embedded language? ~
|
||||
|
||||
Vim supports interfaces to Perl, Python, Lua, Tcl and a few others. But
|
||||
these interfaces have never become widely used, for various reasons. When
|
||||
|
@ -1513,8 +1513,10 @@ set_one_cmd_context(
|
||||
break;
|
||||
#endif
|
||||
#ifdef FEAT_EVAL
|
||||
case CMD_final:
|
||||
case CMD_const:
|
||||
case CMD_let:
|
||||
case CMD_var:
|
||||
case CMD_if:
|
||||
case CMD_elseif:
|
||||
case CMD_while:
|
||||
|
@ -270,4 +270,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
|
||||
|
22
src/eval.c
22
src/eval.c
@ -1213,7 +1213,7 @@ set_var_lval(
|
||||
char_u *endp,
|
||||
typval_T *rettv,
|
||||
int copy,
|
||||
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
|
||||
char_u *op)
|
||||
{
|
||||
int cc;
|
||||
@ -1281,7 +1281,7 @@ set_var_lval(
|
||||
{
|
||||
typval_T tv;
|
||||
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_(e_cannot_mod));
|
||||
*endp = cc;
|
||||
@ -1319,7 +1319,7 @@ set_var_lval(
|
||||
listitem_T *ll_li = lp->ll_li;
|
||||
int ll_n1 = lp->ll_n1;
|
||||
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock a range"));
|
||||
return;
|
||||
@ -1378,7 +1378,7 @@ set_var_lval(
|
||||
/*
|
||||
* Assign to a List or Dictionary item.
|
||||
*/
|
||||
if (flags & LET_IS_CONST)
|
||||
if (flags & ASSIGN_CONST)
|
||||
{
|
||||
emsg(_("E996: Cannot lock a list or dict"));
|
||||
return;
|
||||
@ -1688,7 +1688,7 @@ next_for_item(void *fi_void, char_u *arg)
|
||||
{
|
||||
forinfo_T *fi = (forinfo_T *)fi_void;
|
||||
int result;
|
||||
int flag = in_vim9script() ? LET_NO_COMMAND : 0;
|
||||
int flag = in_vim9script() ? ASSIGN_NO_DECL : 0;
|
||||
listitem_T *item;
|
||||
|
||||
if (fi->fi_blob != NULL)
|
||||
@ -1741,11 +1741,12 @@ set_context_for_expression(
|
||||
char_u *arg,
|
||||
cmdidx_T cmdidx)
|
||||
{
|
||||
int got_eq = FALSE;
|
||||
int has_expr = cmdidx != CMD_let && cmdidx != CMD_var;
|
||||
int c;
|
||||
char_u *p;
|
||||
|
||||
if (cmdidx == CMD_let || cmdidx == CMD_const)
|
||||
if (cmdidx == CMD_let || cmdidx == CMD_var
|
||||
|| cmdidx == CMD_const || cmdidx == CMD_final)
|
||||
{
|
||||
xp->xp_context = EXPAND_USER_VARS;
|
||||
if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL)
|
||||
@ -1774,8 +1775,7 @@ set_context_for_expression(
|
||||
if (c == '&')
|
||||
{
|
||||
++xp->xp_pattern;
|
||||
xp->xp_context = cmdidx != CMD_let || got_eq
|
||||
? EXPAND_EXPRESSION : EXPAND_NOTHING;
|
||||
xp->xp_context = has_expr ? EXPAND_EXPRESSION : EXPAND_NOTHING;
|
||||
}
|
||||
else if (c != ' ')
|
||||
{
|
||||
@ -1792,7 +1792,7 @@ set_context_for_expression(
|
||||
}
|
||||
else if (c == '=')
|
||||
{
|
||||
got_eq = TRUE;
|
||||
has_expr = TRUE;
|
||||
xp->xp_context = EXPAND_EXPRESSION;
|
||||
}
|
||||
else if (c == '#'
|
||||
@ -1808,7 +1808,7 @@ set_context_for_expression(
|
||||
// Function name can start with "<SNR>" and contain '#'.
|
||||
break;
|
||||
}
|
||||
else if (cmdidx != CMD_let || got_eq)
|
||||
else if (has_expr)
|
||||
{
|
||||
if (c == '"') // string
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -11,26 +11,26 @@ static const unsigned short cmdidxs1[26] =
|
||||
/* d */ 108,
|
||||
/* e */ 133,
|
||||
/* f */ 156,
|
||||
/* g */ 172,
|
||||
/* h */ 178,
|
||||
/* i */ 187,
|
||||
/* j */ 206,
|
||||
/* k */ 208,
|
||||
/* l */ 213,
|
||||
/* m */ 275,
|
||||
/* n */ 293,
|
||||
/* o */ 313,
|
||||
/* p */ 325,
|
||||
/* q */ 364,
|
||||
/* r */ 367,
|
||||
/* s */ 387,
|
||||
/* t */ 456,
|
||||
/* u */ 501,
|
||||
/* v */ 512,
|
||||
/* w */ 531,
|
||||
/* x */ 545,
|
||||
/* y */ 555,
|
||||
/* z */ 556
|
||||
/* g */ 173,
|
||||
/* h */ 179,
|
||||
/* i */ 188,
|
||||
/* j */ 207,
|
||||
/* k */ 209,
|
||||
/* l */ 214,
|
||||
/* m */ 276,
|
||||
/* n */ 294,
|
||||
/* o */ 314,
|
||||
/* p */ 326,
|
||||
/* q */ 365,
|
||||
/* r */ 368,
|
||||
/* s */ 388,
|
||||
/* t */ 457,
|
||||
/* u */ 502,
|
||||
/* v */ 513,
|
||||
/* w */ 533,
|
||||
/* x */ 547,
|
||||
/* y */ 557,
|
||||
/* z */ 558
|
||||
};
|
||||
|
||||
/*
|
||||
@ -46,7 +46,7 @@ static const unsigned char cmdidxs2[26][26] =
|
||||
/* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 37, 40, 46, 56, 58, 59, 60, 0, 62, 0, 65, 0, 0, 0 },
|
||||
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 19, 0, 0, 20, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 17, 0, 18, 0, 0 },
|
||||
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0 },
|
||||
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 },
|
||||
/* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 },
|
||||
/* h */ { 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* i */ { 1, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0, 0, 0, 0, 0, 14, 0, 16, 0, 0, 0, 0, 0 },
|
||||
@ -62,11 +62,11 @@ static const unsigned char cmdidxs2[26][26] =
|
||||
/* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 50, 0, 51, 0, 63, 64, 0, 65, 0 },
|
||||
/* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 0, 0 },
|
||||
/* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 10, 13, 0, 0, 0, 0, 16, 0, 17, 0, 0, 0, 0, 0 },
|
||||
/* v */ { 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 11, 14, 0, 0, 0, 0, 17, 0, 18, 0, 0, 0, 0, 0 },
|
||||
/* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 12, 13, 0, 0, 0, 0 },
|
||||
/* x */ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 7, 0, 0, 8, 0, 0, 0, 0, 0 },
|
||||
/* y */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static const int command_count = 569;
|
||||
static const int command_count = 571;
|
||||
|
@ -592,6 +592,9 @@ EXCMD(CMD_filter, "filter", ex_wrongmodifier,
|
||||
EXCMD(CMD_find, "find", ex_find,
|
||||
EX_RANGE|EX_BANG|EX_FILE1|EX_CMDARG|EX_ARGOPT|EX_TRLBAR|EX_NEEDARG,
|
||||
ADDR_OTHER),
|
||||
EXCMD(CMD_final, "final", ex_let,
|
||||
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
EXCMD(CMD_finally, "finally", ex_finally,
|
||||
EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
@ -1648,6 +1651,9 @@ EXCMD(CMD_update, "update", ex_update,
|
||||
EXCMD(CMD_vglobal, "vglobal", ex_global,
|
||||
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_LINES),
|
||||
EXCMD(CMD_var, "var", ex_var,
|
||||
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
EXCMD(CMD_version, "version", ex_version,
|
||||
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
|
||||
ADDR_NONE),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
115
src/filepath.c
115
src/filepath.c
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
16
src/testdir/dumps/Test_popupwin_set_firstline_1.dump
Normal file
16
src/testdir/dumps/Test_popupwin_set_firstline_1.dump
Normal 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|
|
16
src/testdir/dumps/Test_popupwin_set_firstline_2.dump
Normal file
16
src/testdir/dumps/Test_popupwin_set_firstline_2.dump
Normal 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|
|
@ -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|
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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('.'))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
/**/
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user