patch 8.2.0149: maintaining a Vim9 branch separately is more work

Problem:    Maintaining a Vim9 branch separately is more work.
Solution:   Merge the Vim9 script changes.
This commit is contained in:
Bram Moolenaar 2020-01-26 15:56:19 +01:00
parent 1d9215b9aa
commit 8a7d6542b3
65 changed files with 11777 additions and 1327 deletions

View File

@ -31,6 +31,8 @@ flavours of UNIX. Porting to other systems should not be very difficult.
Older versions of Vim run on MS-DOS, MS-Windows 95/98/Me/NT/2000, Amiga DOS,
Atari MiNT, BeOS, RISC OS and OS/2. These are no longer maintained.
For Vim9 script see [README_VIM9](README_VIM9.md).
## Distribution ##
You can often use your favorite package manager to install Vim. On Mac and

344
README_VIM9.md Normal file
View File

@ -0,0 +1,344 @@
![Vim Logo](https://github.com/vim/vim/blob/master/runtime/vimlogo.gif)
# What is Vim9?
This is an experimental side of [Vim](https://github.com/vim/vim).
It explores ways of making Vim script faster and better.
WARNING: The Vim9 script features are in the early stages of development,
anything can break!
# Why Vim9?
## 1. FASTER VIM SCRIPT
The third item on the poll results of 2018, after popup windows and text
properties, is faster Vim script. So how do we do that?
I have been throwing some ideas around, and soon came to the conclusion
that the current way functions are called and executed, with
dictionaries for the arguments and local variables, is never going to be
very fast. We're lucky if we can make it twice as fast. The overhead
of a function call and executing every line is just too high.
So what then? We can only make something fast by having a new way of
defining a function, with similar but different properties of the old
way:
* Arguments are only available by name, not through the a: dictionary or
the a:000 list.
* Local variables are not available in an l: dictionary.
* A few more things that slow us down, such as exception handling details.
I Implemented a "proof of concept" and measured the time to run a simple
for loop with an addition (Justin used this example in his presentation,
full code is below):
``` vim
let sum = 0
for i in range(1, 2999999)
let sum += i
endfor
```
| how | time in sec |
| --------| -------- |
| Vim old | 5.018541 |
| Python | 0.369598 |
| Lua | 0.078817 |
| Vim new | 0.073595 |
That looks very promising! It's just one example, but it shows how much
we can gain, and also that Vim script can be faster than builtin
interfaces.
In practice the script would not do something useless as counting but change
the text. For example, re-indent all the lines:
``` vim
let totallen = 0
for i in range(1, 100000)
call setline(i, ' ' .. getline(i))
let totallen += len(getline(i))
endfor
```
| how | time in sec |
| --------| -------- |
| Vim old | 0.853752 |
| Python | 0.304584 |
| Lua | 0.286573 |
| Vim new | 0.190276 |
The differences are smaller, but Vim 9 script is clearly the fastest.
How does Vim9 script work? The function is first compiled into a sequence of
instructions. Each instruction has one or two parameters and a stack is
used to store intermediate results. Local variables are also on the
stack, space is reserved during compilation. This is a fairly normal
way of compilation into an intermediate format, specialized for Vim,
e.g. each stack item is a typeval_T. And one of the instructions is
"execute Ex command", for commands that are not compiled.
## 2. PHASING OUT INTERFACES
Attempts have been made to implement functionality with built-in script
languages such as Python, Perl, Lua, Tcl and Ruby. This never gained much
foothold, for various reasons.
Instead of using script language support in Vim:
* Encourage implementing external tools in any language and communicate
with them. The job and channel support already makes this possible.
Really any language can be used, also Java and Go, which are not
available built-in.
* Phase out the built-in language interfaces, make maintenance a bit easier
and executables easier to build. They will be kept for backwards
compatibility, no new features.
* Improve the Vim script language, it is used to communicate with the external
tool and implements the Vim side of the interface. Also, it can be used when
an external tool is undesired.
All together this creates a clear situation: Vim with the +eval feature
will be sufficient for most plugins, while some plugins require
installing a tool that can be written in any language. No confusion
about having Vim but the plugin not working because some specific
language is missing. This is a good long term goal.
Rationale: Why is it better to run a tool separately from Vim than using a
built-in interface and interpreter? Take for example something that is
written in Python:
* The built-in interface uses the embedded python interpreter. This is less
well maintained than the python command. Building Vim with it requires
installing developer packages. If loaded dynamically there can be a version
mismatch.
* When running the tool externally the standard python command can be used,
which is quite often available by default or can be easily installed.
* The built-in interface has an API that is unique for Vim with Python. This is
an extra API to learn.
* A .py file can be compiled into a .pyc file and execute much faster.
* Inside Vim multi-threading can cause problems, since the Vim core is single
threaded. In an external tool there are no such problems.
* The Vim part is written in .vim files, the Python part is in .py files, this
is nicely separated.
* Disadvantage: An interface needs to be made between Vim and Python.
JSON is available for this, and it's fairly easy to use. But it still
requires implementing asynchronous communication.
## 3. BETTER VIM SCRIPT
To make Vim faster a new way of defining a function needs to be added.
While we are doing that, since the lines in this function won't be fully
backwards compatible anyway, we can also make Vim script easier to use.
In other words: "less weird". Making it work more like modern
programming languages will help. No surprises.
A good example is how in a function the arguments are prefixed with
"a:". No other language I know does that, so let's drop it.
Taking this one step further is also dropping "s:" for script-local variables;
everything at the script level is script-local by default. Since this is not
backwards compatible it requires a new script style: Vim9 script!
It should be possible to convert code from other languages to Vim
script. We can add functionality to make this easier. This still needs
to be discussed, but we can consider adding type checking and a simple
form of classes. If you look at JavaScript for example, it has gone
through these stages over time, adding real class support and now
TypeScript adds type checking. But we'll have to see how much of that
we actually want to include in Vim script. Ideally a conversion tool
can take Python, JavaScript or TypeScript code and convert it to Vim
script, with only some things that cannot be converted.
Vim script won't work the same as any specific language, but we can use
mechanisms that are commonly known, ideally with the same syntax. One
thing I have been thinking of is assignments without ":let". I often
make that mistake (after writing JavaScript especially). I think it is
possible, if we make local variables shadow commands. That should be OK,
if you shadow a command you want to use, just rename the variable.
Using "let" and "const" to declare a variable, like in JavaScript and
TypeScript, can work:
``` vim
def MyFunction(arg: number): number
let local = 1
let todo = arg
const ADD = 88
while todo > 0
local += ADD
--todo
endwhile
return local
enddef
```
The similarity with JavaScript/TypeScript can also be used for dependencies
between files. Vim currently uses the `:source` command, which has several
disadvantages:
* In the sourced script, is not clear what it provides. By default all
functions are global and can be used elsewhere.
* In a script that sources other scripts, it is not clear what function comes
from what sourced script. Finding the implementation is a hassle.
* Prevention of loading the whole script twice must be manually implemented.
We can use the `:import` and `:export` commands from the JavaScript standard to
make this much better. For example, in script "myfunction.vim" define a
function and export it:
``` vim
vim9script " Vim9 script syntax used here
let local = 'local variable is not exported, script-local'
export def MyFunction() " exported function
...
def LocalFunction() " not exported, script-local
...
```
And in another script import the function:
``` vim
vim9script " Vim9 script syntax used here
import MyFunction from 'myfunction.vim'
```
This looks like JavaScript/TypeScript, thus many users will understand the
syntax.
These are ideas, this will take time to design, discuss and implement.
Eventually this will lead to Vim 9!
## Code for sum time measurements
Vim was build with -O2.
``` vim
func VimOld()
let sum = 0
for i in range(1, 2999999)
let sum += i
endfor
return sum
endfunc
func Python()
py3 << END
sum = 0
for i in range(1, 3000000):
sum += i
END
return py3eval('sum')
endfunc
func Lua()
lua << END
sum = 0
for i = 1, 2999999 do
sum = sum + i
end
END
return luaeval('sum')
endfunc
def VimNew()
let sum = 0
for i in range(1, 2999999)
let sum += i
endfor
return sum
enddef
let start = reltime()
echo VimOld()
echo 'Vim old: ' .. reltimestr(reltime(start))
let start = reltime()
echo Python()
echo 'Python: ' .. reltimestr(reltime(start))
let start = reltime()
echo Lua()
echo 'Lua: ' .. reltimestr(reltime(start))
let start = reltime()
echo VimNew()
echo 'Vim new: ' .. reltimestr(reltime(start))
```
## Code for indent time measurements
``` vim
def VimNew(): number
let totallen = 0
for i in range(1, 100000)
setline(i, ' ' .. getline(i))
totallen += len(getline(i))
endfor
return totallen
enddef
func VimOld()
let totallen = 0
for i in range(1, 100000)
call setline(i, ' ' .. getline(i))
let totallen += len(getline(i))
endfor
return totallen
endfunc
func Lua()
lua << END
b = vim.buffer()
totallen = 0
for i = 1, 100000 do
b[i] = " " .. b[i]
totallen = totallen + string.len(b[i])
end
END
return luaeval('totallen')
endfunc
func Python()
py3 << END
cb = vim.current.buffer
totallen = 0
for i in range(0, 100000):
cb[i] = ' ' + cb[i]
totallen += len(cb[i])
END
return py3eval('totallen')
endfunc
new
call setline(1, range(100000))
let start = reltime()
echo VimOld()
echo 'Vim old: ' .. reltimestr(reltime(start))
bwipe!
new
call setline(1, range(100000))
let start = reltime()
echo Python()
echo 'Python: ' .. reltimestr(reltime(start))
bwipe!
new
call setline(1, range(100000))
let start = reltime()
echo Lua()
echo 'Lua: ' .. reltimestr(reltime(start))
bwipe!
new
call setline(1, range(100000))
let start = reltime()
echo VimNew()
echo 'Vim new: ' .. reltimestr(reltime(start))
bwipe!
```

View File

@ -149,6 +149,7 @@ DOCS = \
version7.txt \
version8.txt \
vi_diff.txt \
vim9.txt \
visual.txt \
windows.txt \
workshop.txt
@ -289,6 +290,7 @@ HTMLS = \
version8.html \
vi_diff.html \
vimindex.html \
vim9.html \
visual.html \
windows.html \
workshop.html

View File

@ -12,6 +12,10 @@ Note: Expression evaluation can be disabled at compile time. If this has been
done, the features in this document are not available. See |+eval| and
|no-eval-feature|.
This file is about the backwards compatible Vim script. For Vim9 script,
which executes much faster, supports type checking and much more, see
|vim9.txt|.
1. Variables |variables|
1.1 Variable types
1.2 Function references |Funcref|
@ -2512,8 +2516,8 @@ haslocaldir([{winnr} [, {tabnr}]])
or |:tcd|
hasmapto({what} [, {mode} [, {abbr}]])
Number |TRUE| if mapping to {what} exists
histadd({history}, {item}) String add an item to a history
histdel({history} [, {item}]) String remove an item from a history
histadd({history}, {item}) Number add an item to a history
histdel({history} [, {item}]) Number remove an item from a history
histget({history} [, {index}]) String get the item {index} from a history
histnr({history}) Number highest index of a history
hlexists({name}) Number |TRUE| if highlight group {name} exists
@ -10894,6 +10898,9 @@ New functions can be defined. These can be called just like builtin
functions. The function executes a sequence of Ex commands. Normal mode
commands can be executed with the |:normal| command.
This section is about the legacy functions. For the Vim9 functions, which
execute much faster, support type checking and more, see |vim9.txt|.
The function name must start with an uppercase letter, to avoid confusion with
builtin functions. To prevent from using the same name in different scripts
avoid obvious, short names. A good habit is to start the function name with

View File

@ -6221,6 +6221,7 @@ A jump table for the options with a short description can be found at |Q_op|.
compiler/ compiler files |:compiler|
doc/ documentation |write-local-help|
ftplugin/ filetype plugins |write-filetype-plugin|
import/ files that are found by `:import`
indent/ indent scripts |indent-expression|
keymap/ key mapping files |mbyte-keymap|
lang/ menu translations |:menutrans|

561
runtime/doc/vim9.txt Normal file
View File

@ -0,0 +1,561 @@
*vim9.txt* For Vim version 8.2. Last change: 2019 Dec 06
VIM REFERENCE MANUAL by Bram Moolenaar
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
Vim9 script commands and expressions.
Most expression help is in |eval.txt|. This file is about the new syntax and
features in Vim9 script.
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
1 What is Vim9 script? |vim9-script|
2. Differences |vim9-differences|
3. New style functions |fast-functions|
4. Types |vim9-types|
5. Namespace, Import and Export |vim9script|
9. Rationale |vim9-rationale|
==============================================================================
1. What is Vim9 script? *vim9-script*
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
Vim script has been growing over time, while keeping backwards compatibility.
That means bad choices from the past often can't be changed. Execution is
quite slow, every line is parsed every time it is executed.
The main goal of Vim9 script is to drastically improve performance. An
increase in execution speed of 10 to 100 times can be expected. A secondary
goal is to avoid Vim-specific constructs and get closer to commonly used
programming languages, such as JavaScript, TypeScript and Java.
The performance improvements can only be achieved by not being 100% backwards
compatible. For example, in a function the arguments are not available in the
"a:" dictionary, as creating that dictionary adds quite a lot of overhead.
Other differences are more subtle, such as how errors are handled.
The Vim9 script syntax and semantics are used in:
- a function defined with the `:def` command
- a script file where the first command is `vim9script`
When using `:function` in a Vim9 script file the legacy syntax is used.
However, this is discouraged.
Vim9 script and legacy Vim script can be mixed. There is no need to rewrite
old scripts, they keep working as before.
==============================================================================
2. Differences from legacy Vim script *vim9-differences*
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
Vim9 functions ~
`:def` has no extra arguments like `:function` does: "range", "abort", "dict"
or "closure". A `:def` function always aborts on an error, does not get a
range passed and cannot be a "dict" function.
In the function body:
- Arguments are accessed by name, without "a:".
- There is no "a:" dictionary or "a:000" list. Variable arguments are defined
with a name and have a list type: >
def MyFunc(...itemlist: list<type>)
for item in itemlist
...
Variable declarations with :let and :const ~
Local variables need to be declared with `:let`. Local constants need to be
declared with `:const`. We refer to both as "variables".
Variables can be local to a script, function or code block: >
vim9script
let script_var = 123
def SomeFunc()
let func_var = script_var
if cond
let 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
else
let inner = 0
endif
echo inner " Error!
The declaration must be done earlier: >
let inner: number
if cond
inner = 5
else
inner = 0
endif
echo inner
To intentionally use a variable that won't be available later, a block can be
used: >
{
let temp = 'temp'
...
}
echo temp " Error!
An existing variable cannot be assigend to with `:let`, since that implies a
declaration. An exception is global variables: these can be both used with
and without `:let`, because there is no rule about where they are declared.
Variables cannot shadow previously defined variables.
Variables may shadow Ex commands, rename the variable if needed.
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
used to repeat a `:substitute` command.
Omitting :call and :eval ~
Functions can be called without `:call`: >
writefile(lines, 'file')
Using `:call` is still posible, but this is discouraged.
A method call without `eval` is possible, so long as the start is an
identifier or can't be an Ex command. It does not work for string constants: >
myList->add(123) " works
g:myList->add(123) " works
[1, 2, 3]->Process() " works
#{a: 1, b: 2}->Process() " works
{'a': 1, 'b': 2}->Process() " works
"foobar"->Process() " does NOT work
eval "foobar"->Process() " works
No curly braces expansion ~
|curly-braces-names| cannot be used.
Comperators ~
The 'ignorecase' option is not used for comperators 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!
There must be white space before and after the "=": >
let var = 234 " OK
White space is required around most operators.
White space is not allowed:
- Between a function name and the "(": >
call Func (arg) " Error!
call Func
\ (arg) " Error!
call Func(arg) " OK
call Func(
\ arg) " OK
Conditions and expressions ~
Conditions and expression 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.
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
few exceptions.
type TRUE when ~
bool v:true
number non-zero
float non-zero
string non-empty
blob non-empty
list non-empty (different from JavaScript)
dictionary non-empty (different from JavaScript)
funcref when not NULL
partial when not NULL
special v:true
job when not NULL
channel when not NULL
class when not NULL
object when not NULL (TODO: when isTrue() returns v:true)
The boolean operators "||" and "&&" do not change the value: >
8 || 2 == 8
0 || 2 == 2
0 || '' == ''
8 && 2 == 2
0 && 2 == 0
[] && 2 == []
When using `..` for string concatenation the arguments are always converted to
string. >
'hello ' .. 123 == 'hello 123'
'hello ' .. v:true == 'hello true'
In Vim9 script one can use "true" for v:true and "false" for v:false.
==============================================================================
3. New style functions *fast-functions*
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
*:def*
:def[!] {name}([arguments])[: {return-type}
Define a new function by the name {name}. The body of
the function follows in the next lines, until the
matching `:enddef`.
When {return-type} is omitted the return type will be
decided upon by the first encountered `return`
statement in the function. E.g., for: >
return 'message'
< The return type will be "string".
{arguments} is a sequence of zero or more argument
declarations. There are three forms:
{name}: {type}
{name} = {value}
{name}: {type} = {value}
The first form is a mandatory argument, the caller
must always provide them.
The second and third form are optional arguments.
When the caller omits an argument the {value} is used.
[!] is used as with `:function`.
*:enddef*
:enddef End of a function defined with `:def`.
==============================================================================
4. Types *vim9-types*
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
The following builtin types are supported:
bool
number
float
string
blob
list<type>
dict<type>
(a: type, b: type): type
job
channel
Not supported yet:
tuple<a: type, b: type, ...>
These types can be used in declarations, but no variable will have this type:
type|type
void
any
There is no array type, use list<type> instead. For a list constant an
efficient implementation is used that avoids allocating lot of small pieces of
memory.
A function defined with `:def` must declare the return type. If there is no
type then the function doesn't return anything. "void" is used in type
declarations.
Custom types can be defined with `:type`: >
:type MyList list<string>
{not implemented yet}
And classes and interfaces can be used as types: >
:class MyClass
:let mine: MyClass
:interface MyInterface
:let mine: MyInterface
:class MyTemplate<Targ>
:let mine: MyTemplate<number>
:let mine: MyTemplate<string>
:class MyInterface<Targ>
:let mine: MyInterface<number>
:let mine: MyInterface<string>
{not implemented yet}
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
==============================================================================
5. Namespace, Import and Export
*vim9script* *vim9-export* *vim9-import*
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
A Vim9 script can be written to be imported. This means that everything in
the script is local, unless exported. Those exported items, and only those
items, can then be imported in another script.
Namespace ~
*:vim9script* *:vim9*
To recognize an file that can be imported the `vim9script` statement must
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'
Then "myvar" will only exist in this file. While without `vim9script` it would
be available as `g:myvar` from any other script and function.
The variables at the file level are very much like the script-local "s:"
variables in legacy Vim script, but the "s:" is omitted.
In Vim9 script the global "g:" namespace can still be used as before.
A side effect of `:vim9script` is that the 'cpoptions' option is set to the
Vim default value, like with: >
:set cpo&vim
One of the effects is that |line-continuation| is always enabled.
The original value of 'cpoptions' is restored at the end of the script.
Export ~
*:export* *:exp*
Exporting one item can be written as: >
export const EXPORTED_CONST = 1234
export let someValue = ...
export def MyFunc() ...
export class MyClass ...
As this suggests, only constants, variables, `:def` functions and classes can
be exported.
Alternatively, an export statement can be used to export several already
defined (otherwise script-local) items: >
export {EXPORTED_CONST, someValue, MyFunc, MyClass}
Import ~
*:import* *:imp*
The exported items can be imported individually in another Vim9 script: >
import EXPORTED_CONST from "thatscript.vim"
import MyClass from "myclass.vim"
To import multiple items at the same time: >
import {someValue, MyClass} from "thatscript.vim"
In case the name is ambigiuous, another name can be specified: >
import MyClass as ThatClass from "myclass.vim"
import {someValue, MyClass as ThatClass} from "myclass.vim"
To import all exported items under a specific identifier: >
import * as That from 'thatscript.vim'
Then you can use "That.EXPORTED_CONST", "That.someValue", etc. You are free
to choose the name "That", but it is highly recommended to use the name of the
script file to avoid confusion.
The script name after `import` can be:
- A relative path, starting "." or "..". This finds a file relative to the
location of the script file itself. This is useful to split up a large
plugin into several files.
- An absolute path, starting with "/" on Unix or "D:/" on MS-Windows. This
will be rarely used.
- A path not being relative or absolute. This will be found in the
"import" subdirectories of 'runtimepath' entries. The name will usually be
longer and unique, to avoid loading the wrong file.
Once a vim9 script file has been imported, the result is cached and used the
next time the same script is imported. It will not be read again.
*:import-cycle*
The `import` commands are executed when encountered. If that script (directly
or indirectly) imports the current script, then items defined after the
`import` won't be processed yet. Therefore cyclic imports can exist, but may
result in undefined items.
Import in an autoload script ~
For optimal startup speed, loading scripts should be postponed until they are
actually needed. A recommended mechamism:
1. In the plugin define user commands, functions and/or mappings that refer to
an autoload script. >
command -nargs=1 SearchForStuff call searchfor#Stuff(<f-args>)
< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen.
2. In the autocommand script do the actual work. You can import items from
other files to split up functionality in appropriate pieces. >
vim9script
import FilterFunc from "../import/someother.vim"
def searchfor#Stuff(arg: string)
let 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
Vim finds the file.
3. Other functionality, possibly shared between plugins, contains the exported
items and any private items. >
vim9script
let localVar = 'local'
export def FilterFunc(arg: string): string
...
< This goes in .../import/someother.vim.
Import in legacy Vim script ~
If an `import` statement is used in legacy Vim script, for identifier the
script-local "s:" namespace will be used, even when "s:" is not specified.
==============================================================================
9. Rationale *vim9-rationale*
The :def command ~
Plugin writers have asked for a much faster Vim script. Investigation have
shown that keeping the existing semantics of funtion 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
need to be handled, such as error messages and exceptions. The need to create
a dictionary for a: and l: scopes, the a:000 list and several others add too
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.
Using "def" to define a function comes from Python. Other languages use
"function" which clashes with legacy Vim script.
Type checking ~
When compiling lines of Vim commands into instructions as much as possible
should be done at compile time. Postponing it to runtime makes the execution
slower and means mistakes are found only later. For example, when
encountering the "+" character and compiling this into a generic add
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.
The syntax for types is similar to Java, since it is easy to understand and
widely used. The type names are what was used in Vim before, with some
additions such as "void" and "bool".
JavaScript/TypeScript syntax and semantics ~
Script writers have complained that the Vim script syntax is unexpectedly
different from what they are used to. To reduce this complaint popular
languages will be used as an example. At the same time, we do not want to
abondon 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 result = 44
...
return result || 0 " returns 1
Vim9 script works like JavaScript, keep the value: >
let result = 44
...
return result || 0 " returns 44
On the other hand, overloading "+" to use both for addition and string
concatenation goes against legacy Vim script and often leads to mistakes.
For that reason we will keep using ".." for string concatenation. Lua also
uses ".." this way.
Import and Export ~
A problem of legacy Vim script is that by default all functions and variables
are global. It is possible to make them script-local, but then they are not
available in other scripts.
In Vim9 script a mechanism very similar to the Javascript import and export
mechanism is supported. It is a variant to the existing `:source` command
that works like one would expect:
- Instead of making everything global by default, everything is script-local,
unless exported.
- When importing a script the symbols that are imported are listed, avoiding
name conflicts and failures if later functionality is added.
- The mechanism allows for writing a big, long script with a very clear API:
the exported function(s) and class(es).
- By using relative paths loading can be much faster for an import inside of a
package, no need to search many directories.
- Once an import has been used, it can be cached and loading it again can be
avoided.
- The Vim-specific use of "s:" to make things script-local can be dropped.
Classes ~
Vim supports interfaces to Perl, Python, Lua, Tcl and a few others. But
these have never become widespread. When Vim 9 was designed a decision was
made to phase out these interfaces and concentrate on Vim script, while
encouraging plugin authors to write code in any language and run it as an
external tool, using jobs and channels.
Still, using an external tool has disadvantages. An alternative is to convert
the tool into Vim script. For that to be possible without too much
translation, and keeping the code fast at the same time, the constructs of the
tool need to be supported. Since most languages support classes the lack of
class support in Vim is then a problem.
Previously Vim supported a kind-of object oriented programming by adding
methods to a dictionary. With some care this could be made to work, but it
does not look like real classes. On top of that, it's very slow, because of
the use of dictionaries.
The support of classes in Vim9 script is a "minimal common functionality" of
class support in most languages. It works mostly like Java, which is the most
popular programming language.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -84,8 +84,10 @@ if exists("loaded_matchit")
let b:match_ignorecase = 0
let b:match_words =
\ '\<fu\%[nction]\>:\<retu\%[rn]\>:\<endf\%[unction]\>,' .
\ '\<def\>:\<retu\%[rn]\>:\<enddef\>,' .
\ '\<\(wh\%[ile]\|for\)\>:\<brea\%[k]\>:\<con\%[tinue]\>:\<end\(w\%[hile]\|fo\%[r]\)\>,' .
\ '\<if\>:\<el\%[seif]\>:\<en\%[dif]\>,' .
\ '{:},' .
\ '\<try\>:\<cat\%[ch]\>:\<fina\%[lly]\>:\<endt\%[ry]\>,' .
\ '\<aug\%[roup]\s\+\%(END\>\)\@!\S:\<aug\%[roup]\s\+END\>,'
" Ignore syntax region commands and settings, any 'en*' would clobber

View File

@ -10,7 +10,7 @@ endif
let b:did_indent = 1
setlocal indentexpr=GetVimIndent()
setlocal indentkeys+==end,=else,=cat,=fina,=END,0\\,0=\"\\\
setlocal indentkeys+==end,=},=else,=cat,=fina,=END,0\\,0=\"\\\
let b:undo_indent = "setl indentkeys< indentexpr<"
@ -92,7 +92,7 @@ function GetVimIndentIntern()
else
" A line starting with :au does not increment/decrement indent.
if prev_text !~ '^\s*au\%[tocmd]'
let i = match(prev_text, '\(^\||\)\s*\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\%[lly]\|fu\%[nction]\|el\%[seif]\)\>')
let i = match(prev_text, '\(^\||\)\s*\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\%[lly]\|fu\%[nction]\|def\|el\%[seif]\)\>\)')
if i >= 0
let ind += shiftwidth()
if strpart(prev_text, i, 1) == '|' && has('syntax_items')
@ -115,8 +115,8 @@ function GetVimIndentIntern()
" Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry,
" :endfun, :else and :augroup END.
if cur_text =~ '^\s*\(ene\@!\|cat\|fina\|el\|aug\%[roup]\s\+[eE][nN][dD]\)'
" :endfun, :enddef, :else and :augroup END.
if cur_text =~ '^\s*\(ene\@!\|}\|cat\|fina\|el\|aug\%[roup]\s\+[eE][nN][dD]\)'
let ind = ind - shiftwidth()
endif

View File

@ -26,6 +26,7 @@ syn keyword vimCommand contained abo[veleft] argdo au bel[owright] bp[revious] b
syn keyword vimCommand contained addd arge[dit] bN[ext] bf[irst] br[ewind] bufdo c[hange] caddf[ile] cbel[ow] ce[nter] cgetb[uffer] chi[story] cn[ext] colo[rscheme] cons[t] cs d[elete] deletel delm[arks] diffo[ff] dir doaut ea el[se] endt[ry] exu[sage] fin[d] foldc[lose] g h[elp] hi if intro k lN[ext] laddb[uffer] lb[uffer] lcl[ose] lex[pr] lgete[xpr] lla[st] lnew[er] lockv[ar] ls lvimgrepa[dd] mat[ch] mksp[ell] n[ext] noa nu[mber] ownsyntax ped[it] prev[ious] ps[earch] ptn[ext] py3 python3 qa[ll] redr[aw] retu[rn] rubyd[o] sIe sN[ext] sb[uffer] sbp[revious] sci scs sf[ind] sgi si sim[alt] sm[agic] sno[magic] spe[llgood] spellu[ndo] sre[wind] srp startr[eplace] sunme sy t tabc[lose] tabl[ast] tabp[revious] tcd ter[minal] tlm tlunmenu tno[remap] ts[elect] undoj[oin] uns[ilent] vert[ical] viu[sage] w[rite] windo wqa[ll] xmapc[lear] xprop y[ank]
syn keyword vimCommand contained al[l] argg[lobal] b[uffer] bl[ast] brea[k] buffers ca caf[ter] cbo[ttom] cex[pr] cgete[xpr] cl[ist] cnew[er] com cope[n] cscope debug deletep delp diffp[atch] dj[ump] dp earlier elsei[f] endw[hile] f[ile] fina[lly] foldd[oopen] go[to] ha[rdcopy] hid[e] ij[ump] is[earch] kee[pmarks] lNf[ile] laddf[ile] lbe[fore] lcs lf[ile] lgr[ep] lli[st] lnf[ile] lol[der] lt[ag] lw[indow] menut[ranslate] mkv[imrc] nb[key] noautocmd o[pen] p[rint] perld[o] pro ptN[ext] ptp[revious] py3do pythonx quita[ll] redraws[tatus] rew[ind] rubyf[ile] sIg sa[rgument] sba[ll] sbr[ewind] scl scscope sfir[st] sgl sic sin sm[ap] snoreme spelld[ump] spellw[rong]
syn match vimCommand contained "\<z[-+^.=]\=\>"
syn keyword vimCommand contained def endd[ef] disa[ssemble] vim9[script] imp[ort] exp[ort]
syn keyword vimStdPlugin contained Arguments Break Cfilter Clear Continue DiffOrig Evaluate Finish Gdb Lfilter Man N[ext] Over P[rint] Program Run S Source Step Stop Termdebug TermdebugCommand TOhtml Winbar XMLent XMLns
" vimOptions are caught only when contained in a vimSet {{{2
@ -237,16 +238,17 @@ endif
" =========
syn cluster vimFuncList contains=vimCommand,vimFunctionError,vimFuncKey,Tag,vimFuncSID
syn cluster vimFuncBodyList contains=vimAbb,vimAddress,vimAugroupKey,vimAutoCmd,vimCmplxRepeat,vimComment,vimContinue,vimCtrlChar,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimFBVar,vimFunc,vimFunction,vimFuncVar,vimGlobal,vimHighlight,vimIsCommand,vimLet,vimLetHereDoc,vimLineComment,vimMap,vimMark,vimNorm,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegion,vimRegister,vimSearch,vimSet,vimSpecFile,vimString,vimSubst,vimSynLine,vimUnmap,vimUserCommand
syn match vimFunction "\<fu\%[nction]!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwWtTlL]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)*\ze\s*(" contains=@vimFuncList nextgroup=vimFuncBody
syn match vimFunction "\<\(fu\%[nction]\|def\)!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwWtTlL]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)*\ze\s*(" contains=@vimFuncList nextgroup=vimFuncBody
if exists("g:vimsyn_folding") && g:vimsyn_folding =~# 'f'
syn region vimFuncBody contained fold start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\)" contains=@vimFuncBodyList
syn region vimFuncBody contained fold start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\|enddef\>\)" contains=@vimFuncBodyList
else
syn region vimFuncBody contained start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\)" contains=@vimFuncBodyList
syn region vimFuncBody contained start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\|enddef\>\)" contains=@vimFuncBodyList
endif
syn match vimFuncVar contained "a:\(\K\k*\|\d\+\)"
syn match vimFuncSID contained "\c<sid>\|\<s:"
syn keyword vimFuncKey contained fu[nction]
syn keyword vimFuncKey contained def
syn match vimFuncBlank contained "\s\+"
syn keyword vimPattern contained start skip end

