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