xref: /vim-8.2.3635/README_VIM9.md (revision 89a9c159)
18a7d6542SBram Moolenaar![Vim Logo](https://github.com/vim/vim/blob/master/runtime/vimlogo.gif)
28a7d6542SBram Moolenaar
38a7d6542SBram Moolenaar# What is Vim9?
48a7d6542SBram Moolenaar
58a7d6542SBram MoolenaarThis is an experimental side of [Vim](https://github.com/vim/vim).
68a7d6542SBram MoolenaarIt explores ways of making Vim script faster and better.
78a7d6542SBram Moolenaar
8e7b1ea02SBram MoolenaarWARNING: The Vim9 script features are still under development, anything can
9e7b1ea02SBram Moolenaarbreak!
108a7d6542SBram Moolenaar
118a7d6542SBram Moolenaar# Why Vim9?
128a7d6542SBram Moolenaar
138a7d6542SBram Moolenaar## 1. FASTER VIM SCRIPT
148a7d6542SBram Moolenaar
158a7d6542SBram MoolenaarThe third item on the poll results of 2018, after popup windows and text
168a7d6542SBram Moolenaarproperties, is faster Vim script.  So how do we do that?
178a7d6542SBram Moolenaar
188a7d6542SBram MoolenaarI have been throwing some ideas around, and soon came to the conclusion
198a7d6542SBram Moolenaarthat the current way functions are called and executed, with
208a7d6542SBram Moolenaardictionaries for the arguments and local variables, is never going to be
218a7d6542SBram Moolenaarvery fast.  We're lucky if we can make it twice as fast.  The overhead
228a7d6542SBram Moolenaarof a function call and executing every line is just too high.
238a7d6542SBram Moolenaar
248a7d6542SBram MoolenaarSo what then?  We can only make something fast by having a new way of
258a7d6542SBram Moolenaardefining a function, with similar but different properties of the old
268a7d6542SBram Moolenaarway:
278a7d6542SBram Moolenaar* Arguments are only available by name, not through the a: dictionary or
288a7d6542SBram Moolenaar  the a:000 list.
298a7d6542SBram Moolenaar* Local variables are not available in an l: dictionary.
308a7d6542SBram Moolenaar* A few more things that slow us down, such as exception handling details.
318a7d6542SBram Moolenaar
328a7d6542SBram MoolenaarI Implemented a "proof of concept" and measured the time to run a simple
338a7d6542SBram Moolenaarfor loop with an addition (Justin used this example in his presentation,
348a7d6542SBram Moolenaarfull code is below):
358a7d6542SBram Moolenaar
368a7d6542SBram Moolenaar``` vim
378a7d6542SBram Moolenaar  let sum = 0
388a7d6542SBram Moolenaar  for i in range(1, 2999999)
398a7d6542SBram Moolenaar    let sum += i
408a7d6542SBram Moolenaar  endfor
418a7d6542SBram Moolenaar```
428a7d6542SBram Moolenaar
438a7d6542SBram Moolenaar| how     | time in sec |
448a7d6542SBram Moolenaar| --------| -------- |
458a7d6542SBram Moolenaar| Vim old | 5.018541 |
468a7d6542SBram Moolenaar| Python  | 0.369598 |
478a7d6542SBram Moolenaar| Lua     | 0.078817 |
48*53f7fcccSBram Moolenaar| LuaJit  | 0.004245 |
498a7d6542SBram Moolenaar| Vim new | 0.073595 |
508a7d6542SBram Moolenaar
518a7d6542SBram MoolenaarThat looks very promising!  It's just one example, but it shows how much
528a7d6542SBram Moolenaarwe can gain, and also that Vim script can be faster than builtin
538a7d6542SBram Moolenaarinterfaces.
548a7d6542SBram Moolenaar
55*53f7fcccSBram MoolenaarLuaJit is much faster at Lua-only instructions.  In practice the script would
56*53f7fcccSBram Moolenaarnot do something useless as counting but change the text.  For example,
57*53f7fcccSBram Moolenaarreindent all the lines:
588a7d6542SBram Moolenaar
598a7d6542SBram Moolenaar``` vim
608a7d6542SBram Moolenaar  let totallen = 0
618a7d6542SBram Moolenaar  for i in range(1, 100000)
628a7d6542SBram Moolenaar    call setline(i, '    ' .. getline(i))
638a7d6542SBram Moolenaar    let totallen += len(getline(i))
648a7d6542SBram Moolenaar  endfor
658a7d6542SBram Moolenaar```
668a7d6542SBram Moolenaar
678a7d6542SBram Moolenaar| how     | time in sec |
688a7d6542SBram Moolenaar| --------| -------- |
69*53f7fcccSBram Moolenaar| Vim old | 0.578598 |
70*53f7fcccSBram Moolenaar| Python  | 0.152040 |
71*53f7fcccSBram Moolenaar| Lua     | 0.164917 |
72*53f7fcccSBram Moolenaar| LuaJit  | 0.128400 |
73*53f7fcccSBram Moolenaar| Vim new | 0.079692 |
74*53f7fcccSBram Moolenaar
75*53f7fcccSBram Moolenaar[These times were measured on a different system by Dominique Pelle]
768a7d6542SBram Moolenaar
778a7d6542SBram MoolenaarThe differences are smaller, but Vim 9 script is clearly the fastest.
78*53f7fcccSBram MoolenaarUsing LuaJIT is only a little bit faster than plain Lua here, clearly the call
79*53f7fcccSBram Moolenaarback to the Vim code is costly.
808a7d6542SBram Moolenaar
818a7d6542SBram MoolenaarHow does Vim9 script work?  The function is first compiled into a sequence of
828a7d6542SBram Moolenaarinstructions.  Each instruction has one or two parameters and a stack is
838a7d6542SBram Moolenaarused to store intermediate results.  Local variables are also on the
848a7d6542SBram Moolenaarstack, space is reserved during compilation.  This is a fairly normal
858a7d6542SBram Moolenaarway of compilation into an intermediate format, specialized for Vim,
868a7d6542SBram Moolenaare.g. each stack item is a typeval_T.  And one of the instructions is
878a7d6542SBram Moolenaar"execute Ex command", for commands that are not compiled.
888a7d6542SBram Moolenaar
898a7d6542SBram Moolenaar
904f4d51a9SBram Moolenaar## 2. DEPRIORITIZE INTERFACES
918a7d6542SBram Moolenaar
928a7d6542SBram MoolenaarAttempts have been made to implement functionality with built-in script
938a7d6542SBram Moolenaarlanguages such as Python, Perl, Lua, Tcl and Ruby.  This never gained much
948a7d6542SBram Moolenaarfoothold, for various reasons.
958a7d6542SBram Moolenaar
968a7d6542SBram MoolenaarInstead of using script language support in Vim:
978a7d6542SBram Moolenaar* Encourage implementing external tools in any language and communicate
988a7d6542SBram Moolenaar  with them.  The job and channel support already makes this possible.
998a7d6542SBram Moolenaar  Really any language can be used, also Java and Go, which are not
1008a7d6542SBram Moolenaar  available built-in.
101e7b1ea02SBram Moolenaar* No priority for the built-in language interfaces.  They will have to be kept
102e7b1ea02SBram Moolenaar  for backwards compatibility, but many users won't need a Vim build with these
103e7b1ea02SBram Moolenaar  interfaces.
1048a7d6542SBram Moolenaar* Improve the Vim script language, it is used to communicate with the external
1058a7d6542SBram Moolenaar  tool and implements the Vim side of the interface.  Also, it can be used when
1068a7d6542SBram Moolenaar  an external tool is undesired.
1078a7d6542SBram Moolenaar
1083d1cde8aSBram MoolenaarAltogether this creates a clear situation: Vim with the +eval feature
1098a7d6542SBram Moolenaarwill be sufficient for most plugins, while some plugins require
1108a7d6542SBram Moolenaarinstalling a tool that can be written in any language.  No confusion
1118a7d6542SBram Moolenaarabout having Vim but the plugin not working because some specific
1128a7d6542SBram Moolenaarlanguage is missing.  This is a good long term goal.
1138a7d6542SBram Moolenaar
1148a7d6542SBram MoolenaarRationale: Why is it better to run a tool separately from Vim than using a
1158a7d6542SBram Moolenaarbuilt-in interface and interpreter?  Take for example something that is
1168a7d6542SBram Moolenaarwritten in Python:
1178a7d6542SBram Moolenaar* The built-in interface uses the embedded python interpreter.  This is less
1188a7d6542SBram Moolenaar  well maintained than the python command.  Building Vim with it requires
1198a7d6542SBram Moolenaar  installing developer packages.  If loaded dynamically there can be a version
1208a7d6542SBram Moolenaar  mismatch.
1218a7d6542SBram Moolenaar* When running the tool externally the standard python command can be used,
1228a7d6542SBram Moolenaar  which is quite often available by default or can be easily installed.
1238a7d6542SBram Moolenaar* The built-in interface has an API that is unique for Vim with Python. This is
1248a7d6542SBram Moolenaar  an extra API to learn.
1258a7d6542SBram Moolenaar* A .py file can be compiled into a .pyc file and execute much faster.
1268a7d6542SBram Moolenaar* Inside Vim multi-threading can cause problems, since the Vim core is single
1278a7d6542SBram Moolenaar  threaded.  In an external tool there are no such problems.
1288a7d6542SBram Moolenaar* The Vim part is written in .vim files, the Python part is in .py files, this
1298a7d6542SBram Moolenaar  is nicely separated.
1308a7d6542SBram Moolenaar* Disadvantage: An interface needs to be made between Vim and Python.
1318a7d6542SBram Moolenaar  JSON is available for this, and it's fairly easy to use.  But it still
1328a7d6542SBram Moolenaar  requires implementing asynchronous communication.
1338a7d6542SBram Moolenaar
1348a7d6542SBram Moolenaar
1358a7d6542SBram Moolenaar## 3. BETTER VIM SCRIPT
1368a7d6542SBram Moolenaar
1378a7d6542SBram MoolenaarTo make Vim faster a new way of defining a function needs to be added.
1388a7d6542SBram MoolenaarWhile we are doing that, since the lines in this function won't be fully
1398a7d6542SBram Moolenaarbackwards compatible anyway, we can also make Vim script easier to use.
1408a7d6542SBram MoolenaarIn other words: "less weird".  Making it work more like modern
1418a7d6542SBram Moolenaarprogramming languages will help.  No surprises.
1428a7d6542SBram Moolenaar
1438a7d6542SBram MoolenaarA good example is how in a function the arguments are prefixed with
1448a7d6542SBram Moolenaar"a:". No other language I know does that, so let's drop it.
1458a7d6542SBram Moolenaar
1468a7d6542SBram MoolenaarTaking this one step further is also dropping "s:" for script-local variables;
1478a7d6542SBram Moolenaareverything at the script level is script-local by default.  Since this is not
1488a7d6542SBram Moolenaarbackwards compatible it requires a new script style: Vim9 script!
1498a7d6542SBram Moolenaar
150e7b1ea02SBram MoolenaarTo avoid having more variations, the syntax inside a compiled function is the
151e7b1ea02SBram Moolenaarsame as in Vim9 script.  Thus you have legacy syntax and Vim9 syntax.
152e7b1ea02SBram Moolenaar
1538a7d6542SBram MoolenaarIt should be possible to convert code from other languages to Vim
1548a7d6542SBram Moolenaarscript.  We can add functionality to make this easier.  This still needs
1558a7d6542SBram Moolenaarto be discussed, but we can consider adding type checking and a simple
1568a7d6542SBram Moolenaarform of classes.  If you look at JavaScript for example, it has gone
1578a7d6542SBram Moolenaarthrough these stages over time, adding real class support and now
1588a7d6542SBram MoolenaarTypeScript adds type checking.  But we'll have to see how much of that
1598a7d6542SBram Moolenaarwe actually want to include in Vim script.  Ideally a conversion tool
1608a7d6542SBram Moolenaarcan take Python, JavaScript or TypeScript code and convert it to Vim
1618a7d6542SBram Moolenaarscript, with only some things that cannot be converted.
1628a7d6542SBram Moolenaar
1638a7d6542SBram MoolenaarVim script won't work the same as any specific language, but we can use
1648a7d6542SBram Moolenaarmechanisms that are commonly known, ideally with the same syntax.  One
1658a7d6542SBram Moolenaarthing I have been thinking of is assignments without ":let".  I often
1668a7d6542SBram Moolenaarmake that mistake (after writing JavaScript especially).  I think it is
1678a7d6542SBram Moolenaarpossible, if we make local variables shadow commands.  That should be OK,
1688a7d6542SBram Moolenaarif you shadow a command you want to use, just rename the variable.
1694466ad6bSBram MoolenaarUsing "var" and "const" to declare a variable, like in JavaScript and
1708a7d6542SBram MoolenaarTypeScript, can work:
1718a7d6542SBram Moolenaar
1728a7d6542SBram Moolenaar
1738a7d6542SBram Moolenaar``` vim
1748a7d6542SBram Moolenaardef MyFunction(arg: number): number
1754466ad6bSBram Moolenaar   var local = 1
1764466ad6bSBram Moolenaar   var todo = arg
1778a7d6542SBram Moolenaar   const ADD = 88
1788a7d6542SBram Moolenaar   while todo > 0
1798a7d6542SBram Moolenaar      local += ADD
1804466ad6bSBram Moolenaar      todo -= 1
1818a7d6542SBram Moolenaar   endwhile
1828a7d6542SBram Moolenaar   return local
1838a7d6542SBram Moolenaarenddef
1848a7d6542SBram Moolenaar```
1858a7d6542SBram Moolenaar
1868a7d6542SBram MoolenaarThe similarity with JavaScript/TypeScript can also be used for dependencies
1878a7d6542SBram Moolenaarbetween files.  Vim currently uses the `:source` command, which has several
1888a7d6542SBram Moolenaardisadvantages:
1898a7d6542SBram Moolenaar*   In the sourced script, is not clear what it provides.  By default all
1908a7d6542SBram Moolenaar    functions are global and can be used elsewhere.
1918a7d6542SBram Moolenaar*   In a script that sources other scripts, it is not clear what function comes
1928a7d6542SBram Moolenaar    from what sourced script.  Finding the implementation is a hassle.
1938a7d6542SBram Moolenaar*   Prevention of loading the whole script twice must be manually implemented.
1948a7d6542SBram Moolenaar
1958a7d6542SBram MoolenaarWe can use the `:import` and `:export` commands from the JavaScript standard to
1968a7d6542SBram Moolenaarmake this much better.  For example, in script "myfunction.vim" define a
1978a7d6542SBram Moolenaarfunction and export it:
1988a7d6542SBram Moolenaar
1998a7d6542SBram Moolenaar``` vim
2008a7d6542SBram Moolenaarvim9script  " Vim9 script syntax used here
2018a7d6542SBram Moolenaar
2024466ad6bSBram Moolenaarvar local = 'local variable is not exported, script-local'
2038a7d6542SBram Moolenaar
2048a7d6542SBram Moolenaarexport def MyFunction()  " exported function
2058a7d6542SBram Moolenaar...
2068a7d6542SBram Moolenaar
2078a7d6542SBram Moolenaardef LocalFunction() " not exported, script-local
2088a7d6542SBram Moolenaar...
2098a7d6542SBram Moolenaar```
2108a7d6542SBram Moolenaar
2118a7d6542SBram MoolenaarAnd in another script import the function:
2128a7d6542SBram Moolenaar
2138a7d6542SBram Moolenaar``` vim
2148a7d6542SBram Moolenaarvim9script  " Vim9 script syntax used here
2158a7d6542SBram Moolenaar
2168a7d6542SBram Moolenaarimport MyFunction from 'myfunction.vim'
2178a7d6542SBram Moolenaar```
2188a7d6542SBram Moolenaar
2198a7d6542SBram MoolenaarThis looks like JavaScript/TypeScript, thus many users will understand the
2208a7d6542SBram Moolenaarsyntax.
2218a7d6542SBram Moolenaar
2228a7d6542SBram MoolenaarThese are ideas, this will take time to design, discuss and implement.
2238a7d6542SBram MoolenaarEventually this will lead to Vim 9!
2248a7d6542SBram Moolenaar
2258a7d6542SBram Moolenaar
2268a7d6542SBram Moolenaar## Code for sum time measurements
2278a7d6542SBram Moolenaar
2288a7d6542SBram MoolenaarVim was build with -O2.
2298a7d6542SBram Moolenaar
2308a7d6542SBram Moolenaar``` vim
2318a7d6542SBram Moolenaarfunc VimOld()
2328a7d6542SBram Moolenaar  let sum = 0
2338a7d6542SBram Moolenaar  for i in range(1, 2999999)
2348a7d6542SBram Moolenaar    let sum += i
2358a7d6542SBram Moolenaar  endfor
2368a7d6542SBram Moolenaar  return sum
2378a7d6542SBram Moolenaarendfunc
2388a7d6542SBram Moolenaar
2398a7d6542SBram Moolenaarfunc Python()
2408a7d6542SBram Moolenaar  py3 << END
2418a7d6542SBram Moolenaarsum = 0
2428a7d6542SBram Moolenaarfor i in range(1, 3000000):
2438a7d6542SBram Moolenaar  sum += i
2448a7d6542SBram MoolenaarEND
2458a7d6542SBram Moolenaar  return py3eval('sum')
2468a7d6542SBram Moolenaarendfunc
2478a7d6542SBram Moolenaar
2488a7d6542SBram Moolenaarfunc Lua()
2498a7d6542SBram Moolenaar  lua << END
2508a7d6542SBram Moolenaar    sum = 0
2518a7d6542SBram Moolenaar    for i = 1, 2999999 do
2528a7d6542SBram Moolenaar      sum = sum + i
2538a7d6542SBram Moolenaar    end
2548a7d6542SBram MoolenaarEND
2558a7d6542SBram Moolenaar  return luaeval('sum')
2568a7d6542SBram Moolenaarendfunc
2578a7d6542SBram Moolenaar
2584466ad6bSBram Moolenaardef VimNew(): number
2594466ad6bSBram Moolenaar  var sum = 0
2608a7d6542SBram Moolenaar  for i in range(1, 2999999)
2614466ad6bSBram Moolenaar    sum += i
2628a7d6542SBram Moolenaar  endfor
2638a7d6542SBram Moolenaar  return sum
2648a7d6542SBram Moolenaarenddef
2658a7d6542SBram Moolenaar
2668a7d6542SBram Moolenaarlet start = reltime()
2678a7d6542SBram Moolenaarecho VimOld()
2688a7d6542SBram Moolenaarecho 'Vim old: ' .. reltimestr(reltime(start))
2698a7d6542SBram Moolenaar
2708a7d6542SBram Moolenaarlet start = reltime()
2718a7d6542SBram Moolenaarecho Python()
2728a7d6542SBram Moolenaarecho 'Python: ' .. reltimestr(reltime(start))
2738a7d6542SBram Moolenaar
2748a7d6542SBram Moolenaarlet start = reltime()
2758a7d6542SBram Moolenaarecho Lua()
2768a7d6542SBram Moolenaarecho 'Lua: ' .. reltimestr(reltime(start))
2778a7d6542SBram Moolenaar
2788a7d6542SBram Moolenaarlet start = reltime()
2798a7d6542SBram Moolenaarecho VimNew()
2808a7d6542SBram Moolenaarecho 'Vim new: ' .. reltimestr(reltime(start))
2818a7d6542SBram Moolenaar```
2828a7d6542SBram Moolenaar
2838a7d6542SBram Moolenaar## Code for indent time measurements
2848a7d6542SBram Moolenaar
2858a7d6542SBram Moolenaar``` vim
2868a7d6542SBram Moolenaardef VimNew(): number
2874466ad6bSBram Moolenaar  var totallen = 0
2888a7d6542SBram Moolenaar  for i in range(1, 100000)
2898a7d6542SBram Moolenaar    setline(i, '    ' .. getline(i))
2908a7d6542SBram Moolenaar    totallen += len(getline(i))
2918a7d6542SBram Moolenaar  endfor
2928a7d6542SBram Moolenaar  return totallen
2938a7d6542SBram Moolenaarenddef
2948a7d6542SBram Moolenaar
2958a7d6542SBram Moolenaarfunc VimOld()
2968a7d6542SBram Moolenaar  let totallen = 0
2978a7d6542SBram Moolenaar  for i in range(1, 100000)
2988a7d6542SBram Moolenaar    call setline(i, '    ' .. getline(i))
2998a7d6542SBram Moolenaar    let totallen += len(getline(i))
3008a7d6542SBram Moolenaar  endfor
3018a7d6542SBram Moolenaar  return totallen
3028a7d6542SBram Moolenaarendfunc
3038a7d6542SBram Moolenaar
3048a7d6542SBram Moolenaarfunc Lua()
3058a7d6542SBram Moolenaar  lua << END
3068a7d6542SBram Moolenaar    b = vim.buffer()
3078a7d6542SBram Moolenaar    totallen = 0
3088a7d6542SBram Moolenaar    for i = 1, 100000 do
3098a7d6542SBram Moolenaar      b[i] = "    " .. b[i]
3108a7d6542SBram Moolenaar      totallen = totallen + string.len(b[i])
3118a7d6542SBram Moolenaar    end
3128a7d6542SBram MoolenaarEND
3138a7d6542SBram Moolenaar  return luaeval('totallen')
3148a7d6542SBram Moolenaarendfunc
3158a7d6542SBram Moolenaar
3168a7d6542SBram Moolenaarfunc Python()
3178a7d6542SBram Moolenaar  py3 << END
3188a7d6542SBram Moolenaarcb = vim.current.buffer
3198a7d6542SBram Moolenaartotallen = 0
3208a7d6542SBram Moolenaarfor i in range(0, 100000):
3218a7d6542SBram Moolenaar  cb[i] = '    ' + cb[i]
3228a7d6542SBram Moolenaar  totallen += len(cb[i])
3238a7d6542SBram MoolenaarEND
3248a7d6542SBram Moolenaar  return py3eval('totallen')
3258a7d6542SBram Moolenaarendfunc
3268a7d6542SBram Moolenaar
3278a7d6542SBram Moolenaarnew
3288a7d6542SBram Moolenaarcall setline(1, range(100000))
3298a7d6542SBram Moolenaarlet start = reltime()
3308a7d6542SBram Moolenaarecho VimOld()
3318a7d6542SBram Moolenaarecho 'Vim old: ' .. reltimestr(reltime(start))
3328a7d6542SBram Moolenaarbwipe!
3338a7d6542SBram Moolenaar
3348a7d6542SBram Moolenaarnew
3358a7d6542SBram Moolenaarcall setline(1, range(100000))
3368a7d6542SBram Moolenaarlet start = reltime()
3378a7d6542SBram Moolenaarecho Python()
3388a7d6542SBram Moolenaarecho 'Python: ' .. reltimestr(reltime(start))
3398a7d6542SBram Moolenaarbwipe!
3408a7d6542SBram Moolenaar
3418a7d6542SBram Moolenaarnew
3428a7d6542SBram Moolenaarcall setline(1, range(100000))
3438a7d6542SBram Moolenaarlet start = reltime()
3448a7d6542SBram Moolenaarecho Lua()
3458a7d6542SBram Moolenaarecho 'Lua: ' .. reltimestr(reltime(start))
3468a7d6542SBram Moolenaarbwipe!
3478a7d6542SBram Moolenaar
3488a7d6542SBram Moolenaarnew
3498a7d6542SBram Moolenaarcall setline(1, range(100000))
3508a7d6542SBram Moolenaarlet start = reltime()
3518a7d6542SBram Moolenaarecho VimNew()
3528a7d6542SBram Moolenaarecho 'Vim new: ' .. reltimestr(reltime(start))
3538a7d6542SBram Moolenaarbwipe!
3548a7d6542SBram Moolenaar```
355