View File

@ -788,6 +788,9 @@ OBJ = \
$(OUTDIR)/usercmd.o \
$(OUTDIR)/userfunc.o \
$(OUTDIR)/version.o \
$(OUTDIR)/vim9compile.o \
$(OUTDIR)/vim9execute.o \
$(OUTDIR)/vim9script.o \
$(OUTDIR)/viminfo.o \
$(OUTDIR)/winclip.o \
$(OUTDIR)/window.o
@ -1153,6 +1156,12 @@ $(OUTDIR)/netbeans.o: netbeans.c $(INCL) version.h
$(OUTDIR)/version.o: version.c $(INCL) version.h
$(OUTDIR)/vim9compile.o: vim9compile.c $(INCL) version.h
$(OUTDIR)/vim9execute.o: vim9execute.c $(INCL) version.h
$(OUTDIR)/vim9script.o: vim9script.c $(INCL) version.h
$(OUTDIR)/viminfo.o: viminfo.c $(INCL) version.h
$(OUTDIR)/gui_dwrite.o: gui_dwrite.cpp gui_dwrite.h

View File

@ -791,6 +791,9 @@ OBJ = \
$(OUTDIR)\undo.obj \
$(OUTDIR)\usercmd.obj \
$(OUTDIR)\userfunc.obj \
$(OUTDIR)\vim9compile.obj \
$(OUTDIR)\vim9execute.obj \
$(OUTDIR)\vim9script.obj \
$(OUTDIR)\viminfo.obj \
$(OUTDIR)\winclip.obj \
$(OUTDIR)\window.obj \
@ -1726,6 +1729,12 @@ $(OUTDIR)/userfunc.obj: $(OUTDIR) userfunc.c $(INCL)
$(OUTDIR)/version.obj: $(OUTDIR) version.c $(INCL) version.h
$(OUTDIR)/vim9compile.obj: $(OUTDIR) vim9compile.c $(INCL)
$(OUTDIR)/vim9execute.obj: $(OUTDIR) vim9execute.c $(INCL)
$(OUTDIR)/vim9script.obj: $(OUTDIR) vim9script.c $(INCL)
$(OUTDIR)/viminfo.obj: $(OUTDIR) viminfo.c $(INCL) version.h
$(OUTDIR)/window.obj: $(OUTDIR) window.c $(INCL)
@ -1907,6 +1916,9 @@ proto.h: \
proto/undo.pro \
proto/usercmd.pro \
proto/userfunc.pro \
proto/vim9compile.pro \
proto/vim9execute.pro \
proto/vim9script.pro \
proto/viminfo.pro \
proto/window.pro \
$(SOUND_PRO) \

View File

@ -1623,6 +1623,7 @@ BASIC_SRC = \
main.c \
map.c \
mark.c \
mbyte.c \
memfile.c \
memline.c \
menu.c \
@ -1631,7 +1632,6 @@ BASIC_SRC = \
misc2.c \
mouse.c \
move.c \
mbyte.c \
normal.c \
ops.c \
option.c \
@ -1645,8 +1645,8 @@ BASIC_SRC = \
quickfix.c \
regexp.c \
register.c \
scriptfile.c \
screen.c \
scriptfile.c \
search.c \
session.c \
sha256.c \
@ -1666,6 +1666,9 @@ BASIC_SRC = \
usercmd.c \
userfunc.c \
version.c \
vim9compile.c \
vim9execute.c \
vim9script.c \
viminfo.c \
window.c \
bufwrite.c \
@ -1761,13 +1764,13 @@ OBJ_COMMON = \
objects/list.o \
objects/map.o \
objects/mark.o \
objects/mbyte.o \
objects/memline.o \
objects/menu.o \
objects/misc1.o \
objects/misc2.o \
objects/mouse.o \
objects/move.o \
objects/mbyte.o \
objects/normal.o \
objects/ops.o \
objects/option.o \
@ -1781,8 +1784,8 @@ OBJ_COMMON = \
objects/quickfix.o \
objects/regexp.o \
objects/register.o \
objects/scriptfile.o \
objects/screen.o \
objects/scriptfile.o \
objects/search.o \
objects/session.o \
objects/sha256.o \
@ -1802,6 +1805,9 @@ OBJ_COMMON = \
objects/usercmd.o \
objects/userfunc.o \
objects/version.o \
objects/vim9compile.o \
objects/vim9execute.o \
objects/vim9script.o \
objects/viminfo.o \
objects/window.o \
objects/bufwrite.o \
@ -1873,9 +1879,12 @@ PRO_AUTO = \
arabic.pro \
arglist.pro \
autocmd.pro \
beval.pro \
blowfish.pro \
buffer.pro \
bufwrite.pro \
change.pro \
channel.pro \
charset.pro \
cindent.pro \
cmdexpand.pro \
@ -1904,6 +1913,7 @@ PRO_AUTO = \
findfile.pro \
fold.pro \
getchar.pro \
gui_beval.pro \
hardcopy.pro \
hashtab.pro \
highlight.pro \
@ -1930,6 +1940,7 @@ PRO_AUTO = \
misc2.pro \
mouse.pro \
move.pro \
netbeans.pro \
normal.pro \
ops.pro \
option.pro \
@ -1943,8 +1954,8 @@ PRO_AUTO = \
quickfix.pro \
regexp.pro \
register.pro \
scriptfile.pro \
screen.pro \
scriptfile.pro \
search.pro \
session.pro \
sha256.pro \
@ -1965,13 +1976,11 @@ PRO_AUTO = \
usercmd.pro \
userfunc.pro \
version.pro \
vim9compile.pro \
vim9execute.pro \
vim9script.pro \
viminfo.pro \
window.pro \
bufwrite.pro \
beval.pro \
gui_beval.pro \
netbeans.pro \
channel.pro \
$(ALL_GUI_PRO) \
$(TCL_PRO)
@ -3079,6 +3088,9 @@ objects/blowfish.o: blowfish.c
objects/buffer.o: buffer.c
$(CCC) -o $@ buffer.c
objects/bufwrite.o: bufwrite.c
$(CCC) -o $@ bufwrite.c
objects/change.o: change.c
$(CCC) -o $@ change.c
@ -3433,15 +3445,21 @@ objects/usercmd.o: usercmd.c
objects/userfunc.o: userfunc.c
$(CCC) -o $@ userfunc.c
objects/vim9compile.o: vim9compile.c
$(CCC) -o $@ vim9compile.c
objects/vim9execute.o: vim9execute.c
$(CCC) -o $@ vim9execute.c
objects/vim9script.o: vim9script.c
$(CCC) -o $@ vim9script.c
objects/viminfo.o: viminfo.c
$(CCC) -o $@ viminfo.c
objects/window.o: window.c
$(CCC) -o $@ window.c
objects/bufwrite.o: bufwrite.c
$(CCC) -o $@ bufwrite.c
objects/netbeans.o: netbeans.c
$(CCC) -o $@ netbeans.c
@ -3787,6 +3805,10 @@ objects/mark.o: mark.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/mbyte.o: mbyte.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/memfile.o: memfile.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
@ -3819,10 +3841,6 @@ objects/move.o: move.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/mbyte.o: mbyte.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/normal.o: normal.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
@ -3875,14 +3893,14 @@ objects/register.o: register.c vim.h protodef.h auto/config.h feature.h os_unix.
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/scriptfile.o: scriptfile.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/screen.o: screen.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/scriptfile.o: scriptfile.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
objects/search.o: search.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
@ -3961,6 +3979,18 @@ objects/version.o: version.c vim.h protodef.h auto/config.h feature.h os_unix.h
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h version.h
objects/vim9compile.o: vim9compile.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h vim9.h
objects/vim9execute.o: vim9execute.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h vim9.h
objects/vim9script.o: vim9script.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h vim9.h
objects/viminfo.o: viminfo.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \

View File

