1*usr_46.txt* For Vim version 8.2. Last change: 2020 Jun 14 2 3 VIM USER MANUAL - by Bram Moolenaar 4 5 Write plugins using Vim9 script 6 7 8The Vim9 script language is used for writing plugins, especially larger ones 9that use multiple files. This chapter explains how to split up a plugin into 10modules, import and export items and keep the rest local. 11 12|46.1| Introduction 13|46.2| Variable declarations 14|46.3| Functions and types 15|46.?| Using a Vim9 script from legacy script 16 17 Next chapter: |usr_90.txt| Installing Vim 18 Previous chapter: |usr_45.txt| Select your language (locale) 19Table of contents: |usr_toc.txt| 20 21============================================================================== 22*46.1* Introduction *vim9-script-intro* 23 24Vim9 script was designed to make it easier to write large Vim scripts. It 25looks more like other script languages, especially Typescript. Also, 26functions are compiled into instructions that can be executed quickly. This 27makes Vim9 script a lot faster, up to a 100 times. 28 29The basic idea is that a script file has items that are private, only used 30inside the script file, and items that are exported, used outside of the 31script file. The exported items can then be used by scripts that import them. 32That makes very clear what is defined where. 33 34Let's start with an example, a script that exports one function and has one 35private function: > 36 37 vim9script " This indicates a Vim9 script file. 38 39 export def GetMessage(): string 40 let result = '' 41 ... 42 result = GetPart(count) 43 ... 44 return result 45 enddef 46 47 def GetPart(nr: number): string 48 if nr == 4 49 return 'yes' 50 else 51 return 'no' 52 endif 53 enddef 54 55The `vim9script` command must be the very first command in the file. Without 56it Vim will assume legacy script syntax. 57 58The `export def GetMessage(): string` line starts with `export`, meaning that 59this function can be imported and called by other scripts. The line 60`def GetPart(...` does not start with `export`, this is a script-local 61function, it can only be used inside this script file. 62 63In the `export def GetMessage(): string` line you will notice the colon and 64the return type. Vim9 functions, defined with `def`, require specifying the 65type of arguments and the return type. That way Vim can compile the code 66efficiently. The GetPart function defines an argument "nr" of type "number". 67 68Notice that the assignment `result = GetPart(count)` does not use the `let` 69command. That is explained in the next section. 70 71============================================================================== 72*46.2* Variable declarations *vim9-declarations* 73 74In Vim9 script variables are declared once with a `:let` or `:const` command. 75Assigning a value is done without `:let` and it is not possible to `:unlet` 76the variable. 77 78In most cases you will want to declare the variable and initialize it at the 79same time: > 80 let myText = 'some text' 81 ... 82 myText = 'other text' 83 84The type of the variable will be inferred from the expression. In this case 85it is a string. If you initialize with a number, then the type is number: > 86 let myNumber = 1234 87 ... 88 myNumber = 0 89 90If you try to assign a string to this variable, you will get an error: > 91 let myNumber = 'this fails!' 92 93In the rare case you want a variable that can take values of any type, you 94have to specify the type: > 95 let myVar: any = 1234 96 myVar = 'text also works' 97 98You can also declare a variable without assigning a value. In that case Vim 99will initialize it to zero or empty: > 100 let word: string 101 if condition 102 word = 'yes' 103 else 104 word = 'no' 105 endif 106 107Although it's shorter to do: > 108 let word = condition ? 'yes' : 'no' 109 110============================================================================== 111*46.3* Functions and types 112 113Legacy Vim script does have type checking, but this happens at runtime, when 114the code is executed. And it's permissive, often a computation gives an 115unexpected value instead of reporting an error. Thus you can define a 116function and think it's fine, but see a problem only later when it is called: > 117 let s:collected = '' 118 func ExtendAndReturn(add) 119 let s:collected += a:add 120 return s:collected 121 endfunc 122 123Can you spot the error? Try this: > 124 echo ExtendAndReturn('text') 125And you'll see zero. Why? Because in legacy Vim script "+=" will convert the 126arguments to numbers, and any string without a number results in zero! 127 128With `:def` the type checking happens when compiling the function. For that 129you need to specify the argument types and the return type. Also notice that 130the argument is used without the "a:" prefix: > 131 let s:collected = '' 132 def ExtendAndReturn(add: string): string 133 s:collected += add 134 return s:collected 135 enddef 136 defcompile 137 138Here we use `:defcompile` to do the compilation right away, without it the 139compilation would happen when the function is called. Vim will tell you what 140you did wrong: > 141 E1013: type mismatch, expected number but got string 142 143Vim9 script is strict, it uses the "+" operator only for numbers and floats. 144For string concatenation ".." must be used. This avoids mistakes and avoids 145the automatic conversion that gave a surprising result above. So you change 146the first line of the function to: > 147 s:collected ..= add 148And now it works. 149 150If the function does not return anything, just leave out the return type: > 151 def ReportResult(result: string) 152 echo 'The result is: ' .. result 153 enddef 154 155This is also checked, if you try to return a value you'll get an error. 156 157In case you don't care about types or have a function that does work with 158multiple types, you can use the "any" type: > 159 def Store(key: string, value: any) 160 resultDict[key] = value 161 enddef 162 163============================================================================== 164*46.?* Using a Vim9 script from legacy script *source-vim9-script* 165 166In some cases you have a legacy Vim script where you want to use items from a 167Vim9 script. For example in your .vimrc you want to initialize a plugin. The 168best way to do this is to use `:import`. For example: > 169 170 import Init as NiceInit from 'myNicePlugin.vim' 171 call NiceInit('today') 172 173This finds the exported function "Init" in the Vim9 script file and makes it 174available as script-local item "NiceInit". `:import` always uses the script 175namespace, even when "s:" is not given. If "myNicePlugin.vim" was already 176sourced it is not sourced again. 177 178Besides avoiding putting any items in the global namespace (where name clashes 179can cause unexpected errors), this also means the script is sourced only once, 180no matter how many times items from it are imported. 181 182In some cases, e.g. for testing, you may just want to source the Vim9 script. 183That is OK, but then only global items will be available. The Vim9 script 184will have to make sure to use a unique name for these global items. Example: > 185 source ~/.vim/extra/myNicePlugin.vim 186 call g:NicePluginTest() 187 188============================================================================== 189 190Next chapter: |usr_90.txt| Installing Vim 191 192Copyright: see |manual-copyright| vim:tw=78:ts=8:noet:ft=help:norl: 193