blob: 1d296354597792c1a72a39d644dd1784215ae19d [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.
73
74How does Vim9 script work? The function is first compiled into a sequence of
75instructions. Each instruction has one or two parameters and a stack is
76used to store intermediate results. Local variables are also on the
77stack, space is reserved during compilation. This is a fairly normal
78way of compilation into an intermediate format, specialized for Vim,
79e.g. each stack item is a typeval_T. And one of the instructions is
80"execute Ex command", for commands that are not compiled.
81
82
83## 2. PHASING OUT INTERFACES
84
85Attempts have been made to implement functionality with built-in script
86languages such as Python, Perl, Lua, Tcl and Ruby. This never gained much
87foothold, for various reasons.
88
89Instead of using script language support in Vim:
90* Encourage implementing external tools in any language and communicate
91 with them. The job and channel support already makes this possible.
92 Really any language can be used, also Java and Go, which are not
93 available built-in.
Bram Moolenaare7b1ea02020-08-07 19:54:59 +020094* No priority for the built-in language interfaces. They will have to be kept
95 for backwards compatibility, but many users won't need a Vim build with these
96 interfaces.
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010097* Improve the Vim script language, it is used to communicate with the external
98 tool and implements the Vim side of the interface. Also, it can be used when
99 an external tool is undesired.
100
Bram Moolenaar3d1cde82020-08-15 18:55:18 +0200101Altogether this creates a clear situation: Vim with the +eval feature
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100102will be sufficient for most plugins, while some plugins require
103installing a tool that can be written in any language. No confusion
104about having Vim but the plugin not working because some specific
105language is missing. This is a good long term goal.
106
107Rationale: Why is it better to run a tool separately from Vim than using a
108built-in interface and interpreter? Take for example something that is
109written in Python:
110* The built-in interface uses the embedded python interpreter. This is less
111 well maintained than the python command. Building Vim with it requires
112 installing developer packages. If loaded dynamically there can be a version
113 mismatch.
114* When running the tool externally the standard python command can be used,
115 which is quite often available by default or can be easily installed.
116* The built-in interface has an API that is unique for Vim with Python. This is
117 an extra API to learn.
118* A .py file can be compiled into a .pyc file and execute much faster.
119* Inside Vim multi-threading can cause problems, since the Vim core is single
120 threaded. In an external tool there are no such problems.
121* The Vim part is written in .vim files, the Python part is in .py files, this
122 is nicely separated.
123* Disadvantage: An interface needs to be made between Vim and Python.
124 JSON is available for this, and it's fairly easy to use. But it still
125 requires implementing asynchronous communication.
126
127
128## 3. BETTER VIM SCRIPT
129
130To make Vim faster a new way of defining a function needs to be added.
131While we are doing that, since the lines in this function won't be fully
132backwards compatible anyway, we can also make Vim script easier to use.
133In other words: "less weird". Making it work more like modern
134programming languages will help. No surprises.
135
136A good example is how in a function the arguments are prefixed with
137"a:". No other language I know does that, so let's drop it.
138
139Taking this one step further is also dropping "s:" for script-local variables;
140everything at the script level is script-local by default. Since this is not
141backwards compatible it requires a new script style: Vim9 script!
142
Bram Moolenaare7b1ea02020-08-07 19:54:59 +0200143To avoid having more variations, the syntax inside a compiled function is the
144same as in Vim9 script. Thus you have legacy syntax and Vim9 syntax.
145
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100146It should be possible to convert code from other languages to Vim
147script. We can add functionality to make this easier. This still needs
148to be discussed, but we can consider adding type checking and a simple
149form of classes. If you look at JavaScript for example, it has gone
150through these stages over time, adding real class support and now
151TypeScript adds type checking. But we'll have to see how much of that
152we actually want to include in Vim script. Ideally a conversion tool
153can take Python, JavaScript or TypeScript code and convert it to Vim
154script, with only some things that cannot be converted.
155
156Vim script won't work the same as any specific language, but we can use
157mechanisms that are commonly known, ideally with the same syntax. One
158thing I have been thinking of is assignments without ":let". I often
159make that mistake (after writing JavaScript especially). I think it is
160possible, if we make local variables shadow commands. That should be OK,
161if you shadow a command you want to use, just rename the variable.
162Using "let" and "const" to declare a variable, like in JavaScript and
163TypeScript, can work:
164
165
166``` vim
167def MyFunction(arg: number): number
168 let local = 1
169 let todo = arg
170 const ADD = 88
171 while todo > 0
172 local += ADD
173 --todo
174 endwhile
175 return local
176enddef
177```
178
179The similarity with JavaScript/TypeScript can also be used for dependencies
180between files. Vim currently uses the `:source` command, which has several
181disadvantages:
182* In the sourced script, is not clear what it provides. By default all
183 functions are global and can be used elsewhere.
184* In a script that sources other scripts, it is not clear what function comes
185 from what sourced script. Finding the implementation is a hassle.
186* Prevention of loading the whole script twice must be manually implemented.
187
188We can use the `:import` and `:export` commands from the JavaScript standard to
189make this much better. For example, in script "myfunction.vim" define a
190function and export it:
191
192``` vim
193vim9script " Vim9 script syntax used here
194
195let local = 'local variable is not exported, script-local'
196
197export def MyFunction() " exported function
198...
199
200def LocalFunction() " not exported, script-local
201...
202```
203
204And in another script import the function:
205
206``` vim
207vim9script " Vim9 script syntax used here
208
209import MyFunction from 'myfunction.vim'
210```
211
212This looks like JavaScript/TypeScript, thus many users will understand the
213syntax.
214
215These are ideas, this will take time to design, discuss and implement.
216Eventually this will lead to Vim 9!
217
218
219## Code for sum time measurements
220
221Vim was build with -O2.
222
223``` vim
224func VimOld()
225 let sum = 0
226 for i in range(1, 2999999)
227 let sum += i
228 endfor
229 return sum
230endfunc
231
232func Python()
233 py3 << END
234sum = 0
235for i in range(1, 3000000):
236 sum += i
237END
238 return py3eval('sum')
239endfunc
240
241func Lua()
242 lua << END
243 sum = 0
244 for i = 1, 2999999 do
245 sum = sum + i
246 end
247END
248 return luaeval('sum')
249endfunc
250
251def VimNew()
252 let sum = 0
253 for i in range(1, 2999999)
254 let sum += i
255 endfor
256 return sum
257enddef
258
259let start = reltime()
260echo VimOld()
261echo 'Vim old: ' .. reltimestr(reltime(start))
262
263let start = reltime()
264echo Python()
265echo 'Python: ' .. reltimestr(reltime(start))
266
267let start = reltime()
268echo Lua()
269echo 'Lua: ' .. reltimestr(reltime(start))
270
271let start = reltime()
272echo VimNew()
273echo 'Vim new: ' .. reltimestr(reltime(start))
274```
275
276## Code for indent time measurements
277
278``` vim
279def VimNew(): number
280 let totallen = 0
281 for i in range(1, 100000)
282 setline(i, ' ' .. getline(i))
283 totallen += len(getline(i))
284 endfor
285 return totallen
286enddef
287
288func VimOld()
289 let totallen = 0
290 for i in range(1, 100000)
291 call setline(i, ' ' .. getline(i))
292 let totallen += len(getline(i))
293 endfor
294 return totallen
295endfunc
296
297func Lua()
298 lua << END
299 b = vim.buffer()
300 totallen = 0
301 for i = 1, 100000 do
302 b[i] = " " .. b[i]
303 totallen = totallen + string.len(b[i])
304 end
305END
306 return luaeval('totallen')
307endfunc
308
309func Python()
310 py3 << END
311cb = vim.current.buffer
312totallen = 0
313for i in range(0, 100000):
314 cb[i] = ' ' + cb[i]
315 totallen += len(cb[i])
316END
317 return py3eval('totallen')
318endfunc
319
320new
321call setline(1, range(100000))
322let start = reltime()
323echo VimOld()
324echo 'Vim old: ' .. reltimestr(reltime(start))
325bwipe!
326
327new
328call setline(1, range(100000))
329let start = reltime()
330echo Python()
331echo 'Python: ' .. reltimestr(reltime(start))
332bwipe!
333
334new
335call setline(1, range(100000))
336let start = reltime()
337echo Lua()
338echo 'Lua: ' .. reltimestr(reltime(start))
339bwipe!
340
341new
342call setline(1, range(100000))
343let start = reltime()
344echo VimNew()
345echo 'Vim new: ' .. reltimestr(reltime(start))
346bwipe!
347```