@ -58,24 +58,24 @@ rettv_blob_set(typval_T *rettv, blob_T *b)
}
int
blob_copy(typval_T *from, typval_T *to)
blob_copy(blob_T *from, typval_T *to)
{
int ret = OK;
to->v_type = VAR_BLOB;
to->v_lock = 0;
if (from->vval.v_blob == NULL)
if (from == NULL)
to->vval.v_blob = NULL;
else if (rettv_blob_alloc(to) == FAIL)
ret = FAIL;
else
{
int len = from->vval.v_blob->bv_ga.ga_len;
int len = from->bv_ga.ga_len;
if (len > 0)
{
to->vval.v_blob->bv_ga.ga_data =
vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
vim_memsave(from->bv_ga.ga_data, len);
if (to->vval.v_blob->bv_ga.ga_data == NULL)
len = 0;
}

View File

@ -2263,7 +2263,10 @@ channel_get_json(
while (item != NULL)
{
list_T *l = item->jq_value->vval.v_list;
typval_T *tv = &l->lv_first->li_tv;
typval_T *tv;
range_list_materialize(l);
tv = &l->lv_first->li_tv;
if ((without_callback || !item->jq_no_callback)
&& ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)

View File

@ -826,7 +826,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
if (**arg != ':')
{
semsg(_("E720: Missing colon in Dictionary: %s"), *arg);
semsg(_(e_missing_dict_colon), *arg);
clear_tv(&tvkey);
goto failret;
}
@ -853,7 +853,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
item = dict_find(d, key, -1);
if (item != NULL)
{
semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key);
semsg(_(e_duplicate_key), key);
clear_tv(&tvkey);
clear_tv(&tv);
goto failret;
@ -873,7 +873,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
break;
if (**arg != ',')
{
semsg(_("E722: Missing comma in Dictionary: %s"), *arg);
semsg(_(e_missing_dict_comma), *arg);
goto failret;
}
*arg = skipwhite(*arg + 1);
@ -881,7 +881,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
if (**arg != '}')
{
semsg(_("E723: Missing end of Dictionary '}': %s"), *arg);
semsg(_(e_missing_dict_end), *arg);
failret:
if (d != NULL)
dict_free(d);

View File

@ -20,12 +20,10 @@
# include <float.h>
#endif
static char *e_missbrac = N_("E111: Missing ']'");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
#ifdef FEAT_FLOAT
static char *e_float_as_string = N_("E806: using Float as a String");
#endif
static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis");
#define NAMESPACE_CHAR (char_u *)"abglstvw"
@ -60,10 +58,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string);
static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp);
static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
static int free_unref_items(int copyID);
static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end);
static int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
@ -222,6 +217,11 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate)
return ret;
}
/*
* Evaluate an expression, which can be a function, partial or string.
* Pass arguments "argv[argc]".
* Return the result in "rettv" and OK or FAIL.
*/
int
eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
{
@ -243,6 +243,13 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
{
partial_T *partial = expr->vval.v_partial;
if (partial->pt_func != NULL && partial->pt_func->uf_dfunc_idx >= 0)
{
if (call_def_function(partial->pt_func, argc, argv, rettv) == FAIL)
return FAIL;
}
else
{
s = partial_name(partial);
if (s == NULL || *s == NUL)
return FAIL;
@ -252,6 +259,7 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
return FAIL;
}
}
else
{
s = tv_get_string_buf_chk(expr, buf);
@ -652,6 +660,7 @@ get_lval(
// Find the end of the name.
p = find_name_end(name, &expr_start, &expr_end, fne_flags);
lp->ll_name_end = p;
if (expr_start != NULL)
{
// Don't expand the name when we already know there is an error.
@ -678,8 +687,20 @@ get_lval(
lp->ll_name = lp->ll_exp_name;
}
else
{
lp->ll_name = name;
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9 && *p == ':')
{
scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
char_u *tp = skipwhite(p + 1);
// parse the type after the name
lp->ll_type = parse_type(&tp, &si->sn_type_list);
lp->ll_name_end = tp;
}
}
// Without [idx] or .key we are done.
if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
return p;
@ -1002,6 +1023,7 @@ get_lval(
}
clear_tv(&var1);
lp->ll_name_end = p;
return p;
}
@ -1027,7 +1049,7 @@ set_var_lval(
char_u *endp,
typval_T *rettv,
int copy,
int is_const, // Disallow to modify existing variable for :const
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
char_u *op)
{
int cc;
@ -1093,7 +1115,7 @@ set_var_lval(
{
typval_T tv;
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_(e_cannot_mod));
*endp = cc;
@ -1114,7 +1136,7 @@ set_var_lval(
}
}
else
set_var_const(lp->ll_name, rettv, copy, is_const);
set_var_const(lp->ll_name, lp->ll_type, rettv, copy, flags);
*endp = cc;
}
else if (var_check_lock(lp->ll_newkey == NULL
@ -1126,7 +1148,7 @@ set_var_lval(
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock a range"));
return;
@ -1185,7 +1207,7 @@ set_var_lval(
/*
* Assign to a List or Dictionary item.
*/
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock a list or dict"));
return;
@ -1250,6 +1272,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
switch (tv1->v_type)
{
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_DICT:
case VAR_FUNC:
case VAR_PARTIAL:
@ -1392,14 +1415,14 @@ eval_for_line(
if (fi == NULL)
return NULL;
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
expr = skip_var_list(arg, TRUE, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL)
return fi;
expr = skipwhite(expr);
if (expr[0] != 'i' || expr[1] != 'n' || !VIM_ISWHITE(expr[2]))
{
emsg(_("E690: Missing \"in\" after :for"));
emsg(_(e_missing_in));
return fi;
}
@ -1420,6 +1443,9 @@ eval_for_line(
}
else
{
// Need a real list here.
range_list_materialize(l);
// No need to increment the refcount, it's already set for
// the list being used in "tv".
fi->fi_list = l;
@ -1436,7 +1462,7 @@ eval_for_line(
// Make a copy, so that the iteration still works when the
// blob is changed.
blob_copy(&tv, &btv);
blob_copy(tv.vval.v_blob, &btv);
fi->fi_blob = btv.vval.v_blob;
}
clear_tv(&tv);
@ -1478,7 +1504,7 @@ next_for_item(void *fi_void, char_u *arg)
tv.vval.v_number = blob_get(fi->fi_blob, fi->fi_bi);
++fi->fi_bi;
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, FALSE, NULL) == OK;
fi->fi_varcount, 0, NULL) == OK;
}
item = fi->fi_lw.lw_item;
@ -1488,7 +1514,7 @@ next_for_item(void *fi_void, char_u *arg)
{
fi->fi_lw.lw_item = item->li_next;
result = (ex_let_vars(arg, &item->li_tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, FALSE, NULL) == OK);
fi->fi_varcount, 0, NULL) == OK);
}
return result;
}
@ -1814,7 +1840,7 @@ eval1(char_u **arg, typval_T *rettv, int evaluate)
*/
if ((*arg)[0] != ':')
{
emsg(_("E109: Missing ':' after '?'"));
emsg(_(e_missing_colon));
if (evaluate && result)
clear_tv(rettv);
return FAIL;
@ -2089,6 +2115,43 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
void
eval_addblob(typval_T *tv1, typval_T *tv2)
{
blob_T *b1 = tv1->vval.v_blob;
blob_T *b2 = tv2->vval.v_blob;
blob_T *b = blob_alloc();
int i;
if (b != NULL)
{
for (i = 0; i < blob_len(b1); i++)
ga_append(&b->bv_ga, blob_get(b1, i));
for (i = 0; i < blob_len(b2); i++)
ga_append(&b->bv_ga, blob_get(b2, i));
clear_tv(tv1);
rettv_blob_set(tv1, b);
}
}
int
eval_addlist(typval_T *tv1, typval_T *tv2)
{
typval_T var3;
// concatenate Lists
if (list_concat(tv1->vval.v_list, tv2->vval.v_list, &var3) == FAIL)
{
clear_tv(tv1);
clear_tv(tv2);
return FAIL;
}
clear_tv(tv1);
*tv1 = var3;
return OK;
}
/*
* Handle fourth level expression:
* + number addition
@ -2105,7 +2168,6 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
eval5(char_u **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
typval_T var3;
int op;
varnumber_T n1, n2;
#ifdef FEAT_FLOAT
@ -2189,37 +2251,13 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
}
else if (op == '+' && rettv->v_type == VAR_BLOB
&& var2.v_type == VAR_BLOB)
{
blob_T *b1 = rettv->vval.v_blob;
blob_T *b2 = var2.vval.v_blob;
blob_T *b = blob_alloc();
int i;
if (b != NULL)
{
for (i = 0; i < blob_len(b1); i++)
ga_append(&b->bv_ga, blob_get(b1, i));
for (i = 0; i < blob_len(b2); i++)
ga_append(&b->bv_ga, blob_get(b2, i));
clear_tv(rettv);
rettv_blob_set(rettv, b);
}
}
eval_addblob(rettv, &var2);
else if (op == '+' && rettv->v_type == VAR_LIST
&& var2.v_type == VAR_LIST)
{
// concatenate Lists
if (list_concat(rettv->vval.v_list, var2.vval.v_list,
&var3) == FAIL)
{
clear_tv(rettv);
clear_tv(&var2);
if (eval_addlist(rettv, &var2) == FAIL)
return FAIL;
}
clear_tv(rettv);
*rettv = var3;
}
else
{
int error = FALSE;
@ -2424,7 +2462,7 @@ eval6(
}
else
{
emsg(_("E804: Cannot use '%' with Float"));
emsg(_(e_modulus));
return FAIL;
}
rettv->v_type = VAR_FLOAT;
@ -2462,6 +2500,7 @@ eval6(
* $VAR environment variable
* (expression) nested expression
* [expr, expr] List
* {arg, arg -> expr} Lambda
* {key: val, key: val} Dictionary
* #{key: val, key: val} Dictionary with literal keys
*
@ -2483,9 +2522,8 @@ eval7(
char_u **arg,
typval_T *rettv,
int evaluate,
int want_string UNUSED) // after "." operator
int want_string) // after "." operator
{
varnumber_T n;
int len;
char_u *s;
char_u *start_leader, *end_leader;
@ -2532,105 +2570,8 @@ eval7(
case '7':
case '8':
case '9':
case '.':
{
#ifdef FEAT_FLOAT
char_u *p;
int get_float = FALSE;
// We accept a float when the format matches
// "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
// strict to avoid backwards compatibility problems.
// With script version 2 and later the leading digit can be
// omitted.
// Don't look for a float after the "." operator, so that
// ":let vers = 1.2.3" doesn't fail.
if (**arg == '.')
p = *arg;
else
p = skipdigits(*arg + 1);
if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
{
get_float = TRUE;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E')
{
++p;
if (*p == '-' || *p == '+')
++p;
if (!vim_isdigit(*p))
get_float = FALSE;
else
p = skipdigits(p + 1);
}
if (ASCII_ISALPHA(*p) || *p == '.')
get_float = FALSE;
}
if (get_float)
{
float_T f;
*arg += string2float(*arg, &f);
if (evaluate)
{
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f;
}
}
else
#endif
if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
{
char_u *bp;
blob_T *blob = NULL; // init for gcc
// Blob constant: 0z0123456789abcdef
if (evaluate)
blob = blob_alloc();
for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
{
if (!vim_isxdigit(bp[1]))
{
if (blob != NULL)
{
emsg(_("E973: Blob literal should have an even number of hex characters"));
ga_clear(&blob->bv_ga);
VIM_CLEAR(blob);
}
ret = FAIL;
case '.': ret = get_number_tv(arg, rettv, evaluate, want_string);
break;
}
if (blob != NULL)
ga_append(&blob->bv_ga,
(hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
if (bp[2] == '.' && vim_isxdigit(bp[3]))
++bp;
}
if (blob != NULL)
rettv_blob_set(rettv, blob);
*arg = bp;
}
else
{
// decimal, hex or octal number
vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
? STR2NR_NO_OCT + STR2NR_QUOTE
: STR2NR_ALL, &n, NULL, 0, TRUE);
if (len == 0)
{
semsg(_(e_invexpr2), *arg);
ret = FAIL;
break;
}
*arg += len;
if (evaluate)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n;
}
}
break;
}
/*
* String constant: "string".
@ -2647,7 +2588,7 @@ eval7(
/*
* List: [expr, expr]
*/
case '[': ret = get_list_tv(arg, rettv, evaluate);
case '[': ret = get_list_tv(arg, rettv, evaluate, TRUE);
break;
/*
@ -2706,7 +2647,7 @@ eval7(
++*arg;
else if (ret == OK)
{
emsg(_("E110: Missing ')'"));
emsg(_(e_missing_close));
clear_tv(rettv);
ret = FAIL;
}
@ -2907,7 +2848,7 @@ eval_lambda(
if (*skipwhite(*arg) == '(')
semsg(_(e_nowhitespace));
else
semsg(_(e_missingparen), "lambda");
semsg(_(e_missing_paren), "lambda");
}
clear_tv(rettv);
ret = FAIL;
@ -2961,7 +2902,7 @@ eval_method(
if (**arg != '(')
{
if (verbose)
semsg(_(e_missingparen), name);
semsg(_(e_missing_paren), name);
ret = FAIL;
}
else if (VIM_ISWHITE((*arg)[-1]))
@ -3024,6 +2965,7 @@ eval_index(
emsg(_("E909: Cannot index a special variable"));
return FAIL;
case VAR_UNKNOWN:
case VAR_VOID:
if (evaluate)
return FAIL;
// FALLTHROUGH
@ -3129,6 +3071,7 @@ eval_index(
switch (rettv->v_type)
{
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_FLOAT:
@ -3377,7 +3320,7 @@ get_option_tv(
if (opt_type == -3) // invalid name
{
if (rettv != NULL)
semsg(_("E113: Unknown option: %s"), *arg);
semsg(_(e_unknown_option), *arg);
ret = FAIL;
}
else if (rettv != NULL)
@ -3412,11 +3355,121 @@ get_option_tv(
return ret;
}
/*
* Allocate a variable for a number constant. Also deals with "0z" for blob.
* Return OK or FAIL.
*/
int
get_number_tv(
char_u **arg,
typval_T *rettv,
int evaluate,
int want_string UNUSED)
{
int len;
#ifdef FEAT_FLOAT
char_u *p;
int get_float = FALSE;
// We accept a float when the format matches
// "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
// strict to avoid backwards compatibility problems.
// With script version 2 and later the leading digit can be
// omitted.
// Don't look for a float after the "." operator, so that
// ":let vers = 1.2.3" doesn't fail.
if (**arg == '.')
p = *arg;
else
p = skipdigits(*arg + 1);
if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
{
get_float = TRUE;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E')
{
++p;
if (*p == '-' || *p == '+')
++p;
if (!vim_isdigit(*p))
get_float = FALSE;
else
p = skipdigits(p + 1);
}
if (ASCII_ISALPHA(*p) || *p == '.')
get_float = FALSE;
}
if (get_float)
{
float_T f;
*arg += string2float(*arg, &f);
if (evaluate)
{
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f;
}
}
else
#endif
if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
{
char_u *bp;
blob_T *blob = NULL; // init for gcc
// Blob constant: 0z0123456789abcdef
if (evaluate)
blob = blob_alloc();
for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
{
if (!vim_isxdigit(bp[1]))
{
if (blob != NULL)
{
emsg(_("E973: Blob literal should have an even number of hex characters"));
ga_clear(&blob->bv_ga);
VIM_CLEAR(blob);
}
return FAIL;
}
if (blob != NULL)
ga_append(&blob->bv_ga,
(hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
if (bp[2] == '.' && vim_isxdigit(bp[3]))
++bp;
}
if (blob != NULL)
rettv_blob_set(rettv, blob);
*arg = bp;
}
else
{
varnumber_T n;
// decimal, hex or octal number
vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
? STR2NR_NO_OCT + STR2NR_QUOTE
: STR2NR_ALL, &n, NULL, 0, TRUE);
if (len == 0)
{
semsg(_(e_invexpr2), *arg);
return FAIL;
}
*arg += len;
if (evaluate)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n;
}
}
return OK;
}
/*
* Allocate a variable for a string constant.
* Return OK or FAIL.
*/
static int
int
get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
@ -3553,7 +3606,7 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
* Allocate a variable for a 'str''ing' constant.
* Return OK or FAIL.
*/
static int
int
get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
@ -3772,6 +3825,8 @@ tv_equal(
return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
case VAR_NUMBER:
case VAR_BOOL:
case VAR_SPECIAL:
return tv1->vval.v_number == tv2->vval.v_number;
case VAR_STRING:
@ -3779,10 +3834,6 @@ tv_equal(
s2 = tv_get_string_buf(tv2, buf2);
return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0);
case VAR_BOOL:
case VAR_SPECIAL:
return tv1->vval.v_number == tv2->vval.v_number;
case VAR_FLOAT:
#ifdef FEAT_FLOAT
return tv1->vval.v_float == tv2->vval.v_float;
@ -3795,9 +3846,11 @@ tv_equal(
#ifdef FEAT_JOB_CHANNEL
return tv1->vval.v_channel == tv2->vval.v_channel;
#endif
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_UNKNOWN:
case VAR_VOID:
break;
}
@ -4511,6 +4564,7 @@ echo_string_core(
case VAR_NUMBER:
case VAR_UNKNOWN:
case VAR_VOID:
*tofree = NULL;
r = tv_get_string_buf(tv, numbuf);
break;
@ -4668,7 +4722,7 @@ string2float(
* If the environment variable was not set, silently assume it is empty.
* Return FAIL if the name is invalid.
*/
static int
int
get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *string = NULL;
@ -5363,6 +5417,7 @@ free_tv(typval_T *varp)
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_BOOL:
case VAR_SPECIAL:
break;
@ -5425,6 +5480,7 @@ clear_tv(typval_T *varp)
varp->vval.v_channel = NULL;
#endif
case VAR_UNKNOWN:
case VAR_VOID:
break;
}
varp->v_lock = 0;
@ -5503,6 +5559,7 @@ tv_get_number_chk(typval_T *varp, int *denote)
emsg(_("E974: Using a Blob as a Number"));
break;
case VAR_UNKNOWN:
case VAR_VOID:
internal_error("tv_get_number(UNKNOWN)");
break;
}
@ -5556,6 +5613,7 @@ tv_get_float(typval_T *varp)
emsg(_("E975: Using a Blob as a Float"));
break;
case VAR_UNKNOWN:
case VAR_VOID:
internal_error("tv_get_float(UNKNOWN)");
break;
}
@ -5678,6 +5736,7 @@ tv_get_string_buf_chk(typval_T *varp, char_u *buf)
#endif
break;
case VAR_UNKNOWN:
case VAR_VOID:
emsg(_(e_inval_string));
break;
}
@ -5826,6 +5885,7 @@ copy_tv(typval_T *from, typval_T *to)
}
break;
case VAR_UNKNOWN:
case VAR_VOID:
internal_error("copy_tv(UNKNOWN)");
break;
}
@ -5885,7 +5945,7 @@ item_copy(
ret = FAIL;
break;
case VAR_BLOB:
ret = blob_copy(from, to);
ret = blob_copy(from->vval.v_blob, to);
break;
case VAR_DICT:
to->v_type = VAR_DICT;
@ -5904,6 +5964,7 @@ item_copy(
ret = FAIL;
break;
case VAR_UNKNOWN:
case VAR_VOID:
internal_error("item_copy(UNKNOWN)");
ret = FAIL;
}
@ -5911,6 +5972,59 @@ item_copy(
return ret;
}
void
echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr)
{
char_u *tofree;
char_u numbuf[NUMBUFLEN];
char_u *p = echo_string(rettv, &tofree, numbuf, get_copyID());
if (*atstart)
{
*atstart = FALSE;
// Call msg_start() after eval1(), evaluating the expression
// may cause a message to appear.
if (with_space)
{
// Mark the saved text as finishing the line, so that what
// follows is displayed on a new line when scrolling back
// at the more prompt.
msg_sb_eol();
msg_start();
}
}
else if (with_space)
msg_puts_attr(" ", echo_attr);
if (p != NULL)
for ( ; *p != NUL && !got_int; ++p)
{
if (*p == '\n' || *p == '\r' || *p == TAB)
{
if (*p != TAB && *needclr)
{
// remove any text still there from the command
msg_clr_eos();
*needclr = FALSE;
}
msg_putchar_attr(*p, echo_attr);
}
else
{
if (has_mbyte)
{
int i = (*mb_ptr2len)(p);
(void)msg_outtrans_len_attr(p, i, echo_attr);
p += i - 1;
}
else
(void)msg_outtrans_len_attr(p, 1, echo_attr);
}
}
vim_free(tofree);
}
/*
* ":echo expr1 ..." print each argument separated with a space, add a
* newline at the end.
@ -5921,11 +6035,9 @@ ex_echo(exarg_T *eap)
{
char_u *arg = eap->arg;
typval_T rettv;
char_u *tofree;
char_u *p;
int needclr = TRUE;
int atstart = TRUE;
char_u numbuf[NUMBUFLEN];
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
@ -5954,52 +6066,8 @@ ex_echo(exarg_T *eap)
need_clr_eos = FALSE;
if (!eap->skip)
{
if (atstart)
{
atstart = FALSE;
// Call msg_start() after eval1(), evaluating the expression
// may cause a message to appear.
if (eap->cmdidx == CMD_echo)
{
// Mark the saved text as finishing the line, so that what
// follows is displayed on a new line when scrolling back
// at the more prompt.
msg_sb_eol();
msg_start();
}
}
else if (eap->cmdidx == CMD_echo)
msg_puts_attr(" ", echo_attr);
p = echo_string(&rettv, &tofree, numbuf, get_copyID());
if (p != NULL)
for ( ; *p != NUL && !got_int; ++p)
{
if (*p == '\n' || *p == '\r' || *p == TAB)
{
if (*p != TAB && needclr)
{
// remove any text still there from the command
msg_clr_eos();
needclr = FALSE;
}
msg_putchar_attr(*p, echo_attr);
}
else
{
if (has_mbyte)
{
int i = (*mb_ptr2len)(p);
echo_one(&rettv, eap->cmdidx == CMD_echo, &atstart, &needclr);
(void)msg_outtrans_len_attr(p, i, echo_attr);
p += i - 1;
}
else
(void)msg_outtrans_len_attr(p, 1, echo_attr);
}
}
vim_free(tofree);
}
clear_tv(&rettv);
arg = skipwhite(arg);
}
@ -6369,7 +6437,7 @@ typval_compare(
case EXPR_SEQUAL: n1 = (f1 <= f2); break;
case EXPR_UNKNOWN:
case EXPR_MATCH:
case EXPR_NOMATCH: break; // avoid gcc warning
default: break; // avoid gcc warning
}
}
#endif
@ -6395,7 +6463,7 @@ typval_compare(
case EXPR_SEQUAL: n1 = (n1 <= n2); break;
case EXPR_UNKNOWN:
case EXPR_MATCH:
case EXPR_NOMATCH: break; // avoid gcc warning
default: break; // avoid gcc warning
}
}
else
@ -6425,7 +6493,7 @@ typval_compare(
n1 = !n1;
break;
case EXPR_UNKNOWN: break; // avoid gcc warning
default: break; // avoid gcc warning
}
}
clear_tv(typ1);

View File

@ -176,6 +176,7 @@ set_buffer_lines(
if (lines->v_type == VAR_LIST)
{
l = lines->vval.v_list;
range_list_materialize(l);
li = l->lv_first;
}
else
@ -689,10 +690,16 @@ get_buffer_lines(
{
char_u *p;
if (retlist)
{
if (rettv_list_alloc(rettv) == FAIL)
return;
}
else
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (retlist && rettv_list_alloc(rettv) == FAIL)
return;
}
if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
return;

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,6 @@
#if defined(FEAT_EVAL) || defined(PROTO)
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static dictitem_T globvars_var; // variable used for g:
static dict_T globvardict; // Dictionary with g: variables
#define globvarht globvardict.dv_hashtab
@ -41,6 +39,8 @@ static hashtab_T compat_hashtab;
#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}
typedef struct vimvar vimvar_T;
static struct vimvar
{
char *vv_name; // name of variable, without v:
@ -163,17 +163,14 @@ static dict_T vimvardict; // Dictionary with v: variables
// for VIM_VERSION_ defines
#include "version.h"
#define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars)
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
static void ex_let_const(exarg_T *eap, int is_const);
static char_u *skip_var_one(char_u *arg);
static char_u *skip_var_one(char_u *arg, int include_type);
static void list_glob_vars(int *first);
static void list_buf_vars(int *first);
static void list_win_vars(int *first);
static void list_tab_vars(int *first);
static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int is_const, char_u *endchars, char_u *op);
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep);
static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit);
static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock);
@ -544,7 +541,7 @@ list_script_vars(int *first)
* indentation in the 'cmd' line) is stripped.
* Returns a List with {lines} or NULL.
*/
static list_T *
list_T *
heredoc_get(exarg_T *eap, char_u *cmd)
{
char_u *theline;
@ -669,6 +666,7 @@ heredoc_get(exarg_T *eap, char_u *cmd)
* ":let var .= expr" assignment command.
* ":let var ..= expr" assignment command.
* ":let [var1, var2] = expr" unpack list.
* ":let var =<< ..." heredoc
*/
void
ex_let(exarg_T *eap)
@ -701,8 +699,13 @@ ex_let_const(exarg_T *eap, int is_const)
char_u *argend;
int first = TRUE;
int concat;
int flags = is_const ? LET_IS_CONST : 0;
argend = skip_var_list(arg, &var_count, &semicolon);
// detect Vim9 assignment without ":let" or ":const"
if (eap->arg == eap->cmd)
flags |= LET_NO_COMMAND;
argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
if (argend == NULL)
return;
if (argend > arg && argend[-1] == '.') // for var.='str'
@ -749,7 +752,7 @@ ex_let_const(exarg_T *eap, int is_const)
op[0] = '=';
op[1] = NUL;
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
is_const, op);
flags, op);
}
clear_tv(&rettv);
}
@ -783,7 +786,7 @@ ex_let_const(exarg_T *eap, int is_const)
else if (i != FAIL)
{
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
is_const, op);
flags, op);
clear_tv(&rettv);
}
}
@ -804,7 +807,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 is_const, // lock variables for const
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
char_u *op)
{
char_u *arg = arg_start;
@ -816,7 +819,7 @@ ex_let_vars(
if (*arg != '[')
{
// ":let var = expr" or ":for var in list"
if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL)
if (ex_let_one(arg, tv, copy, flags, op, op) == NULL)
return FAIL;
return OK;
}
@ -844,8 +847,7 @@ ex_let_vars(
while (*arg != ']')
{
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, &item->li_tv, TRUE, is_const,
(char_u *)",;]", op);
arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]", op);
item = item->li_next;
if (arg == NULL)
return FAIL;
@ -869,7 +871,7 @@ ex_let_vars(
ltv.vval.v_list = l;
l->lv_refcount = 1;
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, is_const,
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, flags,
(char_u *)"]", op);
clear_tv(&ltv);
if (arg == NULL)
@ -896,6 +898,7 @@ ex_let_vars(
char_u *
skip_var_list(
char_u *arg,
int include_type,
int *var_count,
int *semicolon)
{
@ -908,7 +911,7 @@ skip_var_list(
for (;;)
{
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
s = skip_var_one(p);
s = skip_var_one(p, TRUE);
if (s == p)
{
semsg(_(e_invarg2), p);
@ -937,20 +940,29 @@ skip_var_list(
return p + 1;
}
else
return skip_var_one(arg);
return skip_var_one(arg, include_type);
}
/*
* Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
* l[idx].
* In Vim9 script also skip over ": type" if "include_type" is TRUE.
*/
static char_u *
skip_var_one(char_u *arg)
skip_var_one(char_u *arg, int include_type)
{
char_u *end;
if (*arg == '@' && arg[1] != NUL)
return arg + 2;
return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
end = find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
if (include_type && current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& *end == ':')
{
end = skip_type(skipwhite(end + 1));
}
return end;
}
/*
@ -1141,7 +1153,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 is_const, // lock variable for const
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
char_u *endchars, // valid chars after variable name or NULL
char_u *op) // "+", "-", "." or NULL
{
@ -1156,7 +1168,7 @@ ex_let_one(
// ":let $VAR = expr": Set environment variable.
if (*arg == '$')
{
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock an environment variable"));
return NULL;
@ -1214,9 +1226,9 @@ ex_let_one(
// ":let &g:option = expr": Set global option value.
else if (*arg == '&')
{
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock an option"));
emsg(_(e_const_option));
return NULL;
}
// Find the end of the name.
@ -1281,7 +1293,7 @@ ex_let_one(
// ":let @r = expr": Set register contents.
else if (*arg == '@')
{
if (is_const)
if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock a register"));
return NULL;
@ -1317,6 +1329,7 @@ ex_let_one(
}
// ":let var = expr": Set internal variable.
// ":let var: type = expr": Set internal variable with type.
// ":let {expr} = expr": Idem, name made with curly braces
else if (eval_isnamec1(*arg) || *arg == '{')
{
@ -1325,11 +1338,12 @@ ex_let_one(
p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL)
{
if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
if (endchars != NULL && vim_strchr(endchars,
*skipwhite(lv.ll_name_end)) == NULL)
emsg(_(e_letunexp));
else
{
set_var_lval(&lv, p, tv, copy, is_const, op);
set_var_lval(&lv, p, tv, copy, flags, op);
arg_end = p;
}
}
@ -1657,12 +1671,13 @@ item_lock(typval_T *tv, int deep, int lock)
switch (tv->v_type)
{
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_NUMBER:
case VAR_BOOL:
case VAR_STRING:
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_FLOAT:
case VAR_BOOL:
case VAR_SPECIAL:
case VAR_JOB:
case VAR_CHANNEL:
@ -1900,6 +1915,22 @@ get_vimvar_dict(void)
return &vimvardict;
}
/*
* Returns the index of a v:variable. Negative if not found.
*/
int
find_vim_var(char_u *name)
{
dictitem_T *di = find_var_in_ht(&vimvarht, 0, name, TRUE);
struct vimvar *vv;
if (di == NULL)
return -1;
vv = (struct vimvar *)((char *)di - offsetof(vimvar_T, vv_di));
return (int)(vv - vimvars);
}
/*
* Set type of v: variable to "type".
*/
@ -1919,6 +1950,12 @@ set_vim_var_nr(int idx, varnumber_T val)
vimvars[idx].vv_nr = val;
}
char *
get_vim_var_name(int idx)
{
return vimvars[idx].vv_name;
}
/*
* Get typval_T v: variable value.
*/
@ -2245,6 +2282,20 @@ get_var_tv(
*dip = v;
}
if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
imported_T *import = find_imported(name, NULL);
// imported variable from another script
if (import != NULL)
{
scriptitem_T *si = &SCRIPT_ITEM(import->imp_sid);
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ import->imp_var_vals_idx;
tv = sv->sv_tv;
}
}
if (tv == NULL)
{
if (rettv != NULL && verbose)
@ -2365,6 +2416,58 @@ find_var_in_ht(
return HI2DI(hi);
}
/*
* Get the script-local hashtab. NULL if not in a script context.
*/
hashtab_T *
get_script_local_ht(void)
{
scid_T sid = current_sctx.sc_sid;
if (sid > 0 && sid <= script_items.ga_len)
return &SCRIPT_VARS(sid);
return NULL;
}
/*
* Look for "name[len]" in script-local variables.
* Return -1 when not found.
*/
int
lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
{
hashtab_T *ht = get_script_local_ht();
char_u buffer[30];
char_u *p;
int res;
hashitem_T *hi;
if (ht == NULL)
return -1;
if (len < sizeof(buffer) - 1)
{
vim_strncpy(buffer, name, len);
p = buffer;
}
else
{
p = vim_strnsave(name, (int)len);
if (p == NULL)
return -1;
}
hi = hash_find(ht, p);
res = HASHITEM_EMPTY(hi) ? -1 : 1;
// if not script-local, then perhaps imported
if (res == -1 && find_imported(p, NULL) != NULL)
res = 1;
if (p != buffer)
vim_free(p);
return res;
}
/*
* Find the hashtab used for a variable name.
* Return NULL if the name is not valid.
@ -2395,9 +2498,18 @@ find_var_ht(char_u *name, char_u **varname)
}
ht = get_funccal_local_ht();
if (ht == NULL)
return &globvarht; // global variable
if (ht != NULL)
return ht; // local variable
// in Vim9 script items at the script level are script-local
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
ht = get_script_local_ht();
if (ht != NULL)
return ht;
}
return &globvarht; // global variable
}
*varname = name + 2;
if (*name == 'g') // global variable
@ -2414,14 +2526,19 @@ find_var_ht(char_u *name, char_u **varname)
return &curtab->tp_vars->dv_hashtab;
if (*name == 'v') // v: variable
return &vimvarht;
if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
{
if (*name == 'a') // a: function argument
return get_funccal_args_ht();
if (*name == 'l') // l: local function variable
return get_funccal_local_ht();
if (*name == 's' // script variable
&& current_sctx.sc_sid > 0
&& current_sctx.sc_sid <= script_items.ga_len)
return &SCRIPT_VARS(current_sctx.sc_sid);
}
if (*name == 's') // script variable
{
ht = get_script_local_ht();
if (ht != NULL)
return ht;
}
return NULL;
}
@ -2617,7 +2734,7 @@ set_var(
typval_T *tv,
int copy) // make copy of value in "tv"
{
set_var_const(name, tv, copy, FALSE);
set_var_const(name, NULL, tv, copy, 0);
}
/*
@ -2628,13 +2745,15 @@ set_var(
void
set_var_const(
char_u *name,
type_T *type,
typval_T *tv,
int copy, // make copy of value in "tv"
int is_const) // disallow to modify existing variable
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
{
dictitem_T *v;
dictitem_T *di;
char_u *varname;
hashtab_T *ht;
int is_script_local;
ht = find_var_ht(name, &varname);
if (ht == NULL || *varname == NUL)
@ -2642,75 +2761,92 @@ set_var_const(
semsg(_(e_illvar), name);
return;
}
v = find_var_in_ht(ht, 0, varname, TRUE);
is_script_local = ht == get_script_local_ht();
di = find_var_in_ht(ht, 0, varname, TRUE);
// Search in parent scope which is possible to reference from lambda
if (v == NULL)
v = find_var_in_scoped_ht(name, TRUE);
if (di == NULL)
di = find_var_in_scoped_ht(name, TRUE);
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
&& var_check_func_name(name, v == NULL))
&& var_check_func_name(name, di == NULL))
return;
if (v != NULL)
if (di != NULL)
{
if (is_const)
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
{
if (flags & LET_IS_CONST)
{
emsg(_(e_cannot_mod));
return;
}
// existing variable, need to clear the value
if (var_check_ro(v->di_flags, name, FALSE)
|| var_check_lock(v->di_tv.v_lock, name, FALSE))
if (var_check_ro(di->di_flags, name, FALSE)
|| var_check_lock(di->di_tv.v_lock, name, FALSE))
return;
// Handle setting internal v: variables separately where needed to
if ((flags & LET_NO_COMMAND) == 0
&& is_script_local
&& current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
semsg(_("E1041: Redefining script item %s"), name);
return;
}
}
else
// can only redefine once
di->di_flags &= ~DI_FLAGS_RELOAD;
// existing variable, need to clear the value
// Handle setting internal di: variables separately where needed to
// prevent changing the type.
if (ht == &vimvarht)
{
if (v->di_tv.v_type == VAR_STRING)
if (di->di_tv.v_type == VAR_STRING)
{
VIM_CLEAR(v->di_tv.vval.v_string);
VIM_CLEAR(di->di_tv.vval.v_string);
if (copy || tv->v_type != VAR_STRING)
{
char_u *val = tv_get_string(tv);
// Careful: when assigning to v:errmsg and tv_get_string()
// causes an error message the variable will alrady be set.
if (v->di_tv.vval.v_string == NULL)
v->di_tv.vval.v_string = vim_strsave(val);
if (di->di_tv.vval.v_string == NULL)
di->di_tv.vval.v_string = vim_strsave(val);
}
else
{
// Take over the string to avoid an extra alloc/free.
v->di_tv.vval.v_string = tv->vval.v_string;
di->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
return;
}
else if (v->di_tv.v_type == VAR_NUMBER)
else if (di->di_tv.v_type == VAR_NUMBER)
{
v->di_tv.vval.v_number = tv_get_number(tv);
di->di_tv.vval.v_number = tv_get_number(tv);
if (STRCMP(varname, "searchforward") == 0)
set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
#ifdef FEAT_SEARCH_EXTRA
else if (STRCMP(varname, "hlsearch") == 0)
{
no_hlsearch = !v->di_tv.vval.v_number;
no_hlsearch = !di->di_tv.vval.v_number;
redraw_all_later(SOME_VALID);
}
#endif
return;
}
else if (v->di_tv.v_type != tv->v_type)
else if (di->di_tv.v_type != tv->v_type)
{
semsg(_("E963: setting %s to value with wrong type"), name);
return;
}
}
clear_tv(&v->di_tv);
clear_tv(&di->di_tv);
}
else // add a new variable
{
@ -2725,31 +2861,53 @@ set_var_const(
if (!valid_varname(varname))
return;
v = alloc(sizeof(dictitem_T) + STRLEN(varname));
if (v == NULL)
di = alloc(sizeof(dictitem_T) + STRLEN(varname));
if (di == NULL)
return;
STRCPY(v->di_key, varname);
if (hash_add(ht, DI2HIKEY(v)) == FAIL)
STRCPY(di->di_key, varname);
if (hash_add(ht, DI2HIKEY(di)) == FAIL)
{
vim_free(v);
vim_free(di);
return;
}
v->di_flags = DI_FLAGS_ALLOC;
if (is_const)
v->di_flags |= DI_FLAGS_LOCK;
di->di_flags = DI_FLAGS_ALLOC;
if (flags & LET_IS_CONST)
di->di_flags |= DI_FLAGS_LOCK;
if (is_script_local && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
// Store a pointer to the typval_T, so that it can be found by
// index instead of using a hastab lookup.
if (ga_grow(&si->sn_var_vals, 1) == OK)
{
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ si->sn_var_vals.ga_len;
sv->sv_name = di->di_key;
sv->sv_tv = &di->di_tv;
sv->sv_type = type == NULL ? &t_any : type;
sv->sv_const = (flags & LET_IS_CONST);
sv->sv_export = is_export;
++si->sn_var_vals.ga_len;
// let ex_export() know the export worked.
is_export = FALSE;
}
}
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
copy_tv(tv, &v->di_tv);
copy_tv(tv, &di->di_tv);
else
{
v->di_tv = *tv;
v->di_tv.v_lock = 0;
di->di_tv = *tv;
di->di_tv.v_lock = 0;
init_tv(tv);
}
if (is_const)
v->di_tv.v_lock |= VAR_LOCKED;
if (flags & LET_IS_CONST)
di->di_tv.v_lock |= VAR_LOCKED;
}
/*
@ -3130,9 +3288,9 @@ var_redir_start(char_u *name, int append)
tv.v_type = VAR_STRING;
tv.vval.v_string = (char_u *)"";
if (append)
set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)".");
set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)".");
else
set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"=");
set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)"=");
clear_lval(redir_lval);
err = did_emsg;
did_emsg |= save_emsg;
@ -3205,7 +3363,7 @@ var_redir_stop(void)
redir_endp = get_lval(redir_varname, NULL, redir_lval,
FALSE, FALSE, 0, FNE_CHECK_START);
if (redir_endp != NULL && redir_lval->ll_name != NULL)
set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE,
set_var_lval(redir_lval, redir_endp, &tv, FALSE, 0,
(char_u *)".");
clear_lval(redir_lval);
}

View File

@ -9,28 +9,28 @@ static const unsigned short cmdidxs1[26] =
/* b */ 19,
/* c */ 42,
/* d */ 108,
/* e */ 130,
/* f */ 151,
/* g */ 167,
/* h */ 173,
/* i */ 182,
/* j */ 200,
/* k */ 202,
/* l */ 207,
/* m */ 269,
/* n */ 287,
/* o */ 307,
/* p */ 319,
/* q */ 358,
/* r */ 361,
/* s */ 381,
/* t */ 450,
/* u */ 495,
/* v */ 506,
/* w */ 524,
/* x */ 538,
/* y */ 548,
/* z */ 549
/* e */ 132,
/* f */ 155,
/* g */ 171,
/* h */ 177,
/* i */ 186,
/* j */ 205,
/* k */ 207,
/* l */ 212,
/* m */ 274,
/* n */ 292,
/* o */ 312,
/* p */ 324,
/* q */ 363,
/* r */ 366,
/* s */ 386,
/* t */ 455,
/* u */ 500,
/* v */ 511,
/* w */ 530,
/* x */ 544,
/* y */ 554,
/* z */ 555
};
/*
@ -44,12 +44,12 @@ static const unsigned char cmdidxs2[26][26] =
/* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 7, 15, 0, 16, 0, 0, 0, 0, 0 },
/* b */ { 2, 0, 0, 4, 5, 7, 0, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 13, 0, 0, 0, 0, 22, 0, 0, 0 },
/* 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, 6, 15, 0, 16, 0, 0, 17, 0, 0, 19, 20, 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, 16, 0, 17, 0, 0 },
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 7, 17, 0, 18, 0, 0, 19, 0, 0, 21, 22, 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 },
/* 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, 13, 0, 15, 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 },
/* j */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
/* k */ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* l */ { 3, 11, 15, 19, 20, 24, 27, 32, 0, 0, 0, 34, 37, 40, 44, 50, 0, 52, 61, 53, 54, 58, 60, 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, 9, 12, 0, 0, 0, 0, 15, 0, 16, 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 },
/* 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 = 562;
static const int command_count = 568;

View File

@ -442,6 +442,9 @@ EXCMD(CMD_debug, "debug", ex_debug,
EXCMD(CMD_debuggreedy, "debuggreedy", ex_debuggreedy,
EX_RANGE|EX_ZEROR|EX_TRLBAR|EX_CMDWIN,
ADDR_OTHER),
EXCMD(CMD_def, "def", ex_function,
EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN,
ADDR_NONE),
EXCMD(CMD_delcommand, "delcommand", ex_delcommand,
EX_NEEDARG|EX_WORD1|EX_TRLBAR|EX_CMDWIN,
ADDR_NONE),
@ -475,6 +478,9 @@ EXCMD(CMD_diffthis, "diffthis", ex_diffthis,
EXCMD(CMD_digraphs, "digraphs", ex_digraphs,
EX_BANG|EX_EXTRA|EX_TRLBAR|EX_CMDWIN,
ADDR_NONE),
EXCMD(CMD_disassemble, "disassemble", ex_disassemble,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN,
ADDR_NONE),
EXCMD(CMD_djump, "djump", ex_findpat,
EX_BANG|EX_RANGE|EX_DFLALL|EX_WHOLEFOLD|EX_EXTRA,
ADDR_LINES),
@ -529,6 +535,9 @@ EXCMD(CMD_emenu, "emenu", ex_emenu,
EXCMD(CMD_endif, "endif", ex_endif,
EX_TRLBAR|EX_SBOXOK|EX_CMDWIN,
ADDR_NONE),
EXCMD(CMD_enddef, "enddef", ex_endfunction,
EX_TRLBAR|EX_CMDWIN,
ADDR_NONE),
EXCMD(CMD_endfunction, "endfunction", ex_endfunction,
EX_TRLBAR|EX_CMDWIN,
ADDR_NONE),
@ -556,6 +565,9 @@ EXCMD(CMD_execute, "execute", ex_execute,
EXCMD(CMD_exit, "exit", ex_exit,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILE1|EX_ARGOPT|EX_DFLALL|EX_TRLBAR|EX_CMDWIN,
ADDR_LINES),
EXCMD(CMD_export, "export", ex_export,
EX_EXTRA|EX_NOTRLCOM,
ADDR_NONE),
EXCMD(CMD_exusage, "exusage", ex_exusage,
EX_TRLBAR,
ADDR_NONE),
@ -679,6 +691,9 @@ EXCMD(CMD_imapclear, "imapclear", ex_mapclear,
EXCMD(CMD_imenu, "imenu", ex_menu,
EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN,
ADDR_OTHER),
EXCMD(CMD_import, "import", ex_import,
EX_EXTRA|EX_NOTRLCOM,
ADDR_NONE),
EXCMD(CMD_inoremap, "inoremap", ex_map,
EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN,
ADDR_NONE),
@ -1648,6 +1663,9 @@ EXCMD(CMD_vimgrep, "vimgrep", ex_vimgrep,
EXCMD(CMD_vimgrepadd, "vimgrepadd", ex_vimgrep,
EX_RANGE|EX_BANG|EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_TRLBAR|EX_XFILE,
ADDR_OTHER),
EXCMD(CMD_vim9script, "vim9script", ex_vim9script,
0,
ADDR_NONE),
EXCMD(CMD_viusage, "viusage", ex_viusage,
EX_TRLBAR,
ADDR_NONE),

View File

@ -27,7 +27,6 @@ static int if_level = 0; // depth in :if
#endif
static void free_cmdmod(void);
static void append_command(char_u *cmd);
static char_u *find_command(exarg_T *eap, int *full);
#ifndef FEAT_MENU
# define ex_emenu ex_ni
@ -275,6 +274,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
# define ex_debug ex_ni
# define ex_debuggreedy ex_ni
# define ex_delfunction ex_ni
# define ex_disassemble ex_ni
# define ex_echo ex_ni
# define ex_echohl ex_ni
# define ex_else ex_ni
@ -300,7 +300,10 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
# define ex_try ex_ni
# define ex_unlet ex_ni
# define ex_unlockvar ex_ni
# define ex_vim9script ex_ni
# define ex_while ex_ni
# define ex_import ex_ni
# define ex_export ex_ni
#endif
#ifndef FEAT_SESSION
# define ex_loadview ex_ni
@ -1708,7 +1711,13 @@ do_one_cmd(
ea.cmd = skip_range(ea.cmd, NULL);
if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
ea.cmd = skipwhite(ea.cmd + 1);
p = find_command(&ea, NULL);
#ifdef FEAT_EVAL
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9)
p = find_ex_command(&ea, NULL, lookup_scriptvar, NULL);
else
#endif
p = find_ex_command(&ea, NULL, NULL, NULL);
#ifdef FEAT_EVAL
# ifdef FEAT_PROFILE
@ -1876,7 +1885,7 @@ do_one_cmd(
#ifdef FEAT_EVAL
&& !aborting()
#endif
) ? find_command(&ea, NULL) : ea.cmd;
) ? find_ex_command(&ea, NULL, NULL, NULL) : ea.cmd;
}
if (p == NULL)
@ -2485,6 +2494,10 @@ do_one_cmd(
}
#ifdef FEAT_EVAL
// Set flag that any command was executed, used by ex_vim9script().
if (getline_equal(ea.getline, ea.cookie, getsourceline))
SCRIPT_ITEM(current_sctx.sc_sid).sn_had_command = TRUE;
/*
* If the command just executed called do_cmdline(), any throw or ":return"
* or ":finish" encountered there must also check the cstack of the still
@ -3108,15 +3121,64 @@ append_command(char_u *cmd)
* Start of the name can be found at eap->cmd.
* Sets eap->cmdidx and returns a pointer to char after the command name.
* "full" is set to TRUE if the whole command name matched.
*
* If "lookup" is not NULL recognize expression without "eval" or "call" and
* assignment without "let". Sets eap->cmdidx to the command while returning
* "eap->cmd".
*
* Returns NULL for an ambiguous user command.
*/
static char_u *
find_command(exarg_T *eap, int *full UNUSED)
char_u *
find_ex_command(
exarg_T *eap,
int *full UNUSED,
int (*lookup)(char_u *, size_t, cctx_T *) UNUSED,
cctx_T *cctx UNUSED)
{
int len;
char_u *p;
int i;
#ifdef FEAT_EVAL
/*
* Recognize a Vim9 script function/method call and assignment:
* "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
*/
if (lookup != NULL && (p = to_name_const_end(eap->cmd)) > eap->cmd
&& *p != NUL)
{
int oplen;
int heredoc;
// "funcname(" is always a function call.
// "varname[]" is an expression.
// "g:varname" is an expression.
// "varname->expr" is an expression.
if (*p == '('
|| *p == '['
|| p[1] == ':'
|| (*p == '-' && p[1] == '>'))
{
eap->cmdidx = CMD_eval;
return eap->cmd;
}
oplen = assignment_len(skipwhite(p), &heredoc);
if (oplen > 0)
{
// Recognize an assignment if we recognize the variable name:
// "g:var = expr"
// "var = expr" where "var" is a local var name.
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
|| lookup(eap->cmd, p - eap->cmd, cctx) >= 0)
{
eap->cmdidx = CMD_let;
return eap->cmd;
}
}
}
#endif
/*
* Isolate the command and search for it in the command table.
* Exceptions:
@ -3149,8 +3211,17 @@ find_command(exarg_T *eap, int *full UNUSED)
++p;
// for python 3.x support ":py3", ":python3", ":py3file", etc.
if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y')
{
while (ASCII_ISALNUM(*p))
++p;
}
else if (*p == '9' && STRNCMP("vim9", eap->cmd, 4) == 0)
{
// include "9" for "vim9script"
++p;
while (ASCII_ISALPHA(*p))
++p;
}
// check for non-alpha command
if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
@ -3307,7 +3378,7 @@ cmd_exists(char_u *name)
// For ":2match" and ":3match" we need to skip the number.
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
ea.cmdidx = (cmdidx_T)0;
p = find_command(&ea, &full);
p = find_ex_command(&ea, &full, NULL, NULL);
if (p == NULL)
return 3;
if (vim_isdigit(*name) && ea.cmdidx != CMD_match)
@ -8558,7 +8629,7 @@ ex_folddo(exarg_T *eap)
}
#endif
#ifdef FEAT_QUICKFIX
#if defined(FEAT_QUICKFIX) || defined(PROTO)
/*
* Returns TRUE if the supplied Ex cmdidx is for a location list command
* instead of a quickfix command.

View File

@ -15,7 +15,6 @@
#if defined(FEAT_EVAL) || defined(PROTO)
static int throw_exception(void *, except_type_T, char_u *);
static char *get_end_emsg(cstack_T *cstack);
/*
@ -498,7 +497,7 @@ get_exception_string(
* user or interrupt exception, or points to a message list in case of an
* error exception.
*/
static int
int
throw_exception(void *value, except_type_T type, char_u *cmdname)
{
except_T *excp;
@ -649,7 +648,7 @@ discard_current_exception(void)
/*
* Put an exception on the caught stack.
*/
static void
void
catch_exception(except_T *excp)
{
excp->caught = caught_stack;
@ -932,7 +931,7 @@ ex_endif(exarg_T *eap)
if (eap->cstack->cs_idx < 0
|| (eap->cstack->cs_flags[eap->cstack->cs_idx]
& (CSF_WHILE | CSF_FOR | CSF_TRY)))
eap->errmsg = N_("E580: :endif without :if");
eap->errmsg = N_(e_endif_without_if);
else
{
/*
@ -976,10 +975,10 @@ ex_else(exarg_T *eap)
{
if (eap->cmdidx == CMD_else)
{
eap->errmsg = N_("E581: :else without :if");
eap->errmsg = N_(e_else_without_if);
return;
}
eap->errmsg = N_("E582: :elseif without :if");
eap->errmsg = N_(e_elseif_without_if);
skip = TRUE;
}
else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE)
@ -1152,7 +1151,7 @@ ex_continue(exarg_T *eap)
cstack_T *cstack = eap->cstack;
if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = N_("E586: :continue without :while or :for");
eap->errmsg = N_(e_continue);
else
{
// Try to find the matching ":while". This might stop at a try
@ -1190,7 +1189,7 @@ ex_break(exarg_T *eap)
cstack_T *cstack = eap->cstack;
if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = N_("E587: :break without :while or :for");
eap->errmsg = N_(e_break);
else
{
// Inactivate conditionals until the matching ":while" or a try
@ -1492,7 +1491,7 @@ ex_catch(exarg_T *eap)
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
{
eap->errmsg = N_("E603: :catch without :try");
eap->errmsg = e_catch;
give_up = TRUE;
}
else
@ -1648,7 +1647,7 @@ ex_finally(exarg_T *eap)
cstack_T *cstack = eap->cstack;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = N_("E606: :finally without :try");
eap->errmsg = e_finally;
else
{
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
@ -1668,7 +1667,7 @@ ex_finally(exarg_T *eap)
if (cstack->cs_flags[idx] & CSF_FINALLY)
{
// Give up for a multiple ":finally" and ignore it.
eap->errmsg = N_("E607: multiple :finally");
eap->errmsg = e_finally_dup;
return;
}
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
@ -1777,7 +1776,7 @@ ex_endtry(exarg_T *eap)
cstack_T *cstack = eap->cstack;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = N_("E602: :endtry without :try");
eap->errmsg = e_no_endtry;
else
{
/*

View File

@ -1892,6 +1892,7 @@ f_writefile(typval_T *argvars, typval_T *rettv)
list = argvars[0].vval.v_list;
if (list == NULL)
return;
range_list_materialize(list);
for (li = list->lv_first; li != NULL; li = li->li_next)
if (tv_get_string_chk(&li->li_tv) == NULL)
return;

View File

@ -287,6 +287,9 @@ EXTERN int do_profiling INIT(= PROF_NONE); // PROF_ values
# endif
EXTERN garray_T script_items INIT5(0, 0, sizeof(scriptitem_T), 4, NULL);
# define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
# define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars)
# define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
# define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
/*
@ -359,9 +362,6 @@ EXTERN int suppress_errthrow INIT(= FALSE);
*/
EXTERN except_T *caught_stack INIT(= NULL);
#endif
#ifdef FEAT_EVAL
/*
* Garbage collection can only take place when we are sure there are no Lists
* or Dictionaries being used internally. This is flagged with
@ -376,6 +376,39 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
// Script CTX being sourced or was sourced to define the current function.
EXTERN sctx_T current_sctx INIT4(0, 0, 0, 0);
// Commonly used types.
EXTERN type_T t_any INIT4(VAR_UNKNOWN, 0, NULL, NULL);
EXTERN type_T t_void INIT4(VAR_VOID, 0, NULL, NULL);
EXTERN type_T t_bool INIT4(VAR_BOOL, 0, NULL, NULL);
EXTERN type_T t_special INIT4(VAR_SPECIAL, 0, NULL, NULL);
EXTERN type_T t_number INIT4(VAR_NUMBER, 0, NULL, NULL);
#ifdef FEAT_FLOAT
EXTERN type_T t_float INIT4(VAR_FLOAT, 0, NULL, NULL);
#endif
EXTERN type_T t_string INIT4(VAR_STRING, 0, NULL, NULL);
EXTERN type_T t_blob INIT4(VAR_BLOB, 0, NULL, NULL);
EXTERN type_T t_job INIT4(VAR_JOB, 0, NULL, NULL);
EXTERN type_T t_channel INIT4(VAR_CHANNEL, 0, NULL, NULL);
EXTERN type_T t_func_void INIT4(VAR_FUNC, -1, &t_void, NULL);
EXTERN type_T t_func_any INIT4(VAR_FUNC, -1, &t_any, NULL);
EXTERN type_T t_partial_void INIT4(VAR_PARTIAL, -1, &t_void, NULL);
EXTERN type_T t_partial_any INIT4(VAR_PARTIAL, -1, &t_any, NULL);
EXTERN type_T t_list_any INIT4(VAR_LIST, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT4(VAR_DICT, 0, &t_any, NULL);
EXTERN type_T t_list_number INIT4(VAR_LIST, 0, &t_number, NULL);
EXTERN type_T t_list_string INIT4(VAR_LIST, 0, &t_string, NULL);
EXTERN type_T t_list_dict_any INIT4(VAR_LIST, 0, &t_dict_any, NULL);
EXTERN type_T t_dict_number INIT4(VAR_DICT, 0, &t_number, NULL);
EXTERN type_T t_dict_string INIT4(VAR_DICT, 0, &t_string, NULL);
#endif
EXTERN int did_source_packages INIT(= FALSE);
@ -1038,6 +1071,8 @@ EXTERN int ctrl_c_interrupts INIT(= TRUE); // CTRL-C sets got_int
EXTERN cmdmod_T cmdmod; // Ex command modifiers
EXTERN int is_export INIT(= FALSE); // :export {cmd}
EXTERN int msg_silent INIT(= 0); // don't print messages
EXTERN int emsg_silent INIT(= 0); // don't print error messages
EXTERN int emsg_noredir INIT(= 0); // don't redirect error messages
@ -1465,7 +1500,11 @@ EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> exec
EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed from exrc/vimrc in current dir or tag search"));
#ifdef FEAT_EVAL
EXTERN char e_endif[] INIT(= N_("E171: Missing :endif"));
EXTERN char e_catch[] INIT(= N_("E603: :catch without :try"));
EXTERN char e_finally[] INIT(= N_("E606: :finally without :try"));
EXTERN char e_finally_dup[] INIT(= N_("E607: multiple :finally"));
EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry"));
EXTERN char e_no_endtry[] INIT(= N_("E602: :endtry without :try"));
EXTERN char e_endwhile[] INIT(= N_("E170: Missing :endwhile"));
EXTERN char e_endfor[] INIT(= N_("E170: Missing :endfor"));
EXTERN char e_while[] INIT(= N_("E588: :endwhile without :while"));
@ -1556,7 +1595,7 @@ EXTERN char e_notmp[] INIT(= N_("E483: Can't get temp file name"));
EXTERN char e_notopen[] INIT(= N_("E484: Can't open file %s"));
EXTERN char e_notread[] INIT(= N_("E485: Can't read file %s"));
EXTERN char e_null[] INIT(= N_("E38: Null argument"));
#if defined(FEAT_DIGRAPHS) || defined(FEAT_TIMERS)
#if defined(FEAT_DIGRAPHS) || defined(FEAT_TIMERS) || defined(FEAT_EVAL)
EXTERN char e_number_exp[] INIT(= N_("E39: Number expected"));
#endif
#ifdef FEAT_QUICKFIX
@ -1594,12 +1633,18 @@ EXTERN char e_listidx[] INIT(= N_("E684: list index out of range: %ld"));
EXTERN char e_blobidx[] INIT(= N_("E979: Blob index out of range: %ld"));
EXTERN char e_invalblob[] INIT(= N_("E978: Invalid operation for Blob"));
EXTERN char e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s"));
EXTERN char e_toofewarg[] INIT(= N_("E119: Not enough arguments for function: %s"));
EXTERN char e_func_deleted[] INIT(= N_("E933: Function was deleted: %s"));
EXTERN char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s"));
EXTERN char e_listreq[] INIT(= N_("E714: List required"));
EXTERN char e_listblobreq[] INIT(= N_("E897: List or Blob required"));
EXTERN char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary"));
EXTERN char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List, Dictionary or Blob"));
EXTERN char e_modulus[] INIT(= N_("E804: Cannot use '%' with Float"));
EXTERN char e_inval_string[] INIT(= N_("E908: using an invalid value as a String"));
EXTERN char e_const_option[] INIT(= N_("E996: Cannot lock an option"));
EXTERN char e_unknown_option[] INIT(= N_("E113: Unknown option: %s"));
EXTERN char e_letunexp[] INIT(= N_("E18: Unexpected characters in :let"));
#endif
#ifdef FEAT_QUICKFIX
EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
@ -1632,7 +1677,12 @@ EXTERN char e_write[] INIT(= N_("E80: Error while writing"));
EXTERN char e_zerocount[] INIT(= N_("E939: Positive count required"));
#ifdef FEAT_EVAL
EXTERN char e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context"));
EXTERN char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
EXTERN char e_missing_paren[] INIT(= N_("E107: Missing parentheses: %s"));
EXTERN char e_missing_close[] INIT(= N_("E110: Missing ')'"));
EXTERN char e_missing_dict_colon[] INIT(= N_("E720: Missing colon in Dictionary: %s"));
EXTERN char e_duplicate_key[] INIT(= N_("E721: Duplicate key in Dictionary: \"%s\""));
EXTERN char e_missing_dict_comma[] INIT(= N_("E722: Missing comma in Dictionary: %s"));
EXTERN char e_missing_dict_end[] INIT(= N_("E723: Missing end of Dictionary '}': %s"));
#endif
#ifdef FEAT_CLIENTSERVER
EXTERN char e_invexprmsg[] INIT(= N_("E449: Invalid expression received"));
@ -1660,6 +1710,18 @@ EXTERN char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode"
#endif
EXTERN char e_invalwindow[] INIT(= N_("E957: Invalid window number"));
EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
#ifdef FEAT_EVAL
EXTERN char e_missing_colon[] INIT(= N_("E109: Missing ':' after '?'"));
EXTERN char e_missing_in[] INIT(= N_("E690: Missing \"in\" after :for"));
EXTERN char e_unknownfunc[] INIT(= N_("E117: Unknown function: %s"));
EXTERN char e_missbrac[] INIT(= N_("E111: Missing ']'"));
EXTERN char e_else_without_if[] INIT(= N_("E581: :else without :if"));
EXTERN char e_elseif_without_if[] INIT(= N_("E582: :elseif without :if"));
EXTERN char e_endif_without_if[] INIT(= N_("E580: :endif without :if"));
EXTERN char e_continue[] INIT(= N_("E586: :continue without :while or :for"));
EXTERN char e_break[] INIT(= N_("E587: :break without :while or :for"));
EXTERN char e_nowhitespace[] INIT(= N_("E274: No white space allowed before parenthesis"));
#endif
#ifdef FEAT_GUI_MAC
EXTERN short disallow_gui INIT(= FALSE);
@ -1735,6 +1797,9 @@ EXTERN int *eval_lavars_used INIT(= NULL);
// Only filled for Win32.
EXTERN char windowsVersion[20] INIT(= {0});
// Used for a non-materialized range() list.
EXTERN listitem_T range_list_item;
#endif
#ifdef MSWIN

View File

@ -519,7 +519,7 @@ gui_init(void)
if (vim_strchr(p_go, GO_NOSYSMENU) == NULL)
{
sys_menu = TRUE;
do_source((char_u *)SYS_MENU_FILE, FALSE, DOSO_NONE);
do_source((char_u *)SYS_MENU_FILE, FALSE, DOSO_NONE, NULL);
sys_menu = FALSE;
}
#endif
@ -540,8 +540,8 @@ gui_init(void)
{
if (STRCMP(use_gvimrc, "NONE") != 0
&& STRCMP(use_gvimrc, "NORC") != 0
&& do_source(use_gvimrc, FALSE, DOSO_NONE) != OK)
semsg(_("E230: Cannot read from \"%s\""), use_gvimrc);
&& do_source(use_gvimrc, FALSE, DOSO_NONE, NULL) != OK)
semsg(_("E230: Cannot read from \"%s\""), use_gvimrc, NULL);
}
else
{
@ -549,7 +549,7 @@ gui_init(void)
* Get system wide defaults for gvim, only when file name defined.
*/
#ifdef SYS_GVIMRC_FILE
do_source((char_u *)SYS_GVIMRC_FILE, FALSE, DOSO_NONE);
do_source((char_u *)SYS_GVIMRC_FILE, FALSE, DOSO_NONE, NULL);
#endif
/*
@ -563,19 +563,20 @@ gui_init(void)
*/
if (process_env((char_u *)"GVIMINIT", FALSE) == FAIL
&& do_source((char_u *)USR_GVIMRC_FILE, TRUE,
DOSO_GVIMRC) == FAIL
DOSO_GVIMRC, NULL) == FAIL
#ifdef USR_GVIMRC_FILE2
&& do_source((char_u *)USR_GVIMRC_FILE2, TRUE,
DOSO_GVIMRC) == FAIL
DOSO_GVIMRC, NULL) == FAIL
#endif
#ifdef USR_GVIMRC_FILE3
&& do_source((char_u *)USR_GVIMRC_FILE3, TRUE,
DOSO_GVIMRC) == FAIL
DOSO_GVIMRC, NULL) == FAIL
#endif
)
{
#ifdef USR_GVIMRC_FILE4
(void)do_source((char_u *)USR_GVIMRC_FILE4, TRUE, DOSO_GVIMRC);
(void)do_source((char_u *)USR_GVIMRC_FILE4, TRUE,
DOSO_GVIMRC, NULL);
#endif
}
@ -623,7 +624,7 @@ gui_init(void)
(char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
do_source((char_u *)GVIMRC_FILE, TRUE, DOSO_GVIMRC);
do_source((char_u *)GVIMRC_FILE, TRUE, DOSO_GVIMRC, NULL);
if (secure == 2)
need_wait_return = TRUE;

View File

@ -841,8 +841,7 @@ luaV_list_newindex(lua_State *L)
if (lua_isnil(L, 3)) // remove?
{
vimlist_remove(l, li, li);
clear_tv(&li->li_tv);
vim_free(li);
listitem_free(l, li);
}
else
{

View File

@ -785,6 +785,7 @@ VimToPython(typval_T *our_tv, int depth, PyObject *lookup_dict)
return NULL;
}
range_list_materialize(list);
for (curr = list->lv_first; curr != NULL; curr = curr->li_next)
{
if (!(newObj = VimToPython(&curr->li_tv, depth + 1, lookup_dict)))
@ -2255,6 +2256,7 @@ ListNew(PyTypeObject *subtype, list_T *list)
return NULL;
self->list = list;
++list->lv_refcount;
range_list_materialize(list);
pyll_add((PyObject *)(self), &self->ref, &lastlist);
@ -2302,7 +2304,7 @@ list_py_concat(list_T *l, PyObject *obj, PyObject *lookup_dict)
{
Py_DECREF(item);
Py_DECREF(iterator);
listitem_free(li);
listitem_free(l, li);
return -1;
}
@ -2662,7 +2664,7 @@ ListAssSlice(ListObject *self, Py_ssize_t first,
}
for (i = 0; i < numreplaced; i++)
listitem_free(lis[i]);
listitem_free(l, lis[i]);
if (step == 1)
for (i = numreplaced; i < slicelen; i++)
listitem_remove(l, lis[i]);
@ -2822,6 +2824,7 @@ ListIter(ListObject *self)
return NULL;
}
range_list_materialize(l);
list_add_watch(l, &lii->lw);
lii->lw.lw_item = l->lv_first;
lii->list = l;
@ -3018,6 +3021,7 @@ FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
return NULL;
}
argslist = argstv.vval.v_list;
range_list_materialize(argslist);
argc = argslist->lv_len;
if (argc != 0)
@ -6386,6 +6390,7 @@ ConvertToPyObject(typval_T *tv)
(char*) tv->vval.v_blob->bv_ga.ga_data,
(Py_ssize_t) tv->vval.v_blob->bv_ga.ga_len);
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_CHANNEL:
case VAR_JOB:
Py_INCREF(Py_None);

View File

@ -577,7 +577,7 @@ ins_compl_add(
int len,
char_u *fname,
char_u **cptext, // extra text for popup menu or NULL
typval_T *user_data, // "user_data" entry or NULL
typval_T *user_data UNUSED, // "user_data" entry or NULL
int cdir,
int flags_arg,
int adup) // accept duplicate match

View File

@ -350,6 +350,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
break;
#endif
case VAR_UNKNOWN:
case VAR_VOID:
internal_error("json_encode_item()");
return FAIL;
}

View File

@ -65,6 +65,17 @@ list_fix_watch(list_T *l, listitem_T *item)
lw->lw_item = item->li_next;
}
static void
list_init(list_T *l)
{
// Prepend the list to the list of lists for garbage collection.
if (first_list != NULL)
first_list->lv_used_prev = l;
l->lv_used_prev = NULL;
l->lv_used_next = first_list;
first_list = l;
}
/*
* Allocate an empty header for a list.
* Caller should take care of the reference count.
@ -76,14 +87,7 @@ list_alloc(void)
l = ALLOC_CLEAR_ONE(list_T);
if (l != NULL)
{
// Prepend the list to the list of lists for garbage collection.
if (first_list != NULL)
first_list->lv_used_prev = l;
l->lv_used_prev = NULL;
l->lv_used_next = first_list;
first_list = l;
}
list_init(l);
return l;
}
@ -100,6 +104,59 @@ list_alloc_id(alloc_id_T id UNUSED)
return (list_alloc());
}
/*
* Allocate space for a list, plus "count" items.
* Next list_set_item() must be called for each item.
*/
list_T *
list_alloc_with_items(int count)
{
list_T *l;
l = (list_T *)alloc_clear(sizeof(list_T) + count * sizeof(listitem_T));
if (l != NULL)
{
list_init(l);
if (count > 0)
{
listitem_T *li = (listitem_T *)(l + 1);
int i;
l->lv_len = count;
l->lv_with_items = count;
l->lv_first = li;
l->lv_last = li + count - 1;
for (i = 0; i < count; ++i)
{
if (i == 0)
li->li_prev = NULL;
else
li->li_prev = li - 1;
if (i == count - 1)
li->li_next = NULL;
else
li->li_next = li + 1;
++li;
}
}
}
return l;
}
/*
* Set item "idx" for a list previously allocated with list_alloc_with_items().
* The contents of "tv" is moved into the list item.
* Each item must be set exactly once.
*/
void
list_set_item(list_T *l, int idx, typval_T *tv)
{
listitem_T *li = (listitem_T *)(l + 1) + idx;
li->li_tv = *tv;
}
/*
* Allocate an empty list for a return value, with reference count set.
* Returns OK or FAIL.
@ -163,12 +220,13 @@ list_free_contents(list_T *l)
{
listitem_T *item;
if (l->lv_first != &range_list_item)
for (item = l->lv_first; item != NULL; item = l->lv_first)
{
// Remove the item before deleting it.
l->lv_first = item->li_next;
clear_tv(&item->li_tv);
vim_free(item);
list_free_item(l, item);
}
}
@ -250,13 +308,26 @@ listitem_alloc(void)
}
/*
* Free a list item. Also clears the value. Does not notify watchers.
* Free a list item, unless it was allocated together with the list itself.
* Does not clear the value. Does not notify watchers.
*/
void
listitem_free(listitem_T *item)
list_free_item(list_T *l, listitem_T *item)
{
if (l->lv_with_items == 0 || item < (listitem_T *)l
|| item >= (listitem_T *)(l + 1) + l->lv_with_items)
vim_free(item);
}
/*
* Free a list item, unless it was allocated together with the list itself.
* Also clears the value. Does not notify watchers.
*/
void
listitem_free(list_T *l, listitem_T *item)
{
clear_tv(&item->li_tv);
vim_free(item);
list_free_item(l, item);
}
/*
@ -266,7 +337,7 @@ listitem_free(listitem_T *item)
listitem_remove(list_T *l, listitem_T *item)
{
vimlist_remove(l, item, item);
listitem_free(item);
listitem_free(l, item);
}
/*
@ -299,6 +370,9 @@ list_equal(
if (list_len(l1) != list_len(l2))
return FALSE;
range_list_materialize(l1);
range_list_materialize(l2);
for (item1 = l1->lv_first, item2 = l2->lv_first;
item1 != NULL && item2 != NULL;
item1 = item1->li_next, item2 = item2->li_next)
@ -329,6 +403,8 @@ list_find(list_T *l, long n)
if (n < 0 || n >= l->lv_len)
return NULL;
range_list_materialize(l);
// When there is a cached index may start search from there.
if (l->lv_idx_item != NULL)
{
@ -398,6 +474,26 @@ list_find_nr(
{
listitem_T *li;
if (l != NULL && l->lv_first == &range_list_item)
{
long n = idx;
// not materialized range() list: compute the value.
// Negative index is relative to the end.
if (n < 0)
n = l->lv_len + n;
// Check for index out of range.
if (n < 0 || n >= l->lv_len)
{
if (errorp != NULL)
*errorp = TRUE;
return -1L;
}
return l->lv_start + n * l->lv_stride;
}
li = list_find(l, idx);
if (li == NULL)
{
@ -437,6 +533,7 @@ list_idx_of_item(list_T *l, listitem_T *item)
if (l == NULL)
return -1;
range_list_materialize(l);
idx = 0;
for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
++idx;
@ -451,6 +548,7 @@ list_idx_of_item(list_T *l, listitem_T *item)
void
list_append(list_T *l, listitem_T *item)
{
range_list_materialize(l);
if (l->lv_last == NULL)
{
// empty list
@ -469,7 +567,7 @@ list_append(list_T *l, listitem_T *item)
}
/*
* Append typval_T "tv" to the end of list "l".
* Append typval_T "tv" to the end of list "l". "tv" is copied.
* Return FAIL when out of memory.
*/
int
@ -484,6 +582,22 @@ list_append_tv(list_T *l, typval_T *tv)
return OK;
}
/*
* As list_append_tv() but move the value instead of copying it.
* Return FAIL when out of memory.
*/
int
list_append_tv_move(list_T *l, typval_T *tv)
{
listitem_T *li = listitem_alloc();
if (li == NULL)
return FAIL;
li->li_tv = *tv;
list_append(l, li);
return OK;
}
/*
* Add a dictionary to a list. Used by getqflist().
* Return FAIL when out of memory.
@ -584,6 +698,7 @@ list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
void
list_insert(list_T *l, listitem_T *ni, listitem_T *item)
{
range_list_materialize(l);
if (item == NULL)
// Append new item at end of list.
list_append(l, ni);
@ -618,6 +733,9 @@ list_extend(list_T *l1, list_T *l2, listitem_T *bef)
listitem_T *item;
int todo = l2->lv_len;
range_list_materialize(l1);
range_list_materialize(l2);
// We also quit the loop when we have inserted the original item count of
// the list, avoid a hang when we extend a list with itself.
for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next)
@ -675,6 +793,7 @@ list_copy(list_T *orig, int deep, int copyID)
orig->lv_copyID = copyID;
orig->lv_copylist = copy;
}
range_list_materialize(orig);
for (item = orig->lv_first; item != NULL && !got_int;
item = item->li_next)
{
@ -715,6 +834,8 @@ vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2)
{
listitem_T *ip;
range_list_materialize(l);
// notify watchers
for (ip = item; ip != NULL; ip = ip->li_next)
{
@ -748,6 +869,7 @@ list2string(typval_T *tv, int copyID, int restore_copyID)
return NULL;
ga_init2(&ga, (int)sizeof(char), 80);
ga_append(&ga, '[');
range_list_materialize(tv->vval.v_list);
if (list_join(&ga, tv->vval.v_list, (char_u *)", ",
FALSE, restore_copyID, copyID) == FAIL)
{
@ -785,6 +907,7 @@ list_join_inner(
char_u *s;
// Stringify each item in the list.
range_list_materialize(l);
for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
{
s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID,
@ -915,7 +1038,7 @@ f_join(typval_T *argvars, typval_T *rettv)
* Return OK or FAIL.
*/
int
get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error)
{
list_T *l = NULL;
typval_T tv;
@ -950,6 +1073,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
break;
if (**arg != ',')
{
if (do_error)
semsg(_("E696: Missing comma in List: %s"), *arg);
goto failret;
}
@ -958,6 +1082,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
if (**arg != ']')
{
if (do_error)
semsg(_("E697: Missing end of List ']': %s"), *arg);
failret:
if (evaluate)
@ -983,6 +1108,7 @@ write_list(FILE *fd, list_T *list, int binary)
int ret = OK;
char_u *s;
range_list_materialize(list);
for (li = list->lv_first; li != NULL; li = li->li_next)
{
for (s = tv_get_string(&li->li_tv); *s != NUL; ++s)
@ -1069,6 +1195,7 @@ f_list2str(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
range_list_materialize(l);
ga_init2(&ga, 1, 80);
if (has_mbyte || utf8)
{
@ -1123,7 +1250,7 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
// Remove one item, return its value.
vimlist_remove(l, item, item);
*rettv = item->li_tv;
vim_free(item);
list_free_item(l, item);
}
else
{
@ -1361,6 +1488,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
TRUE))
goto theend;
rettv_list_set(rettv, l);
range_list_materialize(l);
len = list_len(l);
if (len <= 1)
@ -1519,7 +1647,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
else
l->lv_last = ptrs[i].item;
list_fix_watch(l, li);
listitem_free(li);
listitem_free(l, li);
l->lv_len--;
}
}
@ -1729,6 +1857,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map)
// set_vim_var_nr() doesn't set the type
set_vim_var_type(VV_KEY, VAR_NUMBER);
range_list_materialize(l);
for (li = l->lv_first; li != NULL; li = nli)
{
if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))

