Christian Brabandt | b725873 | 2024-05-12 19:04:47 +0200 | [diff] [blame] | 1 | *usr_52.txt* For Vim version 9.1. Last change: 2024 May 05 |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 2 | |
| 3 | VIM USER MANUAL - by Bram Moolenaar |
| 4 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 5 | Write larger plugins |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 6 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 7 | When plugins do more than simple things, they tend to grow big. This file |
| 8 | explains how to make sure they still load fast and how to split them up in |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 9 | smaller parts. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 10 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 11 | |52.1| Export and import |
| 12 | |52.2| Autoloading |
| 13 | |52.3| Autoloading without import/export |
| 14 | |52.4| Other mechanisms to use |
| 15 | |52.5| Using a Vim9 script from legacy script |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 16 | |
| 17 | Next chapter: |usr_90.txt| Installing Vim |
Bram Moolenaar | 30ab04e | 2022-05-14 13:33:50 +0100 | [diff] [blame] | 18 | Previous chapter: |usr_51.txt| Create a plugin |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 19 | Table of contents: |usr_toc.txt| |
| 20 | |
| 21 | ============================================================================== |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 22 | *52.1* Export and import |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 23 | |
| 24 | Vim9 script was designed to make it easier to write large Vim scripts. It |
| 25 | looks more like other script languages, especially Typescript. Also, |
| 26 | functions are compiled into instructions that can be executed quickly. This |
| 27 | makes Vim9 script a lot faster, up to a 100 times. |
| 28 | |
| 29 | The basic idea is that a script file has items that are private, only used |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 30 | inside the script file, and items that are exported, which can be used by |
| 31 | scripts that import them. That makes very clear what is defined where. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 32 | |
| 33 | Let's start with an example, a script that exports one function and has one |
| 34 | private function: > |
| 35 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 36 | vim9script |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 37 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 38 | export def GetMessage(count: string): string |
| 39 | var nr = str2nr(count) |
| 40 | var result = $'To {nr} we say ' |
| 41 | result ..= GetReply(nr) |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 42 | return result |
| 43 | enddef |
| 44 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 45 | def GetReply(nr: number): string |
| 46 | if nr == 42 |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 47 | return 'yes' |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 48 | elseif nr = 22 |
| 49 | return 'maybe' |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 50 | else |
| 51 | return 'no' |
| 52 | endif |
| 53 | enddef |
| 54 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 55 | The `vim9script` command is required, `export` only works in a |Vim9| script. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 56 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 57 | The `export def GetMessage(...` line starts with `export`, meaning that this |
| 58 | function can be called by other scripts. The line `def GetReply(...` does not |
| 59 | start with `export`, this is a script-local function, it can only be used |
| 60 | inside this script file. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 61 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 62 | Now about the script where this is imported. In this example we use this |
| 63 | layout, which works well for a plugin below the "pack" directory: |
| 64 | .../plugin/theplugin.vim |
| 65 | .../lib/getmessage.vim |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 66 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 67 | Assuming the "..." directory has been added to 'runtimepath', Vim will look |
| 68 | for plugins in the "plugin" directory and source "theplugin.vim". Vim does |
| 69 | not recognize the "lib" directory, you can put any scripts there. |
| 70 | |
| 71 | The above script that exports GetMessage() goes in lib/getmessage.vim. The |
| 72 | GetMessage() function is used in plugin/theplugin.vim: > |
| 73 | |
| 74 | vim9script |
| 75 | |
| 76 | import "../lib/getmessage.vim" |
| 77 | command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>) |
| 78 | |
| 79 | The `import` command uses a relative path, it starts with "../", which means |
| 80 | to go one directory up. For other kinds of paths see the `:import` command. |
| 81 | |
| 82 | How we can try out the command that the plugin provides: > |
| 83 | ShowMessage 1 |
| 84 | < To 1 we say no ~ |
| 85 | > |
| 86 | ShowMessage 22 |
| 87 | < To 22 we say maybe ~ |
| 88 | |
| 89 | Notice that the function GetMessage() is prefixed with the imported script |
| 90 | name "getmessage". That way, for every imported function used, you know what |
| 91 | script it was imported from. If you import several scripts each of them could |
| 92 | define a GetMessage() function: > |
| 93 | |
| 94 | vim9script |
| 95 | |
| 96 | import "../lib/getmessage.vim" |
| 97 | import "../lib/getother.vim" |
| 98 | command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>) |
| 99 | command -nargs=1 ShowOther echomsg getother.GetMessage(<f-args>) |
| 100 | |
| 101 | If the imported script name is long or you use it in many places, you can |
| 102 | shorten it by adding an "as" argument: > |
| 103 | import "../lib/getmessage.vim" as msg |
| 104 | command -nargs=1 ShowMessage echomsg msg.GetMessage(<f-args>) |
| 105 | |
| 106 | |
| 107 | RELOADING |
| 108 | |
| 109 | One thing to keep in mind: the imported "lib/getmessage.vim" script will be |
| 110 | sourced only once. When it is imported a second time sourcing it will be |
| 111 | skipped, since the items in it have already been created. It does not matter |
| 112 | if this import command is in another script, or in the same script that is |
| 113 | sourced again. |
| 114 | |
| 115 | This is efficient when using a plugin, but when still developing a plugin it |
| 116 | means that changing "lib/getmessage.vim" after it has been imported will have |
| 117 | no effect. You need to quit Vim and start it again. (Rationale: the items |
| 118 | defined in the script could be used in a compiled function, sourcing the |
| 119 | script again may break those functions). |
| 120 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 121 | |
| 122 | USING GLOBALS |
| 123 | |
| 124 | Sometimes you will want to use global variables or functions, so that they can |
| 125 | be used anywhere. A good example is a global variable that passes a |
| 126 | preference to a plugin. To avoid other scripts using the same name, use a |
| 127 | prefix that is very unlikely to be used elsewhere. For example, if you have a |
| 128 | "mytags" plugin, you could use: > |
| 129 | |
| 130 | g:mytags_location = '$HOME/project' |
| 131 | g:mytags_style = 'fast' |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 132 | |
| 133 | ============================================================================== |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 134 | *52.2* Autoloading |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 135 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 136 | After splitting your large script into pieces, all the lines will still be |
| 137 | loaded and executed the moment the script is used. Every `import` loads the |
| 138 | imported script to find the items defined there. Although that is good for |
| 139 | finding errors early, it also takes time. Which is wasted if the |
| 140 | functionality is not often used. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 141 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 142 | Instead of having `import` load the script immediately, it can be postponed |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 143 | until needed. Using the example above, only one change needs to be made in |
| 144 | the plugin/theplugin.vim script: > |
| 145 | import autoload "../lib/getmessage.vim" |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 146 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 147 | Nothing in the rest of the script needs to change. However, the types will |
| 148 | not be checked. Not even the existence of the GetMessage() function is |
| 149 | checked until it is used. You will have to decide what is more important for |
| 150 | your script: fast startup or getting errors early. You can also add the |
| 151 | "autoload" argument later, after you have checked everything works. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 152 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 153 | |
| 154 | AUTOLOAD DIRECTORY |
| 155 | |
| 156 | Another form is to use autoload with a script name that is not an absolute or |
| 157 | relative path: > |
Christian Brabandt | b725873 | 2024-05-12 19:04:47 +0200 | [diff] [blame] | 158 | import autoload "monthlib.vim" |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 159 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 160 | This will search for the script "monthlib.vim" in the autoload directories of |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 161 | 'runtimepath'. With Unix one of the directories often is "~/.vim/autoload". |
Bram Moolenaar | 3c053a1 | 2022-10-16 13:11:12 +0100 | [diff] [blame] | 162 | It will also search under 'packpath', under "start". |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 163 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 164 | The main advantage of this is that this script can be easily shared with other |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 165 | scripts. You do need to make sure that the script name is unique, since Vim |
| 166 | will search all the "autoload" directories in 'runtimepath', and if you are |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 167 | using several plugins with a plugin manager, it may add a directory to |
| 168 | 'runtimepath', each of which might have an "autoload" directory. |
| 169 | |
| 170 | Without autoload: > |
| 171 | import "monthlib.vim" |
| 172 | |
| 173 | Vim will search for the script "monthlib.vim" in the import directories of |
| 174 | 'runtimepath'. Note that in this case adding or removing "autoload" changes |
| 175 | where the script is found. With a relative or absolute path the location does |
| 176 | not change. |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 177 | |
| 178 | ============================================================================== |
| 179 | *52.3* Autoloading without import/export |
| 180 | |
| 181 | *write-library-script* |
| 182 | A mechanism from before import/export is still useful and some users may find |
| 183 | it a bit simpler. The idea is that you call a function with a special name. |
| 184 | That function is then in an autoload script. We will call that one script a |
| 185 | library script. |
| 186 | |
Bram Moolenaar | d592deb | 2022-06-17 15:42:40 +0100 | [diff] [blame] | 187 | The autoload mechanism is based on a function name that has "#" characters: > |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 188 | |
| 189 | mylib#myfunction(arg) |
| 190 | |
| 191 | Vim will recognize the function name by the embedded "#" character and when |
| 192 | it is not defined yet search for the script "autoload/mylib.vim" in |
| 193 | 'runtimepath'. That script must define the "mylib#myfunction()" function. |
| 194 | Obviously the name "mylib" is the part before the "#" and is used as the name |
| 195 | of the script, adding ".vim". |
| 196 | |
| 197 | You can put many other functions in the mylib.vim script, you are free to |
| 198 | organize your functions in library scripts. But you must use function names |
| 199 | where the part before the '#' matches the script name. Otherwise Vim would |
| 200 | not know what script to load. This is where it differs from the import/export |
| 201 | mechanism. |
| 202 | |
| 203 | If you get really enthusiastic and write lots of library scripts, you may |
| 204 | want to use subdirectories. Example: > |
| 205 | |
| 206 | netlib#ftp#read('somefile') |
| 207 | |
| 208 | Here the script name is taken from the function name up to the last "#". The |
| 209 | "#" in the middle are replaced by a slash, the last one by ".vim". Thus you |
| 210 | get "netlib/ftp.vim". For Unix the library script used for this could be: |
| 211 | |
| 212 | ~/.vim/autoload/netlib/ftp.vim |
| 213 | |
| 214 | Where the function is defined like this: > |
| 215 | |
| 216 | def netlib#ftp#read(fname: string) |
| 217 | # Read the file fname through ftp |
| 218 | enddef |
| 219 | |
| 220 | Notice that the name the function is defined with is exactly the same as the |
| 221 | name used for calling the function. And the part before the last '#' |
| 222 | exactly matches the subdirectory and script name. |
| 223 | |
| 224 | You can use the same mechanism for variables: > |
| 225 | |
| 226 | var weekdays = dutch#weekdays |
| 227 | |
| 228 | This will load the script "autoload/dutch.vim", which should contain something |
| 229 | like: > |
| 230 | |
| 231 | var dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag', |
| 232 | \ 'donderdag', 'vrijdag', 'zaterdag'] |
| 233 | |
| 234 | Further reading: |autoload|. |
| 235 | |
| 236 | ============================================================================== |
| 237 | *52.4* Other mechanisms to use |
| 238 | |
| 239 | Some may find the use of several files a hassle and prefer to keep everything |
| 240 | together in one script. To avoid this resulting in slow startup there is a |
| 241 | mechanism that only defines a small part and postpones the rest to when it is |
| 242 | actually used. *write-plugin-quickload* |
| 243 | |
| 244 | The basic idea is that the plugin is loaded twice. The first time user |
| 245 | commands and mappings are defined that offer the functionality. The second |
| 246 | time the functions that implement the functionality are defined. |
| 247 | |
| 248 | It may sound surprising that quickload means loading a script twice. What we |
| 249 | mean is that it loads quickly the first time, postponing the bulk of the |
| 250 | script to the second time, which only happens when you actually use it. When |
| 251 | you always use the functionality it actually gets slower! |
| 252 | |
| 253 | This uses a FuncUndefined autocommand. This works differently from the |
| 254 | |autoload| functionality explained above. |
| 255 | |
| 256 | The following example shows how it's done: > |
| 257 | |
| 258 | " Vim global plugin for demonstrating quick loading |
| 259 | " Last Change: 2005 Feb 25 |
| 260 | " Maintainer: Bram Moolenaar <Bram@vim.org> |
| 261 | " License: This file is placed in the public domain. |
| 262 | |
| 263 | if !exists("s:did_load") |
| 264 | command -nargs=* BNRead call BufNetRead(<f-args>) |
| 265 | map <F19> :call BufNetWrite('something')<CR> |
| 266 | |
| 267 | let s:did_load = 1 |
| 268 | exe 'au FuncUndefined BufNet* source ' .. expand('<sfile>') |
| 269 | finish |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 270 | endif |
| 271 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 272 | function BufNetRead(...) |
| 273 | echo 'BufNetRead(' .. string(a:000) .. ')' |
| 274 | " read functionality here |
| 275 | endfunction |
| 276 | |
| 277 | function BufNetWrite(...) |
| 278 | echo 'BufNetWrite(' .. string(a:000) .. ')' |
| 279 | " write functionality here |
| 280 | endfunction |
| 281 | |
| 282 | When the script is first loaded "s:did_load" is not set. The commands between |
| 283 | the "if" and "endif" will be executed. This ends in a |:finish| command, thus |
| 284 | the rest of the script is not executed. |
| 285 | |
| 286 | The second time the script is loaded "s:did_load" exists and the commands |
| 287 | after the "endif" are executed. This defines the (possible long) |
| 288 | BufNetRead() and BufNetWrite() functions. |
| 289 | |
| 290 | If you drop this script in your plugin directory Vim will execute it on |
| 291 | startup. This is the sequence of events that happens: |
| 292 | |
| 293 | 1. The "BNRead" command is defined and the <F19> key is mapped when the script |
| 294 | is sourced at startup. A |FuncUndefined| autocommand is defined. The |
| 295 | ":finish" command causes the script to terminate early. |
| 296 | |
| 297 | 2. The user types the BNRead command or presses the <F19> key. The |
| 298 | BufNetRead() or BufNetWrite() function will be called. |
| 299 | |
| 300 | 3. Vim can't find the function and triggers the |FuncUndefined| autocommand |
| 301 | event. Since the pattern "BufNet*" matches the invoked function, the |
| 302 | command "source fname" will be executed. "fname" will be equal to the name |
| 303 | of the script, no matter where it is located, because it comes from |
| 304 | expanding "<sfile>" (see |expand()|). |
| 305 | |
| 306 | 4. The script is sourced again, the "s:did_load" variable exists and the |
| 307 | functions are defined. |
| 308 | |
| 309 | Notice that the functions that are loaded afterwards match the pattern in the |
| 310 | |FuncUndefined| autocommand. You must make sure that no other plugin defines |
| 311 | functions that match this pattern. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 312 | |
| 313 | ============================================================================== |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 314 | *52.5* Using a Vim9 script from legacy script *source-vim9-script* |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 315 | |
| 316 | In some cases you have a legacy Vim script where you want to use items from a |
| 317 | Vim9 script. For example in your .vimrc you want to initialize a plugin. The |
| 318 | best way to do this is to use `:import`. For example: > |
| 319 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 320 | import 'myNicePlugin.vim' |
| 321 | call myNicePlugin.NiceInit('today') |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 322 | |
Bram Moolenaar | 016188f | 2022-06-06 20:52:59 +0100 | [diff] [blame] | 323 | This finds the exported function "NiceInit" in the Vim9 script file and makes |
| 324 | it available as script-local item "myNicePlugin.NiceInit". `:import` always |
| 325 | uses the script namespace, even when "s:" is not given. If "myNicePlugin.vim" |
| 326 | was already sourced it is not sourced again. |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 327 | |
| 328 | Besides avoiding putting any items in the global namespace (where name clashes |
| 329 | can cause unexpected errors), this also means the script is sourced only once, |
| 330 | no matter how many times items from it are imported. |
| 331 | |
| 332 | In some cases, e.g. for testing, you may just want to source the Vim9 script. |
| 333 | That is OK, but then only global items will be available. The Vim9 script |
| 334 | will have to make sure to use a unique name for these global items. Example: > |
| 335 | source ~/.vim/extra/myNicePlugin.vim |
| 336 | call g:NicePluginTest() |
| 337 | |
| 338 | ============================================================================== |
| 339 | |
| 340 | Next chapter: |usr_90.txt| Installing Vim |
| 341 | |
Bram Moolenaar | cfa8f9a | 2022-06-03 21:59:47 +0100 | [diff] [blame] | 342 | |
Bram Moolenaar | 65e0d77 | 2020-06-14 17:29:55 +0200 | [diff] [blame] | 343 | Copyright: see |manual-copyright| vim:tw=78:ts=8:noet:ft=help:norl: |