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