View File

@ -302,6 +302,10 @@
# endif
#endif
#ifdef FEAT_EVAL
# define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#endif
/*
* In a hashtab item "hi_key" points to "di_key" in a dictitem.
* This avoids adding a pointer to the hashtab item.

View File

@ -482,7 +482,7 @@ vim_main2(void)
# else
(char_u *)"plugin/**/*.vim",
# endif
DIP_ALL | DIP_NOAFTER);
DIP_ALL | DIP_NOAFTER, NULL);
TIME_MSG("loading plugins");
vim_free(rtp_copy);
@ -3169,7 +3169,7 @@ source_startup_scripts(mparm_T *parmp)
*/
if (parmp->evim_mode)
{
(void)do_source((char_u *)EVIM_FILE, FALSE, DOSO_NONE);
(void)do_source((char_u *)EVIM_FILE, FALSE, DOSO_NONE, NULL);
TIME_MSG("source evim file");
}
@ -3180,7 +3180,7 @@ source_startup_scripts(mparm_T *parmp)
if (parmp->use_vimrc != NULL)
{
if (STRCMP(parmp->use_vimrc, "DEFAULTS") == 0)
do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE);
do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, NULL);
else if (STRCMP(parmp->use_vimrc, "NONE") == 0
|| STRCMP(parmp->use_vimrc, "NORC") == 0)
{
@ -3191,7 +3191,7 @@ source_startup_scripts(mparm_T *parmp)
}
else
{
if (do_source(parmp->use_vimrc, FALSE, DOSO_NONE) != OK)
if (do_source(parmp->use_vimrc, FALSE, DOSO_NONE, NULL) != OK)
semsg(_("E282: Cannot read from \"%s\""), parmp->use_vimrc);
}
}
@ -3209,10 +3209,11 @@ source_startup_scripts(mparm_T *parmp)
* Get system wide defaults, if the file name is defined.
*/
#ifdef SYS_VIMRC_FILE
(void)do_source((char_u *)SYS_VIMRC_FILE, FALSE, DOSO_NONE);
(void)do_source((char_u *)SYS_VIMRC_FILE, FALSE, DOSO_NONE, NULL);
#endif
#ifdef MACOS_X
(void)do_source((char_u *)"$VIMRUNTIME/macmap.vim", FALSE, DOSO_NONE);
(void)do_source((char_u *)"$VIMRUNTIME/macmap.vim", FALSE,
DOSO_NONE, NULL);
#endif
/*
@ -3227,28 +3228,31 @@ source_startup_scripts(mparm_T *parmp)
*/
if (process_env((char_u *)"VIMINIT", TRUE) != OK)
{
if (do_source((char_u *)USR_VIMRC_FILE, TRUE, DOSO_VIMRC) == FAIL
if (do_source((char_u *)USR_VIMRC_FILE, TRUE,
DOSO_VIMRC, NULL) == FAIL
#ifdef USR_VIMRC_FILE2
&& do_source((char_u *)USR_VIMRC_FILE2, TRUE,
DOSO_VIMRC) == FAIL
DOSO_VIMRC, NULL) == FAIL
#endif
#ifdef USR_VIMRC_FILE3
&& do_source((char_u *)USR_VIMRC_FILE3, TRUE,
DOSO_VIMRC) == FAIL
DOSO_VIMRC, NULL) == FAIL
#endif
#ifdef USR_VIMRC_FILE4
&& do_source((char_u *)USR_VIMRC_FILE4, TRUE,
DOSO_VIMRC) == FAIL
DOSO_VIMRC, NULL) == FAIL
#endif
&& process_env((char_u *)"EXINIT", FALSE) == FAIL
&& do_source((char_u *)USR_EXRC_FILE, FALSE, DOSO_NONE) == FAIL
&& do_source((char_u *)USR_EXRC_FILE, FALSE,
DOSO_NONE, NULL) == FAIL
#ifdef USR_EXRC_FILE2
&& do_source((char_u *)USR_EXRC_FILE2, FALSE, DOSO_NONE) == FAIL
&& do_source((char_u *)USR_EXRC_FILE2, FALSE,
DOSO_NONE, NULL) == FAIL
#endif
&& !has_dash_c_arg)
{
// When no .vimrc file was found: source defaults.vim.
do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE);
do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, NULL);
}
}
@ -3285,7 +3289,7 @@ source_startup_scripts(mparm_T *parmp)
(char_u *)VIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
i = do_source((char_u *)VIMRC_FILE, TRUE, DOSO_VIMRC);
i = do_source((char_u *)VIMRC_FILE, TRUE, DOSO_VIMRC, NULL);
if (i == FAIL)
{
@ -3303,7 +3307,8 @@ source_startup_scripts(mparm_T *parmp)
(char_u *)EXRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
(void)do_source((char_u *)EXRC_FILE, FALSE, DOSO_NONE);
(void)do_source((char_u *)EXRC_FILE, FALSE,
DOSO_NONE, NULL);
}
}
if (secure == 2)
@ -3334,7 +3339,7 @@ main_start_gui(void)
#endif // NO_VIM_MAIN
/*
* Get an environment variable, and execute it as Ex commands.
* Get an environment variable and execute it as Ex commands.
* Returns FAIL if the environment variable was not executed, OK otherwise.
*/
int

