mirror of https://github.com/vim/vim.git
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:
parent
1d9215b9aa
commit
8a7d6542b3
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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:
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
|
|
72
src/Makefile
72
src/Makefile
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
498
src/eval.c
498
src/eval.c
|
@ -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,14 +243,22 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
|
|||
{
|
||||
partial_T *partial = expr->vval.v_partial;
|
||||
|
||||
s = partial_name(partial);
|
||||
if (s == NULL || *s == NUL)
|
||||
return FAIL;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.evaluate = TRUE;
|
||||
funcexe.partial = partial;
|
||||
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
|
||||
return FAIL;
|
||||
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;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.evaluate = TRUE;
|
||||
funcexe.partial = partial;
|
||||
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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,36 +2251,12 @@ 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
|
||||
{
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
case '.': ret = get_number_tv(arg, rettv, evaluate, want_string);
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
if (retlist && rettv_list_alloc(rettv) == FAIL)
|
||||
return;
|
||||
if (retlist)
|
||||
{
|
||||
if (rettv_list_alloc(rettv) == FAIL)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
}
|
||||
|
||||
if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
|
||||
return;
|
||||
|
|
1071
src/evalfunc.c
1071
src/evalfunc.c
File diff suppressed because it is too large
Load Diff
320
src/evalvars.c
320
src/evalvars.c
|
@ -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), <v, FALSE, is_const,
|
||||
arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, flags,
|
||||
(char_u *)"]", op);
|
||||
clear_tv(<v);
|
||||
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
|
||||
return ht; // local 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 (*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 (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
|
||||
{
|
||||
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)
|
||||
{
|
||||
emsg(_(e_cannot_mod));
|
||||
return;
|
||||
if (flags & LET_IS_CONST)
|
||||
{
|
||||
emsg(_(e_cannot_mod));
|
||||
return;
|
||||
}
|
||||
|
||||
if (var_check_ro(di->di_flags, name, FALSE)
|
||||
|| var_check_lock(di->di_tv.v_lock, name, FALSE))
|
||||
return;
|
||||
|
||||
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
|
||||
if (var_check_ro(v->di_flags, name, FALSE)
|
||||
|| var_check_lock(v->di_tv.v_lock, name, FALSE))
|
||||
return;
|
||||
|
||||
// Handle setting internal v: variables separately where needed to
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -286,8 +286,11 @@ EXTERN int debug_backtrace_level INIT(= 0); // breakpoint backtrace level
|
|||
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 FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
|
||||
# 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]
|
||||
|
||||
/*
|
||||
* The exception currently being thrown. Used to pass an exception to
|
||||
|
@ -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,9 +1500,13 @@ 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_endtry[] INIT(= N_("E600: Missing :endtry"));
|
||||
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_endfor[] INIT(= N_("E170: Missing :endfor"));
|
||||
EXTERN char e_while[] INIT(= N_("E588: :endwhile without :while"));
|
||||
EXTERN char e_for[] INIT(= N_("E588: :endfor without :for"));
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -1575,10 +1614,10 @@ EXTERN char e_prev_dir[] INIT(= N_("E459: Cannot go back to previous directory")
|
|||
|
||||
#ifdef FEAT_QUICKFIX
|
||||
EXTERN char e_quickfix[] INIT(= N_("E42: No Errors"));
|
||||
EXTERN char e_loclist[] INIT(= N_("E776: No location list"));
|
||||
EXTERN char e_loclist[] INIT(= N_("E776: No location list"));
|
||||
#endif
|
||||
EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string"));
|
||||
EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
|
||||
EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string"));
|
||||
EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
|
||||
EXTERN char e_readonly[] INIT(= N_("E45: 'readonly' option is set (add ! to override)"));
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_undefvar[] INIT(= N_("E121: Undefined variable: %s"));
|
||||
|
@ -1589,17 +1628,23 @@ EXTERN char e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%
|
|||
EXTERN char e_readonlysbx[] INIT(= N_("E794: Cannot set variable in the sandbox: \"%s\""));
|
||||
EXTERN char e_stringreq[] INIT(= N_("E928: String required"));
|
||||
EXTERN char e_emptykey[] INIT(= N_("E713: Cannot use empty key for Dictionary"));
|
||||
EXTERN char e_dictreq[] INIT(= N_("E715: Dictionary required"));
|
||||
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_dictreq[] INIT(= N_("E715: Dictionary required"));
|
||||
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_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
|
||||
|
|
19
src/gui.c
19
src/gui.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -576,8 +576,8 @@ ins_compl_add(
|
|||
char_u *str,
|
||||
int len,
|
||||
char_u *fname,
|
||||
char_u **cptext, // extra text for popup menu or NULL
|
||||
typval_T *user_data, // "user_data" entry or NULL
|
||||
char_u **cptext, // extra text for popup menu or NULL
|
||||
typval_T *user_data UNUSED, // "user_data" entry or NULL
|
||||
int cdir,
|
||||
int flags_arg,
|
||||
int adup) // accept duplicate match
|
||||
|
|
|
@ -215,7 +215,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
|
|||
|
||||
case VAR_NUMBER:
|
||||
vim_snprintf((char *)numbuf, NUMBUFLEN, "%lld",
|
||||
(long_long_T)val->vval.v_number);
|
||||
(long_long_T)val->vval.v_number);
|
||||
ga_concat(gap, numbuf);
|
||||
break;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
179
src/list.c
179
src/list.c
|
@ -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,13 +220,14 @@ list_free_contents(list_T *l)
|
|||
{
|
||||
listitem_T *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);
|
||||
}
|
||||
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);
|
||||
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,7 +1073,8 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||
break;
|
||||
if (**arg != ',')
|
||||
{
|
||||
semsg(_("E696: Missing comma in List: %s"), *arg);
|
||||
if (do_error)
|
||||
semsg(_("E696: Missing comma in List: %s"), *arg);
|
||||
goto failret;
|
||||
}
|
||||
*arg = skipwhite(*arg + 1);
|
||||
|
@ -958,7 +1082,8 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||
|
||||
if (**arg != ']')
|
||||
{
|
||||
semsg(_("E697: Missing end of List ']': %s"), *arg);
|
||||
if (do_error)
|
||||
semsg(_("E697: Missing end of List ']': %s"), *arg);
|
||||
failret:
|
||||
if (evaluate)
|
||||
list_free(l);
|
||||
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
37
src/main.c
37
src/main.c
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
12
src/misc1.c
12
src/misc1.c
|
@ -2067,13 +2067,17 @@ 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)
|
||||
{
|
||||
STRCPY(dest, str1);
|
||||
STRCPY(dest + l, str2);
|
||||
if (str1 == NULL)
|
||||
*dest = NUL;
|
||||
else
|
||||
STRCPY(dest, str1);
|
||||
if (str2 != NULL)
|
||||
STRCPY(dest + l, str2);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 : */
|
|
@ -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 : */
|
|
@ -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 : */
|
125
src/scriptfile.c
125
src/scriptfile.c
|
@ -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)
|
||||
{
|
||||
# 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;
|
||||
}
|
||||
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))
|
||||
{
|
||||
--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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
137
src/structs.h
137
src/structs.h
|
@ -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_NUMBER, // "v_number" is used
|
||||
VAR_STRING, // "v_string" 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
|
||||
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_JOB, // "v_job" is used
|
||||
VAR_CHANNEL, // "v_channel" 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
|
||||
listitem_T *lv_idx_item; // when not NULL item at index "lv_idx"
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
925
src/userfunc.c
925
src/userfunc.c
File diff suppressed because it is too large
Load Diff
|
@ -742,6 +742,8 @@ static char *(features[]) =
|
|||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
149,
|
||||
/**/
|
||||
148,
|
||||
/**/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue