blob: c9ce73a2d1ffaa3f116b104cfb12b38a4d3d95eb [file] [log] [blame] [view]
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001![Vim Logo](https://github.com/vim/vim/blob/master/runtime/vimlogo.gif)
2
3# What is Vim9?
4
Bram Moolenaarbe4e0162023-02-02 13:59:48 +00005This is a new syntax for Vim script that was introduced with Vim 9.0.
6It intends making Vim script faster and better.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01007
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01008
9# Why Vim9?
10
11## 1. FASTER VIM SCRIPT
12
13The third item on the poll results of 2018, after popup windows and text
Bram Moolenaarbe4e0162023-02-02 13:59:48 +000014properties, both of which have been implemented, is faster Vim script.
15So how do we do that?
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010016
17I have been throwing some ideas around, and soon came to the conclusion
18that the current way functions are called and executed, with
19dictionaries for the arguments and local variables, is never going to be
20very fast. We're lucky if we can make it twice as fast. The overhead
21of a function call and executing every line is just too high.
22
23So what then? We can only make something fast by having a new way of
24defining a function, with similar but different properties of the old
25way:
26* Arguments are only available by name, not through the a: dictionary or
27 the a:000 list.
28* Local variables are not available in an l: dictionary.
29* A few more things that slow us down, such as exception handling details.
30
31I Implemented a "proof of concept" and measured the time to run a simple
32for loop with an addition (Justin used this example in his presentation,
33full code is below):
34
35``` vim
36 let sum = 0
37 for i in range(1, 2999999)
38 let sum += i
39 endfor
40```
41
42| how | time in sec |
43| --------| -------- |
44| Vim old | 5.018541 |
45| Python | 0.369598 |
46| Lua | 0.078817 |
Bram Moolenaar53f7fcc2021-07-28 20:10:16 +020047| LuaJit | 0.004245 |
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010048| Vim new | 0.073595 |
49
50That looks very promising! It's just one example, but it shows how much
51we can gain, and also that Vim script can be faster than builtin
52interfaces.
53
Bram Moolenaar53f7fcc2021-07-28 20:10:16 +020054LuaJit is much faster at Lua-only instructions. In practice the script would
Bram Moolenaarbe4e0162023-02-02 13:59:48 +000055not do something useless counting, but change the text. For example,
Bram Moolenaar53f7fcc2021-07-28 20:10:16 +020056reindent all the lines:
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010057
58``` vim
59 let totallen = 0
60 for i in range(1, 100000)
61 call setline(i, ' ' .. getline(i))
62 let totallen += len(getline(i))
63 endfor
64```
65
66| how | time in sec |
67| --------| -------- |
Bram Moolenaar53f7fcc2021-07-28 20:10:16 +020068| Vim old | 0.578598 |
69| Python | 0.152040 |
70| Lua | 0.164917 |
71| LuaJit | 0.128400 |
72| Vim new | 0.079692 |
73
74[These times were measured on a different system by Dominique Pelle]
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010075
76The differences are smaller, but Vim 9 script is clearly the fastest.
Bram Moolenaar53f7fcc2021-07-28 20:10:16 +020077Using LuaJIT is only a little bit faster than plain Lua here, clearly the call
78back to the Vim code is costly.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010079
80How does Vim9 script work? The function is first compiled into a sequence of
81instructions. Each instruction has one or two parameters and a stack is
82used to store intermediate results. Local variables are also on the
83stack, space is reserved during compilation. This is a fairly normal
84way of compilation into an intermediate format, specialized for Vim,
85e.g. each stack item is a typeval_T. And one of the instructions is
86"execute Ex command", for commands that are not compiled.
87
88
Bram Moolenaar4f4d51a2020-10-11 13:57:40 +020089## 2. DEPRIORITIZE INTERFACES
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010090
91Attempts have been made to implement functionality with built-in script
92languages such as Python, Perl, Lua, Tcl and Ruby. This never gained much
93foothold, for various reasons.
94
95Instead of using script language support in Vim:
96* Encourage implementing external tools in any language and communicate
97 with them. The job and channel support already makes this possible.
98 Really any language can be used, also Java and Go, which are not
99 available built-in.
Bram Moolenaare7b1ea02020-08-07 19:54:59 +0200100* No priority for the built-in language interfaces. They will have to be kept
101 for backwards compatibility, but many users won't need a Vim build with these
102 interfaces.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100103* Improve the Vim script language, it is used to communicate with the external
104 tool and implements the Vim side of the interface. Also, it can be used when
105 an external tool is undesired.
106
Bram Moolenaar3d1cde82020-08-15 18:55:18 +0200107Altogether this creates a clear situation: Vim with the +eval feature
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100108will be sufficient for most plugins, while some plugins require
109installing a tool that can be written in any language. No confusion
110about having Vim but the plugin not working because some specific
111language is missing. This is a good long term goal.
112
113Rationale: Why is it better to run a tool separately from Vim than using a
114built-in interface and interpreter? Take for example something that is
115written in Python:
116* The built-in interface uses the embedded python interpreter. This is less
117 well maintained than the python command. Building Vim with it requires
118 installing developer packages. If loaded dynamically there can be a version
119 mismatch.
120* When running the tool externally the standard python command can be used,
121 which is quite often available by default or can be easily installed.
122* The built-in interface has an API that is unique for Vim with Python. This is
123 an extra API to learn.
124* A .py file can be compiled into a .pyc file and execute much faster.
125* Inside Vim multi-threading can cause problems, since the Vim core is single
126 threaded. In an external tool there are no such problems.
127* The Vim part is written in .vim files, the Python part is in .py files, this
128 is nicely separated.
129* Disadvantage: An interface needs to be made between Vim and Python.
130 JSON is available for this, and it's fairly easy to use. But it still
131 requires implementing asynchronous communication.
132
133
134## 3. BETTER VIM SCRIPT
135
136To make Vim faster a new way of defining a function needs to be added.
137While we are doing that, since the lines in this function won't be fully
138backwards compatible anyway, we can also make Vim script easier to use.
139In other words: "less weird". Making it work more like modern
140programming languages will help. No surprises.
141
142A good example is how in a function the arguments are prefixed with
143"a:". No other language I know does that, so let's drop it.
144
145Taking this one step further is also dropping "s:" for script-local variables;
146everything at the script level is script-local by default. Since this is not
147backwards compatible it requires a new script style: Vim9 script!
148
Bram Moolenaare7b1ea02020-08-07 19:54:59 +0200149To avoid having more variations, the syntax inside a compiled function is the
150same as in Vim9 script. Thus you have legacy syntax and Vim9 syntax.
151
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100152It should be possible to convert code from other languages to Vim
153script. We can add functionality to make this easier. This still needs
154to be discussed, but we can consider adding type checking and a simple
155form of classes. If you look at JavaScript for example, it has gone
156through these stages over time, adding real class support and now
157TypeScript adds type checking. But we'll have to see how much of that
158we actually want to include in Vim script. Ideally a conversion tool
159can take Python, JavaScript or TypeScript code and convert it to Vim
160script, with only some things that cannot be converted.
161
162Vim script won't work the same as any specific language, but we can use
163mechanisms that are commonly known, ideally with the same syntax. One
164thing I have been thinking of is assignments without ":let". I often
165make that mistake (after writing JavaScript especially). I think it is
166possible, if we make local variables shadow commands. That should be OK,
167if you shadow a command you want to use, just rename the variable.
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100168Using "var" and "const" to declare a variable, like in JavaScript and
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100169TypeScript, can work:
170
171
172``` vim
173def MyFunction(arg: number): number
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100174 var local = 1
175 var todo = arg
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100176 const ADD = 88
177 while todo > 0
178 local += ADD
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100179 todo -= 1
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100180 endwhile
181 return local
182enddef
183```
184
185The similarity with JavaScript/TypeScript can also be used for dependencies
186between files. Vim currently uses the `:source` command, which has several
187disadvantages:
188* In the sourced script, is not clear what it provides. By default all
189 functions are global and can be used elsewhere.
190* In a script that sources other scripts, it is not clear what function comes
191 from what sourced script. Finding the implementation is a hassle.
192* Prevention of loading the whole script twice must be manually implemented.
193
194We can use the `:import` and `:export` commands from the JavaScript standard to
195make this much better. For example, in script "myfunction.vim" define a
196function and export it:
197
198``` vim
199vim9script " Vim9 script syntax used here
200
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100201var local = 'local variable is not exported, script-local'
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100202
203export def MyFunction() " exported function
204...
205
206def LocalFunction() " not exported, script-local
207...
208```
209
210And in another script import the function:
211
212``` vim
213vim9script " Vim9 script syntax used here
214
215import MyFunction from 'myfunction.vim'
216```
217
218This looks like JavaScript/TypeScript, thus many users will understand the
219syntax.
220
221These are ideas, this will take time to design, discuss and implement.
222Eventually this will lead to Vim 9!
223
224
225## Code for sum time measurements
226
227Vim was build with -O2.
228
229``` vim
230func VimOld()
231 let sum = 0
232 for i in range(1, 2999999)
233 let sum += i
234 endfor
235 return sum
236endfunc
237
238func Python()
239 py3 << END
240sum = 0
241for i in range(1, 3000000):
242 sum += i
243END
244 return py3eval('sum')
245endfunc
246
247func Lua()
248 lua << END
249 sum = 0
250 for i = 1, 2999999 do
251 sum = sum + i
252 end
253END
254 return luaeval('sum')
255endfunc
256
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100257def VimNew(): number
258 var sum = 0
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100259 for i in range(1, 2999999)
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100260 sum += i
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100261 endfor
262 return sum
263enddef
264
265let start = reltime()
266echo VimOld()
267echo 'Vim old: ' .. reltimestr(reltime(start))
268
269let start = reltime()
270echo Python()
271echo 'Python: ' .. reltimestr(reltime(start))
272
273let start = reltime()
274echo Lua()
275echo 'Lua: ' .. reltimestr(reltime(start))
276
277let start = reltime()
278echo VimNew()
279echo 'Vim new: ' .. reltimestr(reltime(start))
280```
281
282## Code for indent time measurements
283
284``` vim
285def VimNew(): number
Bram Moolenaar4466ad62020-11-21 13:16:30 +0100286 var totallen = 0
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100287 for i in range(1, 100000)
288 setline(i, ' ' .. getline(i))
289 totallen += len(getline(i))
290 endfor
291 return totallen
292enddef
293
294func VimOld()
295 let totallen = 0
296 for i in range(1, 100000)
297 call setline(i, ' ' .. getline(i))
298 let totallen += len(getline(i))
299 endfor
300 return totallen
301endfunc
302
303func Lua()
304 lua << END
305 b = vim.buffer()
306 totallen = 0
307 for i = 1, 100000 do
308 b[i] = " " .. b[i]
309 totallen = totallen + string.len(b[i])
310 end
311END
312 return luaeval('totallen')
313endfunc
314
315func Python()
316 py3 << END
317cb = vim.current.buffer
318totallen = 0
319for i in range(0, 100000):
320 cb[i] = ' ' + cb[i]
321 totallen += len(cb[i])
322END
323 return py3eval('totallen')
324endfunc
325
326new
327call setline(1, range(100000))
328let start = reltime()
329echo VimOld()
330echo 'Vim old: ' .. reltimestr(reltime(start))
331bwipe!
332
333new
334call setline(1, range(100000))
335let start = reltime()
336echo Python()
337echo 'Python: ' .. reltimestr(reltime(start))
338bwipe!
Bram Moolenaar89a9c152021-08-29 21:55:35 +0200339
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100340new
341call setline(1, range(100000))
342let start = reltime()
343echo Lua()
344echo 'Lua: ' .. reltimestr(reltime(start))
345bwipe!
346
347new
348call setline(1, range(100000))
349let start = reltime()
350echo VimNew()
351echo 'Vim new: ' .. reltimestr(reltime(start))
352bwipe!
353```