View File

@ -846,6 +846,17 @@ emsg_invreg(int name)
semsg(_("E354: Invalid register name: '%s'"), transchar(name));
}
/*
* Give an error message which contains %s for "name[len]".
*/
void
emsg_namelen(char *msg, char_u *name, int len)
{
char_u *copy = vim_strnsave((char_u *)name, len);
semsg(msg, copy == NULL ? "NULL" : (char *)copy);
}
/*
* Like msg(), but truncate to a single line if p_shm contains 't', or when
* "force" is TRUE. This truncates in another way as for normal messages.

View File

@ -2067,12 +2067,16 @@ match_user(char_u *name)
concat_str(char_u *str1, char_u *str2)
{
char_u *dest;
size_t l = STRLEN(str1);
size_t l = str1 == NULL ? 0 : STRLEN(str1);
dest = alloc(l + STRLEN(str2) + 1L);
dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
if (dest != NULL)
{
if (str1 == NULL)
*dest = NUL;
else
STRCPY(dest, str1);
if (str2 != NULL)
STRCPY(dest + l, str2);
}
return dest;

View File

@ -226,6 +226,11 @@ void mbyte_im_set_active(int active_arg);
# include "usercmd.pro"
# include "userfunc.pro"
# include "version.pro"
# ifdef FEAT_EVAL
# include "vim9compile.pro"
# include "vim9execute.pro"
# include "vim9script.pro"
# endif
# include "window.pro"
# ifdef FEAT_LUA

View File

@ -2,7 +2,7 @@
blob_T *blob_alloc(void);
int rettv_blob_alloc(typval_T *rettv);
void rettv_blob_set(typval_T *rettv, blob_T *b);
int blob_copy(typval_T *from, typval_T *to);
int blob_copy(blob_T *from, typval_T *to);
void blob_free(blob_T *b);
void blob_unref(blob_T *b);
long blob_len(blob_T *b);

View File

@ -27,7 +27,12 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
int pattern_match(char_u *pat, char_u *text, int ic);
int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate);
int eval1(char_u **arg, typval_T *rettv, int evaluate);
void eval_addblob(typval_T *tv1, typval_T *tv2);
int eval_addlist(typval_T *tv1, typval_T *tv2);
int get_option_tv(char_u **arg, typval_T *rettv, int evaluate);
int get_number_tv(char_u **arg, typval_T *rettv, int evaluate, int want_string);
int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
char_u *partial_name(partial_T *pt);
void partial_unref(partial_T *pt);
int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
@ -43,6 +48,7 @@ char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
char_u *string_quote(char_u *str, int function);
int string2float(char_u *text, float_T *value);
int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum);
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp);
int get_env_len(char_u **arg);
@ -66,6 +72,7 @@ char_u *tv_get_string_chk(typval_T *varp);
char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf);
void copy_tv(typval_T *from, typval_T *to);
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
void ex_echo(exarg_T *eap);
void ex_echohl(exarg_T *eap);
int get_echo_attr(void);

View File

@ -1,8 +1,13 @@
/* evalfunc.c */
char_u *get_function_name(expand_T *xp, int idx);
char_u *get_expr_name(expand_T *xp, int idx);
int find_internal_func(char_u *name);
int has_internal_func(char_u *name);
char *internal_func_name(int idx);
type_T *internal_func_ret_type(int idx, int argcount);
int check_internal_func(int idx, int argcount);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
int non_zero_arg(typval_T *argvars);
linenr_T tv_get_lnum(typval_T *argvars);
@ -13,6 +18,7 @@ win_T *get_optional_window(typval_T *argvars, int idx);
void execute_redir_str(char_u *value, int value_len);
void execute_common(typval_T *argvars, typval_T *rettv, int arg_off);
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
void range_list_materialize(list_T *list);
float_T vim_round(float_T f);
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
void f_string(typval_T *argvars, typval_T *rettv);

View File

@ -13,10 +13,11 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr);
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);
void ex_let(exarg_T *eap);
void ex_const(exarg_T *eap);
int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int is_const, char_u *op);
char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon);
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);
void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
void ex_unlet(exarg_T *eap);
void ex_lockvar(exarg_T *eap);
@ -27,8 +28,10 @@ char *get_var_special_name(int nr);
dict_T *get_globvar_dict(void);
hashtab_T *get_globvar_ht(void);
dict_T *get_vimvar_dict(void);
int find_vim_var(char_u *name);
void set_vim_var_type(int idx, vartype_T type);
void set_vim_var_nr(int idx, varnumber_T val);
char *get_vim_var_name(int idx);
typval_T *get_vim_var_tv(int idx);
varnumber_T get_vim_var_nr(int idx);
char_u *get_vim_var_str(int idx);
@ -50,6 +53,8 @@ int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int ver
void check_vars(char_u *name, int len);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
hashtab_T *get_script_local_ht(void);
int lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);
@ -58,7 +63,7 @@ void unref_var_dict(dict_T *dict);
void vars_clear(hashtab_T *ht);
void vars_clear_ext(hashtab_T *ht, int free_val);
void set_var(char_u *name, typval_T *tv, int copy);
void set_var_const(char_u *name, typval_T *tv, int copy, int is_const);
void set_var_const(char_u *name, type_T *type, typval_T *tv, int copy, int flags);
int var_check_ro(int flags, char_u *name, int use_gettext);
int var_check_fixed(int flags, char_u *name, int use_gettext);
int var_check_func_name(char_u *name, int new_var);

View File

@ -7,6 +7,7 @@ void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie);
int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
int checkforcmd(char_u **pp, char *cmd, int len);
char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);

View File

@ -8,7 +8,9 @@ void free_global_msglist(void);
void do_errthrow(cstack_T *cstack, char_u *cmdname);
int do_intthrow(cstack_T *cstack);
char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int *should_free);
int throw_exception(void *value, except_type_T type, char_u *cmdname);
void discard_current_exception(void);
void catch_exception(except_T *excp);
void report_make_pending(int pending, void *value);
void ex_eval(exarg_T *eap);
void ex_if(exarg_T *eap);

View File

@ -3,6 +3,8 @@ void list_add_watch(list_T *l, listwatch_T *lw);
void list_rem_watch(list_T *l, listwatch_T *lwrem);
list_T *list_alloc(void);
list_T *list_alloc_id(alloc_id_T id);
list_T *list_alloc_with_items(int count);
void list_set_item(list_T *l, int idx, typval_T *tv);
int rettv_list_alloc(typval_T *rettv);
int rettv_list_alloc_id(typval_T *rettv, alloc_id_T id);
void rettv_list_set(typval_T *rettv, list_T *l);
@ -11,7 +13,8 @@ int list_free_nonref(int copyID);
void list_free_items(int copyID);
void list_free(list_T *l);
listitem_T *listitem_alloc(void);
void listitem_free(listitem_T *item);
void list_free_item(list_T *l, listitem_T *item);
void listitem_free(list_T *l, listitem_T *item);
void listitem_remove(list_T *l, listitem_T *item);
long list_len(list_T *l);
int list_equal(list_T *l1, list_T *l2, int ic, int recursive);
@ -21,6 +24,7 @@ char_u *list_find_str(list_T *l, long idx);
long list_idx_of_item(list_T *l, listitem_T *item);
void list_append(list_T *l, listitem_T *item);
int list_append_tv(list_T *l, typval_T *tv);
int list_append_tv_move(list_T *l, typval_T *tv);
int list_append_dict(list_T *list, dict_T *dict);
int list_append_list(list_T *list1, list_T *list2);
int list_append_string(list_T *l, char_u *str, int len);
@ -34,7 +38,7 @@ void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID);
void f_join(typval_T *argvars, typval_T *rettv);
int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
int get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error);
int write_list(FILE *fd, list_T *list, int binary);
void init_static_list(staticList10_T *sl);
void f_list2str(typval_T *argvars, typval_T *rettv);

View File

@ -13,6 +13,7 @@ int emsg(char *s);
void iemsg(char *s);
void internal_error(char *where);
void emsg_invreg(int name);
void emsg_namelen(char *msg, char_u *name, int len);
char *msg_trunc_attr(char *s, int force, int attr);
char_u *msg_may_trunc(int force, char_u *s);
int delete_first_msg(void);

View File

@ -8,7 +8,7 @@ void ex_runtime(exarg_T *eap);
int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
int source_runtime(char_u *name, int flags);
int source_in_path(char_u *path, char_u *name, int flags);
int source_in_path(char_u *path, char_u *name, int flags, int *ret_sid);
void add_pack_start_dirs(void);
void load_start_packages(void);
void ex_packloadall(exarg_T *eap);
@ -21,7 +21,7 @@ void ex_options(exarg_T *eap);
linenr_T *source_breakpoint(void *cookie);
int *source_dbg_tick(void *cookie);
int source_level(void *cookie);
int do_source(char_u *fname, int check_other, int is_vimrc);
int do_source(char_u *fname, int check_other, int is_vimrc, int *ret_sid);
void ex_scriptnames(exarg_T *eap);
void scriptnames_slash_adjust(void);
char_u *get_scriptname(scid_T id);

View File

@ -1,26 +1,32 @@
/* userfunc.c */
void func_init(void);
hashtab_T *func_tbl_get(void);
int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T *argtypes, int *varargs, garray_T *default_args, int skip);
int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe);
ufunc_T *find_func(char_u *name);
ufunc_T *find_func(char_u *name, cctx_T *cctx);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
void restore_funccal(void);
funccall_T *get_current_funccal(void);
void free_all_functions(void);
int builtin_function(char_u *name, int len);
int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
int get_callback_depth(void);
int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars);
void user_func_error(int error, char_u *name);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial);
void ex_function(exarg_T *eap);
int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name);
int has_varargs(ufunc_T *ufunc);
int function_exists(char_u *name, int no_deref);
char_u *get_expanded_name(char_u *name, int check);
char_u *get_user_func_name(expand_T *xp, int idx);
void clean_script_functions(int sid);
void ex_delfunction(exarg_T *eap);
void func_unref(char_u *name);
void func_ptr_unref(ufunc_T *fp);

14
src/proto/vim9compile.pro Normal file
View File

@ -0,0 +1,14 @@
/* vim9compile.c */
char_u *skip_type(char_u *start);
type_T *parse_type(char_u **arg, garray_T *type_list);
char *vartype_name(vartype_T type);
char *type_name(type_T *type, char **tofree);
int get_script_item_idx(int sid, char_u *name, int check_writable);
imported_T *find_imported(char_u *name, cctx_T *cctx);
char_u *to_name_end(char_u *arg);
char_u *to_name_const_end(char_u *arg);
int assignment_len(char_u *p, int *heredoc);
void compile_def_function(ufunc_T *ufunc, int set_return_type);
void delete_def_function(ufunc_T *ufunc);
void free_def_functions(void);
/* vim: set ft=c : */

View File

@ -0,0 +1,6 @@
/* vim9execute.c */
int call_def_function(ufunc_T *ufunc, int argc, typval_T *argv, typval_T *rettv);
void ex_disassemble(exarg_T *eap);
int tv2bool(typval_T *tv);
int check_not_string(typval_T *tv);
/* vim: set ft=c : */

8
src/proto/vim9script.pro Normal file
View File

@ -0,0 +1,8 @@
/* vim9script.c */
int in_vim9script(void);
void ex_vim9script(exarg_T *eap);
void ex_export(exarg_T *eap);
void free_imports(int sid);
void ex_import(exarg_T *eap);
char_u *handle_import(char_u *arg_start, garray_T *gap, int sid);
/* vim: set ft=c : */

View File

@ -182,9 +182,9 @@ ex_runtime(exarg_T *eap)
}
static void
source_callback(char_u *fname, void *cookie UNUSED)
source_callback(char_u *fname, void *cookie)
{
(void)do_source(fname, FALSE, DOSO_NONE);
(void)do_source(fname, FALSE, DOSO_NONE, cookie);
}
/*
@ -399,16 +399,16 @@ do_in_runtimepath(
int
source_runtime(char_u *name, int flags)
{
return source_in_path(p_rtp, name, flags);
return source_in_path(p_rtp, name, flags, NULL);
}
/*
* Just like source_runtime(), but use "path" instead of 'runtimepath'.
*/
int
source_in_path(char_u *path, char_u *name, int flags)
source_in_path(char_u *path, char_u *name, int flags, int *ret_sid)
{
return do_in_path_and_pp(path, name, flags, source_callback, NULL);
return do_in_path_and_pp(path, name, flags, source_callback, ret_sid);
}
@ -427,7 +427,7 @@ source_all_matches(char_u *pat)
if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
{
for (i = 0; i < num_files; ++i)
(void)do_source(files[i], FALSE, DOSO_NONE);
(void)do_source(files[i], FALSE, DOSO_NONE, NULL);
FreeWild(num_files, files);
}
}
@ -930,7 +930,7 @@ cmd_source(char_u *fname, exarg_T *eap)
);
// ":source" read ex commands
else if (do_source(fname, FALSE, DOSO_NONE) == FAIL)
else if (do_source(fname, FALSE, DOSO_NONE, NULL) == FAIL)
semsg(_(e_notopen), fname);
}
@ -1063,16 +1063,20 @@ fopen_noinh_readbin(char *filename)
/*
* do_source: Read the file "fname" and execute its lines as EX commands.
* When "ret_sid" is not NULL and we loaded the script before, don't load it
* again.
*
* This function may be called recursively!
*
* return FAIL if file could not be opened, OK otherwise
* Return FAIL if file could not be opened, OK otherwise.
* If a scriptitem_T was found or created "*ret_sid" is set to the SID.
*/
int
do_source(
char_u *fname,
int check_other, // check for .vimrc and _vimrc
int is_vimrc) // DOSO_ value
int is_vimrc, // DOSO_ value
int *ret_sid UNUSED)
{
struct source_cookie cookie;
char_u *p;
@ -1085,6 +1089,7 @@ do_source(
static int last_current_SID_seq = 0;
funccal_entry_T funccalp_entry;
int save_debug_break_level = debug_break_level;
int sid;
scriptitem_T *si = NULL;
# ifdef UNIX
stat_T st;
@ -1114,6 +1119,37 @@ do_source(
goto theend;
}
#ifdef FEAT_EVAL
// See if we loaded this script before.
# ifdef UNIX
stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
# endif
for (sid = script_items.ga_len; sid > 0; --sid)
{
si = &SCRIPT_ITEM(sid);
if (si->sn_name != NULL)
{
# ifdef UNIX
// Compare dev/ino when possible, it catches symbolic links. Also
// compare file names, the inode may change when the file was
// edited or it may be re-used for another script (esp. in tests).
if ((stat_ok && si->sn_dev_valid)
&& (si->sn_dev != st.st_dev || si->sn_ino != st.st_ino))
continue;
# endif
if (fnamecmp(si->sn_name, fname_exp) == 0)
// Found it!
break;
}
}
if (sid > 0 && ret_sid != NULL)
{
// Already loaded and no need to load again, return here.
*ret_sid = sid;
return OK;
}
#endif
// Apply SourceCmd autocommands, they should get the file and source it.
if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
&& apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
@ -1239,33 +1275,32 @@ do_source(
current_sctx.sc_version = 1; // default script version
// Check if this script was sourced before to finds its SID.
// If it's new, generate a new SID.
// Always use a new sequence number.
current_sctx.sc_seq = ++last_current_SID_seq;
# ifdef UNIX
stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
# endif
for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
--current_sctx.sc_sid)
if (sid > 0)
{
si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_name != NULL)
hashtab_T *ht;
hashitem_T *hi;
dictitem_T *di;
int todo;
// loading the same script again
si->sn_had_command = FALSE;
current_sctx.sc_sid = sid;
ht = &SCRIPT_VARS(sid);
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0; ++hi)
if (!HASHITEM_EMPTY(hi))
{
# ifdef UNIX
// Compare dev/ino when possible, it catches symbolic links. Also
// compare file names, the inode may change when the file was
// edited or it may be re-used for another script (esp. in tests).
if ((stat_ok && si->sn_dev_valid)
&& (si->sn_dev != st.st_dev || si->sn_ino != st.st_ino))
continue;
# endif
if (fnamecmp(si->sn_name, fname_exp) == 0)
// Found it!
break;
--todo;
di = HI2DI(hi);
di->di_flags |= DI_FLAGS_RELOAD;
}
}
if (current_sctx.sc_sid == 0)
else
{
// It's new, generate a new SID.
current_sctx.sc_sid = ++last_current_SID;
if (ga_grow(&script_items,
(int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL)
@ -1273,13 +1308,17 @@ do_source(
while (script_items.ga_len < current_sctx.sc_sid)
{
++script_items.ga_len;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
SCRIPT_ITEM(script_items.ga_len).sn_version = 1;
si = &SCRIPT_ITEM(script_items.ga_len);
si->sn_name = NULL;
si->sn_version = 1;
// Allocate the local script variables to use for this script.
new_script_vars(script_items.ga_len);
ga_init2(&si->sn_var_vals, sizeof(typval_T), 10);
ga_init2(&si->sn_imports, sizeof(imported_T), 10);
ga_init2(&si->sn_type_list, sizeof(type_T), 10);
# ifdef FEAT_PROFILE
SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
si->sn_prof_on = FALSE;
# endif
}
si = &SCRIPT_ITEM(current_sctx.sc_sid);
@ -1295,6 +1334,8 @@ do_source(
else
si->sn_dev_valid = FALSE;
# endif
if (ret_sid != NULL)
*ret_sid = current_sctx.sc_sid;
}
# ifdef FEAT_PROFILE
@ -1392,6 +1433,15 @@ do_source(
#ifdef FEAT_EVAL
almosttheend:
// Get "si" again, "script_items" may have been reallocated.
si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_save_cpo != NULL)
{
free_string_option(p_cpo);
p_cpo = si->sn_save_cpo;
si->sn_save_cpo = NULL;
}
current_sctx = save_current_sctx;
restore_funccal();
# ifdef FEAT_PROFILE
@ -1488,7 +1538,9 @@ free_scriptnames(void)
{
// the variables themselves are cleared in evalvars_clear()
vim_free(SCRIPT_ITEM(i).sn_vars);
vim_free(SCRIPT_ITEM(i).sn_name);
free_string_option(SCRIPT_ITEM(i).sn_save_cpo);
# ifdef FEAT_PROFILE
ga_clear(&SCRIPT_ITEM(i).sn_prl_ga);
# endif
@ -1789,6 +1841,11 @@ ex_scriptversion(exarg_T *eap UNUSED)
emsg(_("E984: :scriptversion used outside of a sourced file"));
return;
}
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
emsg(_("E1040: Cannot use :scriptversion after :vim9script"));
return;
}
nr = getdigits(&eap->arg);
if (nr == 0 || *eap->arg != NUL)

View File

@ -980,7 +980,7 @@ ex_loadview(exarg_T *eap)
fname = get_view_file(*eap->arg);
if (fname != NULL)
{
do_source(fname, FALSE, DOSO_NONE);
do_source(fname, FALSE, DOSO_NONE, NULL);
vim_free(fname);
}
}

View File

@ -67,6 +67,9 @@ typedef struct terminal_S term_T;
typedef struct VimMenu vimmenu_T;
#endif
// value for sc_version in a Vim9 script file
#define SCRIPT_VERSION_VIM9 999999
/*
* SCript ConteXt (SCTX): identifies a script line.
* When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current
@ -1298,30 +1301,43 @@ typedef struct {
int cb_free_name; // cb_name was allocated
} callback_T;
typedef struct dfunc_S dfunc_T; // :def function
typedef struct jobvar_S job_T;
typedef struct readq_S readq_T;
typedef struct writeq_S writeq_T;
typedef struct jsonq_S jsonq_T;
typedef struct cbq_S cbq_T;
typedef struct channel_S channel_T;
typedef struct cctx_S cctx_T;
typedef enum
{
VAR_UNKNOWN = 0,
VAR_UNKNOWN = 0, // not set, also used for "any" type
VAR_VOID, // no value
VAR_BOOL, // "v_number" is used: VVAL_TRUE or VVAL_FALSE
VAR_SPECIAL, // "v_number" is used: VVAL_NULL or VVAL_NONE
VAR_NUMBER, // "v_number" is used
VAR_FLOAT, // "v_float" is used
VAR_STRING, // "v_string" is used
VAR_BLOB, // "v_blob" is used
VAR_FUNC, // "v_string" is function name
VAR_PARTIAL, // "v_partial" is used
VAR_LIST, // "v_list" is used
VAR_DICT, // "v_dict" is used
VAR_FLOAT, // "v_float" is used
VAR_BOOL, // "v_number" is VVAL_FALSE or VVAL_TRUE
VAR_SPECIAL, // "v_number" is VVAL_NONE or VVAL_NULL
VAR_JOB, // "v_job" is used
VAR_CHANNEL, // "v_channel" is used
VAR_BLOB, // "v_blob" is used
} vartype_T;
// A type specification.
typedef struct type_S type_T;
struct type_S {
vartype_T tt_type;
short tt_argcount; // for func, partial, -1 for unknown
type_T *tt_member; // for list, dict, func return type
type_T *tt_args; // func arguments
};
/*
* Structure to hold an internal variable without a name.
*/
@ -1380,19 +1396,34 @@ struct listwatch_S
/*
* Structure to hold info about a list.
* Order of members is optimized to reduce padding.
* When created by range() it will at first have special value:
* lv_first == &range_list_item;
* and use lv_start, lv_end, lv_stride.
*/
struct listvar_S
{
listitem_T *lv_first; // first item, NULL if none
listitem_T *lv_last; // last item, NULL if none
listwatch_T *lv_watch; // first watcher, NULL if none
union {
struct { // used for non-materialized range list:
// "lv_first" is &range_list_item
varnumber_T lv_start;
varnumber_T lv_end;
int lv_stride;
};
struct { // used for materialized list
listitem_T *lv_last; // last item, NULL if none
listitem_T *lv_idx_item; // when not NULL item at index "lv_idx"
int lv_idx; // cached index of an item
};
};
list_T *lv_copylist; // copied list used by deepcopy()
list_T *lv_used_next; // next list in used lists list
list_T *lv_used_prev; // previous list in used lists list
int lv_refcount; // reference count
int lv_len; // number of items
int lv_idx; // cached index of an item
int lv_with_items; // number of items following this struct that
// should not be freed
int lv_copyID; // ID used by deepcopy()
char lv_lock; // zero, VAR_LOCKED, VAR_FIXED
};
@ -1413,7 +1444,7 @@ typedef struct {
struct dictitem_S
{
typval_T di_tv; // type and value of the variable
char_u di_flags; // flags (only used for variable)
char_u di_flags; // DI_FLAGS_ flags (only used for variable)
char_u di_key[1]; // key (actually longer!)
};
typedef struct dictitem_S dictitem_T;
@ -1426,16 +1457,18 @@ typedef struct dictitem_S dictitem_T;
struct dictitem16_S
{
typval_T di_tv; // type and value of the variable
char_u di_flags; // flags (only used for variable)
char_u di_flags; // DI_FLAGS_ flags (only used for variable)
char_u di_key[DICTITEM16_KEY_LEN + 1]; // key
};
typedef struct dictitem16_S dictitem16_T;
#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable
#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox
#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove()
#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable
#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated
// Flags for "di_flags"
#define DI_FLAGS_RO 0x01 // read-only variable
#define DI_FLAGS_RO_SBX 0x02 // read-only in the sandbox
#define DI_FLAGS_FIX 0x04 // fixed: no :unlet or remove()
#define DI_FLAGS_LOCK 0x08 // locked variable
#define DI_FLAGS_ALLOC 0x10 // separately allocated
#define DI_FLAGS_RELOAD 0x20 // set when script sourced again
/*
* Structure to hold info about a Dictionary.
@ -1470,12 +1503,21 @@ typedef struct funccall_S funccall_T;
*/
typedef struct
{
int uf_varargs; // variable nr of arguments
int uf_flags;
int uf_varargs; // variable nr of arguments (old style)
int uf_flags; // FC_ flags
int uf_calls; // nr of active calls
int uf_cleared; // func_clear() was already called
int uf_dfunc_idx; // >= 0 for :def function only
garray_T uf_args; // arguments
garray_T uf_def_args; // default argument expressions
// for :def (for :function uf_ret_type is NULL)
type_T **uf_arg_types; // argument types (count == uf_args.ga_len)
type_T *uf_ret_type; // return type
garray_T uf_type_list; // types used in arg and return types
char_u *uf_va_name; // name from "...name" or NULL
type_T *uf_va_type; // type from "...name: type" or NULL
garray_T uf_lines; // function lines
# ifdef FEAT_PROFILE
int uf_profiling; // TRUE when func is being profiled
@ -1577,6 +1619,32 @@ typedef struct
dict_T sv_dict;
} scriptvar_T;
/*
* Entry for "sn_var_vals". Used for script-local variables.
*/
typedef struct {
char_u *sv_name; // points into "sn_vars" di_key
typval_T *sv_tv; // points into "sn_vars" di_tv
type_T *sv_type;
int sv_const;
int sv_export; // "export let var = val"
} svar_T;
typedef struct {
char_u *imp_name; // name imported as (allocated)
int imp_sid; // script ID of "from"
// for "import * as Name", "imp_name" is "Name"
int imp_all;
// for variable
type_T *imp_type;
int imp_var_vals_idx; // index in sn_var_vals of "from"
// for function
char_u *imp_funcname; // user func name (NOT allocated)
} imported_T;
/*
* Growarray to store info about already sourced scripts.
* For Unix also store the dev/ino, so that we don't have to stat() each
@ -1584,11 +1652,18 @@ typedef struct
*/
typedef struct
{
scriptvar_T *sn_vars; // stores s: variables for this script
char_u *sn_name;
scriptvar_T *sn_vars; // stores s: variables for this script
garray_T sn_var_vals; // same variables as a list of svar_T
garray_T sn_imports; // imported items, imported_T
garray_T sn_type_list; // keeps types used by variables
int sn_version; // :scriptversion
int sn_had_command; // TRUE if any command was executed
char_u *sn_save_cpo; // 'cpo' value when :vim9script found
# ifdef UNIX
int sn_dev_valid;
@ -3691,6 +3766,12 @@ typedef enum
EXPR_NOMATCH, // !~
EXPR_IS, // is
EXPR_ISNOT, // isnot
// used with ISN_OPNR
EXPR_ADD, // +
EXPR_SUB, // -
EXPR_MULT, // *
EXPR_DIV, // /
EXPR_REM, // %
} exptype_T;
/*
@ -3804,6 +3885,8 @@ typedef struct
typedef struct lval_S
{
char_u *ll_name; // start of variable name (can be NULL)
char_u *ll_name_end; // end of variable name (can be NULL)
type_T *ll_type; // type of variable (can be NULL)
char_u *ll_exp_name; // NULL or expanded name in allocated memory.
typval_T *ll_tv; // Typeval of item being used. If "newkey"
// isn't NULL it's the Dict to which to add

View File

@ -4736,7 +4736,7 @@ syn_cmd_include(exarg_T *eap, int syncing UNUSED)
current_syn_inc_tag = ++running_syn_inc_tag;
prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
curwin->w_s->b_syn_topgrp = sgl_id;
if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
: source_runtime(eap->arg, DIP_ALL) == FAIL)
semsg(_(e_notopen), eap->arg);
curwin->w_s->b_syn_topgrp = prev_toplvl_grp;

View File

@ -90,10 +90,10 @@ NEW_TESTS = \
test_digraph \
test_display \
test_edit \
test_environ \
test_erasebackword \
test_escaped_glob \
test_eval_stuff \
test_environ \
test_ex_equal \
test_ex_undo \
test_ex_z \
@ -268,6 +268,8 @@ NEW_TESTS = \
test_utf8 \
test_utf8_comparisons \
test_vartabs \
test_vim9_expr \
test_vim9_script \
test_viminfo \
test_vimscript \
test_virtualedit \
@ -435,6 +437,8 @@ NEW_TESTS_RES = \
test_user_func.res \
test_usercommands.res \
test_vartabs.res \
test_vim9_expr.res \
test_vim9_script.res \
test_viminfo.res \
test_vimscript.res \
test_visual.res \

View File

@ -0,0 +1,734 @@
" Tests for Vim9 script expressions
source check.vim
" Check that "line" inside ":def" results in an "error" message.
func CheckDefFailure(line, error)
call writefile(['def! Func()', a:line, 'enddef'], 'Xdef')
call assert_fails('so Xdef', a:error, a:line)
call delete('Xdef')
endfunc
func CheckDefFailureList(lines, error)
call writefile(['def! Func()'] + a:lines + ['enddef'], 'Xdef')
call assert_fails('so Xdef', a:error, string(a:lines))
call delete('Xdef')
endfunc
" test cond ? expr : expr
def Test_expr1()
assert_equal('one', true ? 'one' : 'two')
assert_equal('one', 1 ? 'one' : 'two')
assert_equal('one', 0.1 ? 'one' : 'two')
assert_equal('one', 'x' ? 'one' : 'two')
" assert_equal('one', 0z1234 ? 'one' : 'two')
assert_equal('one', [0] ? 'one' : 'two')
" assert_equal('one', #{x: 0} ? 'one' : 'two')
let var = 1
assert_equal('one', var ? 'one' : 'two')
assert_equal('two', false ? 'one' : 'two')
assert_equal('two', 0 ? 'one' : 'two')
assert_equal('two', 0.0 ? 'one' : 'two')
assert_equal('two', '' ? 'one' : 'two')
" assert_equal('one', 0z ? 'one' : 'two')
assert_equal('two', [] ? 'one' : 'two')
" assert_equal('two', {} ? 'one' : 'two')
var = 0
assert_equal('two', var ? 'one' : 'two')
enddef
func Test_expr1_fails()
call CheckDefFailure("let x = 1 ? 'one'", "Missing ':' after '?'")
let msg = "white space required before and after '?'"
call CheckDefFailure("let x = 1? 'one' : 'two'", msg)
call CheckDefFailure("let x = 1 ?'one' : 'two'", msg)
call CheckDefFailure("let x = 1?'one' : 'two'", msg)
let msg = "white space required before and after ':'"
call CheckDefFailure("let x = 1 ? 'one': 'two'", msg)
call CheckDefFailure("let x = 1 ? 'one' :'two'", msg)
call CheckDefFailure("let x = 1 ? 'one':'two'", msg)
endfunc
" TODO: define inside test function
def Record(val: any): any
g:vals->add(val)
return val
enddef
" test ||
def Test_expr2()
assert_equal(2, 2 || 0)
assert_equal(7, 0 || 0 || 7)
assert_equal(0, 0 || 0)
assert_equal('', 0 || '')
g:vals = []
assert_equal(3, Record(3) || Record(1))
assert_equal([3], g:vals)
g:vals = []
assert_equal(5, Record(0) || Record(5))
assert_equal([0, 5], g:vals)
g:vals = []
assert_equal(4, Record(0) || Record(4) || Record(0))
assert_equal([0, 4], g:vals)
g:vals = []
assert_equal(0, Record([]) || Record('') || Record(0))
assert_equal([[], '', 0], g:vals)
enddef
func Test_expr2_fails()
let msg = "white space required before and after '||'"
call CheckDefFailure("let x = 1||2", msg)
call CheckDefFailure("let x = 1 ||2", msg)
call CheckDefFailure("let x = 1|| 2", msg)
endfunc
" test &&
def Test_expr3()
assert_equal(0, 2 && 0)
assert_equal(0, 0 && 0 && 7)
assert_equal(7, 2 && 3 && 7)
assert_equal(0, 0 && 0)
assert_equal(0, 0 && '')
assert_equal('', 8 && '')
g:vals = []
assert_equal(1, Record(3) && Record(1))
assert_equal([3, 1], g:vals)
g:vals = []
assert_equal(0, Record(0) && Record(5))
assert_equal([0], g:vals)
g:vals = []
assert_equal(0, Record(0) && Record(4) && Record(0))
assert_equal([0], g:vals)
g:vals = []
assert_equal(0, Record(8) && Record(4) && Record(0))
assert_equal([8, 4, 0], g:vals)
g:vals = []
assert_equal(0, Record([1]) && Record('z') && Record(0))
assert_equal([[1], 'z', 0], g:vals)
enddef
func Test_expr3_fails()
let msg = "white space required before and after '&&'"
call CheckDefFailure("let x = 1&&2", msg)
call CheckDefFailure("let x = 1 &&2", msg)
call CheckDefFailure("let x = 1&& 2", msg)
endfunc
let atrue = v:true
let afalse = v:false
let anone = v:none
let anull = v:null
let anint = 10
let alsoint = 4
if has('float')
let afloat = 0.1
endif
let astring = 'asdf'
let ablob = 0z01ab
let alist = [2, 3, 4]
let adict = #{aaa: 2, bbb: 8}
" test == comperator
def Test_expr4_equal()
assert_equal(true, true == true)
assert_equal(false, true == false)
assert_equal(true, true == g:atrue)
assert_equal(false, g:atrue == false)
assert_equal(true, v:none == v:none)
assert_equal(false, v:none == v:null)
assert_equal(true, g:anone == v:none)
assert_equal(false, v:none == g:anull)
assert_equal(false, 2 == 0)
assert_equal(true, 61 == 61)
assert_equal(true, g:anint == 10)
assert_equal(false, 61 == g:anint)
if has('float')
assert_equal(true, 0.3 == 0.3)
assert_equal(false, 0.4 == 0.3)
assert_equal(true, 0.1 == g:afloat)
assert_equal(false, g:afloat == 0.3)
assert_equal(true, 3.0 == 3)
assert_equal(true, 3 == 3.0)
assert_equal(false, 3.1 == 3)
assert_equal(false, 3 == 3.1)
endif
assert_equal(true, 'abc' == 'abc')
assert_equal(false, 'xyz' == 'abc')
assert_equal(true, g:astring == 'asdf')
assert_equal(false, 'xyz' == g:astring)
assert_equal(false, 'abc' == 'ABC')
set ignorecase
assert_equal(false, 'abc' == 'ABC')
set noignorecase
assert_equal(true, 0z3f == 0z3f)
assert_equal(false, 0z3f == 0z4f)
assert_equal(true, g:ablob == 0z01ab)
assert_equal(false, 0z3f == g:ablob)
assert_equal(true, [1, 2, 3] == [1, 2, 3])
assert_equal(false, [1, 2, 3] == [2, 3, 1])
assert_equal(true, [2, 3, 4] == g:alist)
assert_equal(false, g:alist == [2, 3, 1])
assert_equal(false, [1, 2, 3] == [])
assert_equal(false, [1, 2, 3] == ['1', '2', '3'])
assert_equal(true, #{one: 1, two: 2} == #{one: 1, two: 2})
assert_equal(false, #{one: 1, two: 2} == #{one: 2, two: 2})
assert_equal(false, #{one: 1, two: 2} == #{two: 2})
assert_equal(false, #{one: 1, two: 2} == #{})
assert_equal(true, g:adict == #{bbb: 8, aaa: 2})
assert_equal(false, #{ccc: 9, aaa: 2} == g:adict)
assert_equal(true, function('Test_expr4_equal') == function('Test_expr4_equal'))
assert_equal(false, function('Test_expr4_equal') == function('Test_expr4_is'))
assert_equal(true, function('Test_expr4_equal', [123]) == function('Test_expr4_equal', [123]))
assert_equal(false, function('Test_expr4_equal', [123]) == function('Test_expr4_is', [123]))
assert_equal(false, function('Test_expr4_equal', [123]) == function('Test_expr4_equal', [999]))
enddef
" test != comperator
def Test_expr4_notequal()
assert_equal(false, true != true)
assert_equal(true, true != false)
assert_equal(false, true != g:atrue)
assert_equal(true, g:atrue != false)
assert_equal(false, v:none != v:none)
assert_equal(true, v:none != v:null)
assert_equal(false, g:anone != v:none)
assert_equal(true, v:none != g:anull)
assert_equal(true, 2 != 0)
assert_equal(false, 55 != 55)
assert_equal(false, g:anint != 10)
assert_equal(true, 61 != g:anint)
if has('float')
assert_equal(false, 0.3 != 0.3)
assert_equal(true, 0.4 != 0.3)
assert_equal(false, 0.1 != g:afloat)
assert_equal(true, g:afloat != 0.3)
assert_equal(false, 3.0 != 3)
assert_equal(false, 3 != 3.0)
assert_equal(true, 3.1 != 3)
assert_equal(true, 3 != 3.1)
endif
assert_equal(false, 'abc' != 'abc')
assert_equal(true, 'xyz' != 'abc')
assert_equal(false, g:astring != 'asdf')
assert_equal(true, 'xyz' != g:astring)
assert_equal(true, 'abc' != 'ABC')
set ignorecase
assert_equal(true, 'abc' != 'ABC')
set noignorecase
assert_equal(false, 0z3f != 0z3f)
assert_equal(true, 0z3f != 0z4f)
assert_equal(false, g:ablob != 0z01ab)
assert_equal(true, 0z3f != g:ablob)
assert_equal(false, [1, 2, 3] != [1, 2, 3])
assert_equal(true, [1, 2, 3] != [2, 3, 1])
assert_equal(false, [2, 3, 4] != g:alist)
assert_equal(true, g:alist != [2, 3, 1])
assert_equal(true, [1, 2, 3] != [])
assert_equal(true, [1, 2, 3] != ['1', '2', '3'])
assert_equal(false, #{one: 1, two: 2} != #{one: 1, two: 2})
assert_equal(true, #{one: 1, two: 2} != #{one: 2, two: 2})
assert_equal(true, #{one: 1, two: 2} != #{two: 2})
assert_equal(true, #{one: 1, two: 2} != #{})
assert_equal(false, g:adict != #{bbb: 8, aaa: 2})
assert_equal(true, #{ccc: 9, aaa: 2} != g:adict)
assert_equal(false, function('Test_expr4_equal') != function('Test_expr4_equal'))
assert_equal(true, function('Test_expr4_equal') != function('Test_expr4_is'))
assert_equal(false, function('Test_expr4_equal', [123]) != function('Test_expr4_equal', [123]))
assert_equal(true, function('Test_expr4_equal', [123]) != function('Test_expr4_is', [123]))
assert_equal(true, function('Test_expr4_equal', [123]) != function('Test_expr4_equal', [999]))
enddef
" test > comperator
def Test_expr4_greater()
assert_equal(true, 2 > 0)
assert_equal(true, 2 > 1)
assert_equal(false, 2 > 2)
assert_equal(false, 2 > 3)
enddef
" test >= comperator
def Test_expr4_greaterequal()
assert_equal(true, 2 >= 0)
assert_equal(true, 2 >= 2)
assert_equal(false, 2 >= 3)
enddef
" test < comperator
def Test_expr4_smaller()
assert_equal(false, 2 < 0)
assert_equal(false, 2 < 2)
assert_equal(true, 2 < 3)
enddef
" test <= comperator
def Test_expr4_smallerequal()
assert_equal(false, 2 <= 0)
assert_equal(false, 2 <= 1)
assert_equal(true, 2 <= 2)
assert_equal(true, 2 <= 3)
enddef
" test =~ comperator
def Test_expr4_match()
assert_equal(false, '2' =~ '0')
assert_equal(true, '2' =~ '[0-9]')
enddef
" test !~ comperator
def Test_expr4_nomatch()
assert_equal(true, '2' !~ '0')
assert_equal(false, '2' !~ '[0-9]')
enddef
" test is comperator
def Test_expr4_is()
let mylist = [2]
assert_equal(false, mylist is [2])
let other = mylist
assert_equal(true, mylist is other)
enddef
" test isnot comperator
def Test_expr4_isnot()
let mylist = [2]
assert_equal(true, '2' isnot '0')
assert_equal(true, mylist isnot [2])
let other = mylist
assert_equal(false, mylist isnot other)
enddef
def RetVoid()
let x = 1
enddef
func Test_expr4_fails()
let msg = "white space required before and after '>'"
call CheckDefFailure("let x = 1>2", msg)
call CheckDefFailure("let x = 1 >2", msg)
call CheckDefFailure("let x = 1> 2", msg)
let msg = "white space required before and after '=='"
call CheckDefFailure("let x = 1==2", msg)
call CheckDefFailure("let x = 1 ==2", msg)
call CheckDefFailure("let x = 1== 2", msg)
let msg = "white space required before and after 'is'"
call CheckDefFailure("let x = '1'is'2'", msg)
call CheckDefFailure("let x = '1' is'2'", msg)
call CheckDefFailure("let x = '1'is '2'", msg)
let msg = "white space required before and after 'isnot'"
call CheckDefFailure("let x = '1'isnot'2'", msg)
call CheckDefFailure("let x = '1' isnot'2'", msg)
call CheckDefFailure("let x = '1'isnot '2'", msg)
call CheckDefFailure("let x = 1 is# 2", 'E15:')
call CheckDefFailure("let x = 1 is? 2", 'E15:')
call CheckDefFailure("let x = 1 isnot# 2", 'E15:')
call CheckDefFailure("let x = 1 isnot? 2", 'E15:')
call CheckDefFailure("let x = 1 == '2'", 'Cannot compare number with string')
call CheckDefFailure("let x = '1' == 2", 'Cannot compare string with number')
call CheckDefFailure("let x = 1 == RetVoid()", 'Cannot use void value')
call CheckDefFailure("let x = RetVoid() == 1", 'Cannot compare void with number')
call CheckDefFailure("let x = true > false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true >= false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true < false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true <= false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true =~ false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true !~ false", 'Cannot compare bool with bool')
call CheckDefFailure("let x = true is false", 'Cannot use "is" with bool')
call CheckDefFailure("let x = true isnot false", 'Cannot use "isnot" with bool')
call CheckDefFailure("let x = v:none is v:null", 'Cannot use "is" with special')
call CheckDefFailure("let x = v:none isnot v:null", 'Cannot use "isnot" with special')
call CheckDefFailure("let x = 123 is 123", 'Cannot use "is" with number')
call CheckDefFailure("let x = 123 isnot 123", 'Cannot use "isnot" with number')
if has('float')
call CheckDefFailure("let x = 1.3 is 1.3", 'Cannot use "is" with float')
call CheckDefFailure("let x = 1.3 isnot 1.3", 'Cannot use "isnot" with float')
endif
call CheckDefFailure("let x = 0za1 > 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = 0za1 >= 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = 0za1 < 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = 0za1 <= 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = 0za1 =~ 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = 0za1 !~ 0z34", 'Cannot compare blob with blob')
call CheckDefFailure("let x = [13] > [88]", 'Cannot compare list with list')
call CheckDefFailure("let x = [13] >= [88]", 'Cannot compare list with list')
call CheckDefFailure("let x = [13] < [88]", 'Cannot compare list with list')
call CheckDefFailure("let x = [13] <= [88]", 'Cannot compare list with list')
call CheckDefFailure("let x = [13] =~ [88]", 'Cannot compare list with list')
call CheckDefFailure("let x = [13] !~ [88]", 'Cannot compare list with list')
endfunc
" test addition, subtraction, concatenation
def Test_expr5()
assert_equal(66, 60 + 6)
assert_equal(70, 60 + g:anint)
assert_equal(9, g:alsoint + 5)
assert_equal(14, g:alsoint + g:anint)
assert_equal(54, 60 - 6)
assert_equal(50, 60 - g:anint)
assert_equal(-1, g:alsoint - 5)
assert_equal(-6, g:alsoint - g:anint)
assert_equal('hello', 'hel' .. 'lo')
assert_equal('hello 123', 'hello ' .. 123)
assert_equal('123 hello', 123 .. ' hello')
assert_equal('123456', 123 .. 456)
enddef
def Test_expr5_float()
CheckFeature float
assert_equal(66.0, 60.0 + 6.0)
assert_equal(66.0, 60.0 + 6)
assert_equal(66.0, 60 + 6.0)
assert_equal(5.1, g:afloat + 5)
assert_equal(8.1, 8 + g:afloat)
assert_equal(10.1, g:anint + g:afloat)
assert_equal(10.1, g:afloat + g:anint)
assert_equal(54.0, 60.0 - 6.0)
assert_equal(54.0, 60.0 - 6)
assert_equal(54.0, 60 - 6.0)
assert_equal(-4.9, g:afloat - 5)
assert_equal(7.9, 8 - g:afloat)
assert_equal(9.9, g:anint - g:afloat)
assert_equal(-9.9, g:afloat - g:anint)
enddef
func Test_expr5_fails()
let msg = "white space required before and after '+'"
call CheckDefFailure("let x = 1+2", msg)
call CheckDefFailure("let x = 1 +2", msg)
call CheckDefFailure("let x = 1+ 2", msg)
let msg = "white space required before and after '-'"
call CheckDefFailure("let x = 1-2", msg)
call CheckDefFailure("let x = 1 -2", msg)
call CheckDefFailure("let x = 1- 2", msg)
let msg = "white space required before and after '..'"
call CheckDefFailure("let x = '1'..'2'", msg)
call CheckDefFailure("let x = '1' ..'2'", msg)
call CheckDefFailure("let x = '1'.. '2'", msg)
endfunc
" test multiply, divide, modulo
def Test_expr6()
assert_equal(36, 6 * 6)
assert_equal(24, 6 * g:alsoint)
assert_equal(24, g:alsoint * 6)
assert_equal(40, g:anint * g:alsoint)
assert_equal(10, 60 / 6)
assert_equal(6, 60 / g:anint)
assert_equal(1, g:anint / 6)
assert_equal(2, g:anint / g:alsoint)
assert_equal(5, 11 % 6)
assert_equal(4, g:anint % 6)
assert_equal(3, 13 % g:anint)
assert_equal(2, g:anint % g:alsoint)
assert_equal(4, 6 * 4 / 6)
enddef
def Test_expr6_float()
CheckFeature float
assert_equal(36.0, 6.0 * 6)
assert_equal(36.0, 6 * 6.0)
assert_equal(36.0, 6.0 * 6.0)
assert_equal(1.0, g:afloat * g:anint)
assert_equal(10.0, 60 / 6.0)
assert_equal(10.0, 60.0 / 6)
assert_equal(10.0, 60.0 / 6.0)
assert_equal(0.01, g:afloat / g:anint)
assert_equal(4.0, 6.0 * 4 / 6)
assert_equal(4.0, 6 * 4.0 / 6)
assert_equal(4.0, 6 * 4 / 6.0)
assert_equal(4.0, 6.0 * 4.0 / 6)
assert_equal(4.0, 6 * 4.0 / 6.0)
assert_equal(4.0, 6.0 * 4 / 6.0)
assert_equal(4.0, 6.0 * 4.0 / 6.0)
assert_equal(4.0, 6.0 * 4.0 / 6.0)
enddef
func Test_expr6_fails()
let msg = "white space required before and after '*'"
call CheckDefFailure("let x = 1*2", msg)
call CheckDefFailure("let x = 1 *2", msg)
call CheckDefFailure("let x = 1* 2", msg)
let msg = "white space required before and after '/'"
call CheckDefFailure("let x = 1/2", msg)
call CheckDefFailure("let x = 1 /2", msg)
call CheckDefFailure("let x = 1/ 2", msg)
let msg = "white space required before and after '%'"
call CheckDefFailure("let x = 1%2", msg)
call CheckDefFailure("let x = 1 %2", msg)
call CheckDefFailure("let x = 1% 2", msg)
call CheckDefFailure("let x = '1' * '2'", 'E1036:')
call CheckDefFailure("let x = '1' / '2'", 'E1036:')
call CheckDefFailure("let x = '1' % '2'", 'E1035:')
call CheckDefFailure("let x = 0z01 * 0z12", 'E1036:')
call CheckDefFailure("let x = 0z01 / 0z12", 'E1036:')
call CheckDefFailure("let x = 0z01 % 0z12", 'E1035:')
call CheckDefFailure("let x = [1] * [2]", 'E1036:')
call CheckDefFailure("let x = [1] / [2]", 'E1036:')
call CheckDefFailure("let x = [1] % [2]", 'E1035:')
call CheckDefFailure("let x = #{one: 1} * #{two: 2}", 'E1036:')
call CheckDefFailure("let x = #{one: 1} / #{two: 2}", 'E1036:')
call CheckDefFailure("let x = #{one: 1} % #{two: 2}", 'E1035:')
endfunc
func Test_expr6_float_fails()
CheckFeature float
call CheckDefFailure("let x = 1.0 % 2", 'E1035:')
endfunc
" define here to use old style parsing
if has('float')
let g:float_zero = 0.0
let g:float_neg = -9.8
let g:float_big = 9.9e99
endif
let g:blob_empty = 0z
let g:blob_one = 0z01
let g:blob_long = 0z0102.0304
let g:string_empty = ''
let g:string_short = 'x'
let g:string_long = 'abcdefghijklm'
let g:string_special = "ab\ncd\ref\ekk"
let g:special_true = v:true
let g:special_false = v:false
let g:special_null = v:null
let g:special_none = v:none
let g:list_empty = []
let g:list_mixed = [1, 'b', v:false]
let g:dict_empty = {}
let g:dict_one = #{one: 1}
let $TESTVAR = 'testvar'
let @a = 'register a'
" test low level expression
def Test_expr7_number()
" number constant
assert_equal(0, 0)
assert_equal(654, 0654)
assert_equal(6, 0x6)
assert_equal(15, 0xf)
assert_equal(255, 0xff)
enddef
def Test_expr7_float()
" float constant
if has('float')
assert_equal(g:float_zero, .0)
assert_equal(g:float_zero, 0.0)
assert_equal(g:float_neg, -9.8)
assert_equal(g:float_big, 9.9e99)
endif
enddef
def Test_expr7_blob()
" blob constant
assert_equal(g:blob_empty, 0z)
assert_equal(g:blob_one, 0z01)
assert_equal(g:blob_long, 0z0102.0304)
enddef
def Test_expr7_string()
" string constant
assert_equal(g:string_empty, '')
assert_equal(g:string_empty, "")
assert_equal(g:string_short, 'x')
assert_equal(g:string_short, "x")
assert_equal(g:string_long, 'abcdefghijklm')
assert_equal(g:string_long, "abcdefghijklm")
assert_equal(g:string_special, "ab\ncd\ref\ekk")
enddef
def Test_expr7_special()
" special constant
assert_equal(g:special_true, true)
assert_equal(g:special_false, false)
assert_equal(g:special_null, v:null)
assert_equal(g:special_none, v:none)
enddef
def Test_expr7_list()
" list
assert_equal(g:list_empty, [])
assert_equal(g:list_empty, [ ])
assert_equal(g:list_mixed, [1, 'b', false])
enddef
def Test_expr7_lambda()
" lambda
let La = { -> 'result'}
assert_equal('result', La())
assert_equal([1, 3, 5], [1, 2, 3]->map({key, val -> key + val}))
enddef
def Test_expr7_dict()
" dictionary
assert_equal(g:dict_empty, {})
assert_equal(g:dict_empty, { })
assert_equal(g:dict_one, {'one': 1})
let key = 'one'
let val = 1
assert_equal(g:dict_one, {key: val})
enddef
def Test_expr7_option()
" option
set ts=11
assert_equal(11, &ts)
set ts=8
set grepprg=some\ text
assert_equal('some text', &grepprg)
set grepprg&
enddef
def Test_expr7_environment()
" environment variable
assert_equal('testvar', $TESTVAR)
assert_equal('', $ASDF_ASD_XXX)
enddef
def Test_expr7_register()
" register
assert_equal('register a', @a)
enddef
def Test_expr7_parens()
" (expr)
assert_equal(4, (6 * 4) / 6)
assert_equal(0, 6 * ( 4 / 6 ))
assert_equal(6, +6)
assert_equal(-6, -6)
assert_equal(6, --6)
assert_equal(6, -+-6)
assert_equal(-6, ---6)
enddef
def Test_expr7_not()
assert_equal(true, !'')
assert_equal(true, ![])
assert_equal(false, !'asdf')
assert_equal(false, ![2])
assert_equal(true, !!'asdf')
assert_equal(true, !![2])
enddef
func Test_expr7_fails()
call CheckDefFailure("let x = (12", "E110:")
call CheckDefFailure("let x = -'xx'", "E1030:")
call CheckDefFailure("let x = +'xx'", "E1030:")
call CheckDefFailure("let x = @", "E1002:")
call CheckDefFailure("let x = @<", "E354:")
endfunc
let g:Funcrefs = [function('add')]
func CallMe(arg)
return a:arg
endfunc
def Test_expr7_trailing()
" user function call
assert_equal(123, CallMe(123))
assert_equal('nothing', CallMe('nothing'))
" partial call
let Part = function('CallMe')
assert_equal('yes', Part('yes'))
" funcref call, using list index
let l = []
g:Funcrefs[0](l, 2)
assert_equal([2], l)
" method call
l = [2, 5, 6]
l->map({k, v -> k + v})
assert_equal([2, 6, 8], l)
" lambda method call
l = [2, 5]
l->{l -> add(l, 8)}()
assert_equal([2, 5, 8], l)
" dict member
let d = #{key: 123}
assert_equal(123, d.key)
enddef
func Test_expr7_trailing_fails()
call CheckDefFailureList(['let l = [2]', 'l->{l -> add(l, 8)}'], 'E107')
endfunc
func Test_expr_fails()
call CheckDefFailure("let x = '1'is2", 'E488:')
call CheckDefFailure("let x = '1'isnot2", 'E488:')
endfunc

View File

@ -0,0 +1,359 @@
" Test various aspects of the Vim9 script language.
" Check that "lines" inside ":def" results in an "error" message.
func CheckDefFailure(lines, error)
call writefile(['def! Func()'] + a:lines + ['enddef'], 'Xdef')
call assert_fails('so Xdef', a:error, a:lines)
call delete('Xdef')
endfunc
func CheckScriptFailure(lines, error)
call writefile(a:lines, 'Xdef')
call assert_fails('so Xdef', a:error, a:lines)
call delete('Xdef')
endfunc
def Test_syntax()
let var = 234
let other: list<string> = ['asdf']
enddef
func Test_def_basic()
def SomeFunc(): string
return 'yes'
enddef
call assert_equal('yes', SomeFunc())
endfunc
def Test_assignment()
let bool1: bool = true
assert_equal(v:true, bool1)
let bool2: bool = false
assert_equal(v:false, bool2)
let list1: list<string> = ['sdf', 'asdf']
let list2: list<number> = [1, 2, 3]
" TODO: does not work yet
" let listS: list<string> = []
" let listN: list<number> = []
let dict1: dict<string> = #{key: 'value'}
let dict2: dict<number> = #{one: 1, two: 2}
enddef
func Test_assignment_failure()
call CheckDefFailure(['let var=234'], 'E1004:')
call CheckDefFailure(['let var =234'], 'E1004:')
call CheckDefFailure(['let var= 234'], 'E1004:')
call CheckDefFailure(['let true = 1'], 'E1034:')
call CheckDefFailure(['let false = 1'], 'E1034:')
call CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
call CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
call CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
endfunc
func Test_const()
call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
call CheckDefFailure(['const two'], 'E1021:')
endfunc
def Test_block()
let outer = 1
{
let inner = 2
assert_equal(1, outer)
assert_equal(2, inner)
}
assert_equal(1, outer)
enddef
func Test_block_failure()
call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
endfunc
def ReturnString(): string
return 'string'
enddef
def ReturnNumber(): number
return 123
enddef
def Test_return_string()
assert_equal('string', ReturnString())
assert_equal(123, ReturnNumber())
enddef
func Increment()
let g:counter += 1
endfunc
def Test_call_ufunc_count()
g:counter = 1
Increment()
Increment()
Increment()
" works with and without :call
assert_equal(4, g:counter)
call assert_equal(4, g:counter)
unlet g:counter
enddef
def MyVarargs(arg: string, ...rest: list<string>): string
let res = arg
for s in rest
res ..= ',' .. s
endfor
return res
enddef
def Test_call_varargs()
assert_equal('one', MyVarargs('one'))
assert_equal('one,two', MyVarargs('one', 'two'))
assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef
def Test_return_type_wrong()
" TODO: why is ! needed for Mac and FreeBSD?
CheckScriptFailure(['def! Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def! Func(): string', 'return 1', 'enddef'], 'expected string but got number')
CheckScriptFailure(['def! Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
CheckScriptFailure(['def! Func()', 'return "a"', 'enddef'], 'expected void but got string')
enddef
def Test_try_catch()
let l = []
try
add(l, '1')
throw 'wrong'
add(l, '2')
catch
add(l, v:exception)
finally
add(l, '3')
endtry
assert_equal(['1', 'wrong', '3'], l)
enddef
let s:export_script_lines =<< trim END
vim9script
let name: string = 'bob'
def Concat(arg: string): string
return name .. arg
enddef
let g:result = Concat('bie')
let g:localname = name
export const CONST = 1234
export let exported = 9876
export def Exported(): string
return 'Exported'
enddef
END
def Test_vim9script()
let import_script_lines =<< trim END
vim9script
import {exported, Exported} from './Xexport.vim'
g:imported = exported
g:imported_func = Exported()
END
writefile(import_script_lines, 'Ximport.vim')
writefile(s:export_script_lines, 'Xexport.vim')
source Ximport.vim
assert_equal('bobbie', g:result)
assert_equal('bob', g:localname)
assert_equal(9876, g:imported)
assert_equal('Exported', g:imported_func)
assert_false(exists('g:name'))
unlet g:result
unlet g:localname
unlet g:imported
unlet g:imported_func
delete('Ximport.vim')
delete('Xexport.vim')
CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
enddef
def Test_vim9script_call()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
var = arg
enddef
MyFunc('foobar')
assert_equal('foobar', var)
let str = 'barfoo'
str->MyFunc()
assert_equal('barfoo', var)
let g:value = 'value'
g:value->MyFunc()
assert_equal('value', var)
let listvar = []
def ListFunc(arg: list<number>)
listvar = arg
enddef
[1, 2, 3]->ListFunc()
assert_equal([1, 2, 3], listvar)
let dictvar = {}
def DictFunc(arg: dict<number>)
dictvar = arg
enddef
{'a': 1, 'b': 2}->DictFunc()
assert_equal(#{a: 1, b: 2}, dictvar)
#{a: 3, b: 4}->DictFunc()
assert_equal(#{a: 3, b: 4}, dictvar)
END
writefile(lines, 'Xcall.vim')
source Xcall.vim
delete('Xcall.vim')
enddef
def Test_vim9script_call_fail_decl()
let lines =<< trim END
vim9script
let var = ''
def MyFunc(arg: string)
let var = 123
enddef
END
writefile(lines, 'Xcall_decl.vim')
assert_fails('source Xcall_decl.vim', 'E1054:')
delete('Xcall_decl.vim')
enddef
def Test_vim9script_call_fail_const()
let lines =<< trim END
vim9script
const var = ''
def MyFunc(arg: string)
var = 'asdf'
enddef
END
writefile(lines, 'Xcall_const.vim')
assert_fails('source Xcall_const.vim', 'E46:')
delete('Xcall_const.vim')
enddef
def Test_vim9script_reload()
let lines =<< trim END
vim9script
const var = ''
let valone = 1234
def MyFunc(arg: string)
valone = 5678
enddef
END
let morelines =<< trim END
let valtwo = 222
export def GetValtwo(): number
return valtwo
enddef
END
writefile(lines + morelines, 'Xreload.vim')
source Xreload.vim
source Xreload.vim
source Xreload.vim
let testlines =<< trim END
vim9script
def TheFunc()
import GetValtwo from './Xreload.vim'
assert_equal(222, GetValtwo())
enddef
TheFunc()
END
writefile(testlines, 'Ximport.vim')
source Ximport.vim
" test that when not using "morelines" valtwo is still defined
" need to source Xreload.vim again, import doesn't reload a script
writefile(lines, 'Xreload.vim')
source Xreload.vim
source Ximport.vim
" cannot declare a var twice
lines =<< trim END
vim9script
let valone = 1234
let valone = 5678
END
writefile(lines, 'Xreload.vim')
assert_fails('source Xreload.vim', 'E1041:')
delete('Xreload.vim')
delete('Ximport.vim')
enddef
def Test_import_absolute()
let import_lines = [
\ 'vim9script',
\ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
\ 'g:imported_abs = exported',
\ ]
writefile(import_lines, 'Ximport_abs.vim')
writefile(s:export_script_lines, 'Xexport_abs.vim')
source Ximport_abs.vim
assert_equal(9876, g:imported_abs)
unlet g:imported_abs
delete('Ximport_abs.vim')
delete('Xexport_abs.vim')
enddef
def Test_import_rtp()
let import_lines = [
\ 'vim9script',
\ 'import exported from "Xexport_rtp.vim"',
\ 'g:imported_rtp = exported',
\ ]
writefile(import_lines, 'Ximport_rtp.vim')
mkdir('import')
writefile(s:export_script_lines, 'import/Xexport_rtp.vim')
let save_rtp = &rtp
&rtp = getcwd()
source Ximport_rtp.vim
&rtp = save_rtp
assert_equal(9876, g:imported_rtp)
unlet g:imported_rtp
delete('Ximport_rtp.vim')
delete('import/Xexport_rtp.vim')
delete('import', 'd')
enddef
def Test_fixed_size_list()
" will be allocated as one piece of memory, check that changes work
let l = [1, 2, 3, 4]
l->remove(0)
l->add(5)
l->insert(99, 1)
call assert_equal([2, 99, 3, 4, 5], l)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -758,9 +758,10 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
switch (argvars[0].v_type)
{
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_BOOL:
case VAR_FLOAT:
case VAR_SPECIAL:
case VAR_STRING:
break;
@ -781,7 +782,7 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
{
ufunc_T *fp;
fp = find_func(argvars[0].vval.v_string);
fp = find_func(argvars[0].vval.v_string, NULL);
if (fp != NULL)
retval = fp->uf_refcount;
}

File diff suppressed because it is too large Load Diff

View File

@ -742,6 +742,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
149,
/**/
148,
/**/

View File

@ -2151,6 +2151,10 @@ typedef enum {
USEPOPUP_HIDDEN // use info popup initially hidden
} use_popup_T;
// Flags for assignment functions.
#define LET_IS_CONST 1 // ":const"
#define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const"
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff

252
src/vim9.h Normal file
View File

@ -0,0 +1,252 @@
/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* vim9.h: types and globals used for Vim9 script.
*/
typedef enum {
ISN_EXEC, // execute Ex command line isn_arg.string
ISN_ECHO, // echo isn_arg.number items on top of stack
// get and set variables
ISN_LOAD, // push local variable isn_arg.number
ISN_LOADV, // push v: variable isn_arg.number
ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
ISN_LOADS, // push s: variable isn_arg.string
ISN_LOADG, // push g: variable isn_arg.string
ISN_LOADOPT, // push option isn_arg.string
ISN_LOADENV, // push environment variable isn_arg.string
ISN_LOADREG, // push register isn_arg.number
ISN_STORE, // pop into local variable isn_arg.number
ISN_STOREG, // pop into global variable isn_arg.string
ISN_STORESCRIPT, // pop into scirpt variable isn_arg.script
ISN_STOREOPT, // pop into option isn_arg.string
// ISN_STOREOTHER, // pop into other script variable isn_arg.other.
ISN_STORENR, // store number into local variable isn_arg.storenr.str_idx
// constants
ISN_PUSHNR, // push number isn_arg.number
ISN_PUSHBOOL, // push bool value isn_arg.number
ISN_PUSHSPEC, // push special value isn_arg.number
ISN_PUSHF, // push float isn_arg.fnumber
ISN_PUSHS, // push string isn_arg.string
ISN_PUSHBLOB, // push blob isn_arg.blob
ISN_NEWLIST, // push list from stack items, size is isn_arg.number
ISN_NEWDICT, // push dict from stack items, size is isn_arg.number
// function call
ISN_BCALL, // call builtin function isn_arg.bfunc
ISN_DCALL, // call def function isn_arg.dfunc
ISN_UCALL, // call user function or funcref/partial isn_arg.ufunc
ISN_PCALL, // call partial, use isn_arg.pfunc
ISN_RETURN, // return, result is on top of stack
ISN_FUNCREF, // push a function ref to dfunc isn_arg.number
// expression operations
ISN_JUMP, // jump if condition is matched isn_arg.jump
// loop
ISN_FOR, // get next item from a list, uses isn_arg.forloop
ISN_TRY, // add entry to ec_trystack, uses isn_arg.try
ISN_THROW, // pop value of stack, store in v:exception
ISN_PUSHEXC, // push v:exception
ISN_CATCH, // drop v:exception
ISN_ENDTRY, // take entry off from ec_trystack
// moreexpression operations
ISN_ADDLIST,
ISN_ADDBLOB,
// operation with two arguments; isn_arg.op.op_type is exptype_T
ISN_OPNR,
ISN_OPFLOAT,
ISN_OPANY,
// comparative operations; isn_arg.op.op_type is exptype_T, op_ic used
ISN_COMPAREBOOL,
ISN_COMPARESPECIAL,
ISN_COMPARENR,
ISN_COMPAREFLOAT,
ISN_COMPARESTRING,
ISN_COMPAREBLOB,
ISN_COMPARELIST,
ISN_COMPAREDICT,
ISN_COMPAREFUNC,
ISN_COMPAREPARTIAL,
ISN_COMPAREANY,
// expression operations
ISN_CONCAT,
ISN_INDEX, // [expr] list index
ISN_MEMBER, // dict.member using isn_arg.string
ISN_2BOOL, // convert value to bool, invert if isn_arg.number != 0
ISN_2STRING, // convert value to string at isn_arg.number on stack
ISN_NEGATENR, // apply "-" to number
ISN_CHECKNR, // check value can be used as a number
ISN_CHECKTYPE, // check value type is isn_arg.type.tc_type
ISN_DROP // pop stack and discard value
} isntype_T;
// arguments to ISN_BCALL
typedef struct {
int cbf_idx; // index in "global_functions"
int cbf_argcount; // number of arguments on top of stack
} cbfunc_T;
// arguments to ISN_DCALL
typedef struct {
int cdf_idx; // index in "def_functions" for ISN_DCALL
int cdf_argcount; // number of arguments on top of stack
} cdfunc_T;
// arguments to ISN_PCALL
typedef struct {
int cpf_top; // when TRUE partial is above the arguments
int cpf_argcount; // number of arguments on top of stack
} cpfunc_T;
// arguments to ISN_UCALL and ISN_XCALL
typedef struct {
char_u *cuf_name;
int cuf_argcount; // number of arguments on top of stack
} cufunc_T;
typedef enum {
JUMP_ALWAYS,
JUMP_IF_TRUE, // pop and jump if true
JUMP_IF_FALSE, // pop and jump if false
JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is true, drop if not
JUMP_AND_KEEP_IF_FALSE, // jump if top of stack is false, drop if not
} jumpwhen_T;
// arguments to ISN_JUMP
typedef struct {
jumpwhen_T jump_when;
int jump_where; // position to jump to
} jump_T;
// arguments to ISN_FOR
typedef struct {
int for_idx; // loop variable index
int for_end; // position to jump to after done
} forloop_T;
// arguments to ISN_TRY
typedef struct {
int try_catch; // position to jump to on throw
int try_finally; // position to jump to for return
} try_T;
// arguments to ISN_ECHO
typedef struct {
int echo_with_white; // :echo instead of :echon
int echo_count; // number of expressions
} echo_T;
// arguments to ISN_OPNR, ISN_OPFLOAT, etc.
typedef struct {
exptype_T op_type;
int op_ic; // TRUE with '#', FALSE with '?', else MAYBE
} opexpr_T;
// arguments to ISN_CHECKTYPE
typedef struct {
vartype_T ct_type;
int ct_off; // offset in stack, -1 is bottom
} checktype_T;
// arguments to ISN_STORENR
typedef struct {
int str_idx;
varnumber_T str_val;
} storenr_T;
// arguments to ISN_STOREOPT
typedef struct {
char_u *so_name;
int so_flags;
} storeopt_T;
// arguments to ISN_LOADS
typedef struct {
char_u *ls_name; // variable name
int ls_sid; // script ID
} loads_T;
// arguments to ISN_LOADSCRIPT
typedef struct {
int script_sid; // script ID
int script_idx; // index in sn_var_vals
} script_T;
/*
* Instruction
*/
typedef struct {
isntype_T isn_type;
int isn_lnum;
union {
char_u *string;
varnumber_T number;
blob_T *blob;
#ifdef FEAT_FLOAT
float_T fnumber;
#endif
jump_T jump;
forloop_T forloop;
try_T try;
cbfunc_T bfunc;
cdfunc_T dfunc;
cpfunc_T pfunc;
cufunc_T ufunc;
echo_T echo;
opexpr_T op;
checktype_T type;
storenr_T storenr;
storeopt_T storeopt;
loads_T loads;
script_T script;
} isn_arg;
} isn_T;
/*
* Info about a function defined with :def. Used in "def_functions".
*/
struct dfunc_S {
ufunc_T *df_ufunc; // struct containing most stuff
int df_idx; // index in def_functions
int df_deleted; // if TRUE function was deleted
garray_T df_def_args_isn; // default argument instructions
isn_T *df_instr; // function body to be executed
int df_instr_count;
int df_varcount; // number of local variables
};
// Number of entries used by stack frame for a function call.
#define STACK_FRAME_SIZE 3
#ifdef DEFINE_VIM9_GLOBALS
// Functions defined with :def are stored in this growarray.
// They are never removed, so that they can be found by index.
// Deleted functions have the df_deleted flag set.
garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL};
#else
extern garray_T def_functions;
#endif

4612
src/vim9compile.c Normal file

File diff suppressed because it is too large Load Diff

1934
src/vim9execute.c Normal file

File diff suppressed because it is too large Load Diff

405
src/vim9script.c Normal file
View File

@ -0,0 +1,405 @@
/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* vim9script.c: :vim9script, :import, :export and friends
*/
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
#include "vim9.h"
static char e_needs_vim9[] = N_("E1042: import/export can only be used in vim9script");
int
in_vim9script(void)
{
// TODO: go up the stack?
return current_sctx.sc_version == SCRIPT_VERSION_VIM9;
}
/*
* ":vim9script".
*/
void
ex_vim9script(exarg_T *eap)
{
scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (!getline_equal(eap->getline, eap->cookie, getsourceline))
{
emsg(_("E1038: vim9script can only be used in a script"));
return;
}
if (si->sn_had_command)
{
emsg(_("E1039: vim9script must be the first command in a script"));
return;
}
current_sctx.sc_version = SCRIPT_VERSION_VIM9;
si->sn_version = SCRIPT_VERSION_VIM9;
si->sn_had_command = TRUE;
if (STRCMP(p_cpo, CPO_VIM) != 0)
{
si->sn_save_cpo = p_cpo;
p_cpo = vim_strsave((char_u *)CPO_VIM);
}
}
/*
* ":export let Name: type"
* ":export const Name: type"
* ":export def Name(..."
* ":export class Name ..."
*
* ":export {Name, ...}"
*/
void
ex_export(exarg_T *eap UNUSED)
{
if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
{
emsg(_(e_needs_vim9));
return;
}
eap->cmd = eap->arg;
(void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
switch (eap->cmdidx)
{
case CMD_let:
case CMD_const:
case CMD_def:
// case CMD_class:
is_export = TRUE;
do_cmdline(eap->cmd, eap->getline, eap->cookie,
DOCMD_VERBOSE + DOCMD_NOWAIT);
// The command will reset "is_export" when exporting an item.
if (is_export)
{
emsg(_("E1044: export with invalid argument"));
is_export = FALSE;
}
break;
default:
emsg(_("E1043: Invalid command after :export"));
break;
}
}
/*
* Add a new imported item entry to the current script.
*/
static imported_T *
new_imported(garray_T *gap)
{
if (ga_grow(gap, 1) == OK)
return ((imported_T *)gap->ga_data + gap->ga_len++);
return NULL;
}
/*
* Free all imported items in script "sid".
*/
void
free_imports(int sid)
{
scriptitem_T *si = &SCRIPT_ITEM(sid);
int idx;
for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
{
imported_T *imp = ((imported_T *)si->sn_imports.ga_data + idx);
vim_free(imp->imp_name);
}
ga_clear(&si->sn_imports);
}
/*
* ":import Item from 'filename'"
* ":import Item as Alias from 'filename'"
* ":import {Item} from 'filename'".
* ":import {Item as Alias} from 'filename'"
* ":import {Item, Item} from 'filename'"
* ":import {Item, Item as Alias} from 'filename'"
*
* ":import * as Name from 'filename'"
*/
void
ex_import(exarg_T *eap)
{
if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
emsg(_(e_needs_vim9));
else
{
char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid);
if (cmd_end != NULL)
eap->nextcmd = check_nextcmd(cmd_end);
}
}
/*
* Handle an ":import" command and add the resulting imported_T to "gap", when
* not NULL, or script "import_sid" sn_imports.
* Returns a pointer to after the command or NULL in case of failure
*/
char_u *
handle_import(char_u *arg_start, garray_T *gap, int import_sid)
{
char_u *arg = arg_start;
char_u *cmd_end;
char_u *as_ptr = NULL;
char_u *from_ptr;
int as_len = 0;
int ret = FAIL;
typval_T tv;
int sid = -1;
int res;
if (*arg == '{')
{
// skip over {item} list
while (*arg != NUL && *arg != '}')
++arg;
if (*arg == '}')
arg = skipwhite(arg + 1);
}
else
{
if (*arg == '*')
arg = skipwhite(arg + 1);
else
{
while (eval_isnamec1(*arg))
++arg;
arg = skipwhite(arg);
}
if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
{
// skip over "as Name "
arg = skipwhite(arg + 2);
as_ptr = arg;
while (eval_isnamec1(*arg))
++arg;
as_len = (int)(arg - as_ptr);
arg = skipwhite(arg);
}
else if (*arg_start == '*')
{
emsg(_("E1045: Missing \"as\" after *"));
return NULL;
}
}
if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
{
emsg(_("E1045: Missing \"from\""));
return NULL;
}
from_ptr = arg;
arg = skipwhite(arg + 4);
tv.v_type = VAR_UNKNOWN;
// TODO: should we accept any expression?
if (*arg == '\'')
ret = get_lit_string_tv(&arg, &tv, TRUE);
else if (*arg == '"')
ret = get_string_tv(&arg, &tv, TRUE);
if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
{
emsg(_("E1045: Invalid string after \"from\""));
return NULL;
}
cmd_end = arg;
// find script tv.vval.v_string
if (*tv.vval.v_string == '.')
{
size_t len;
scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
char_u *tail = gettail(si->sn_name);
char_u *from_name;
// Relative to current script: "./name.vim", "../../name.vim".
len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
from_name = alloc((int)len);
if (from_name == NULL)
{
clear_tv(&tv);
return NULL;
}
vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
add_pathsep(from_name);
STRCAT(from_name, tv.vval.v_string);
simplify_filename(from_name);
res = do_source(from_name, FALSE, DOSO_NONE, &sid);
vim_free(from_name);
}
else if (mch_isFullName(tv.vval.v_string))
{
// Absolute path: "/tmp/name.vim"
res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
}
else
{
size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
char_u *from_name;
// Find file in "import" subdirs in 'runtimepath'.
from_name = alloc((int)len);
if (from_name == NULL)
{
clear_tv(&tv);
return NULL;
}
vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
vim_free(from_name);
}
if (res == FAIL || sid <= 0)
{
semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
clear_tv(&tv);
return NULL;
}
clear_tv(&tv);
if (*arg_start == '*')
{
imported_T *imported = new_imported(gap != NULL ? gap
: &SCRIPT_ITEM(import_sid).sn_imports);
if (imported == NULL)
return NULL;
imported->imp_name = vim_strnsave(as_ptr, as_len);
imported->imp_sid = sid;
imported->imp_all = TRUE;
}
else
{
scriptitem_T *script = &SCRIPT_ITEM(sid);
arg = arg_start;
if (*arg == '{')
arg = skipwhite(arg + 1);
for (;;)
{
char_u *name = arg;
int name_len;
int cc;
int idx;
svar_T *sv;
imported_T *imported;
ufunc_T *ufunc;
// isolate one name
while (eval_isnamec1(*arg))
++arg;
name_len = (int)(arg - name);
// find name in "script"
// TODO: also find script-local user function
cc = *arg;
*arg = NUL;
idx = get_script_item_idx(sid, name, FALSE);
if (idx >= 0)
{
sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
if (!sv->sv_export)
{
semsg(_("E1049: Item not exported in script: %s"), name);
*arg = cc;
return NULL;
}
}
else
{
char_u buffer[200];
char_u *funcname;
// it could be a user function.
if (STRLEN(name) < sizeof(buffer) - 10)
funcname = buffer;
else
{
funcname = alloc(STRLEN(name) + 10);
if (funcname == NULL)
{
*arg = cc;
return NULL;
}
}
funcname[0] = K_SPECIAL;
funcname[1] = KS_EXTRA;
funcname[2] = (int)KE_SNR;
sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
ufunc = find_func(funcname, NULL);
if (funcname != buffer)
vim_free(funcname);
if (ufunc == NULL)
{
semsg(_("E1048: Item not found in script: %s"), name);
*arg = cc;
return NULL;
}
}
imported = new_imported(gap != NULL ? gap
: &SCRIPT_ITEM(import_sid).sn_imports);
if (imported == NULL)
return NULL;
*arg = cc;
arg = skipwhite(arg);
// TODO: check for "as" following
// imported->imp_name = vim_strnsave(as_ptr, as_len);
imported->imp_name = vim_strnsave(name, name_len);
imported->imp_sid = sid;
if (idx >= 0)
{
imported->imp_type = sv->sv_type;
imported->imp_var_vals_idx = idx;
}
else
imported->imp_funcname = ufunc->uf_name;
arg = skipwhite(arg);
if (*arg_start != '{')
break;
if (*arg == '}')
{
arg = skipwhite(arg + 1);
break;
}
if (*arg != ',')
{
emsg(_("E1046: Missing comma in import"));
return NULL;
}
arg = skipwhite(arg + 1);
}
if (arg != from_ptr)
{
emsg(_("E1047: syntax error in import"));
return NULL;
}
}
return cmd_end;
}
#endif // FEAT_EVAL

View File

@ -1327,6 +1327,7 @@ write_viminfo_varlist(FILE *fp)
case VAR_SPECIAL: s = "XPL"; break;
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_JOB: