summaryrefslogtreecommitdiffstats
path: root/runtime/doc/usr_52.txt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--runtime/doc/usr_52.txt343
1 files changed, 343 insertions, 0 deletions
diff --git a/runtime/doc/usr_52.txt b/runtime/doc/usr_52.txt
new file mode 100644
index 0000000..787dd91
--- /dev/null
+++ b/runtime/doc/usr_52.txt
@@ -0,0 +1,343 @@
+*usr_52.txt* For Vim version 9.0. Last change: 2022 Jun 04
+
+ VIM USER MANUAL - by Bram Moolenaar
+
+ Write larger plugins
+
+When plugins do more than simple things, they tend to grow big. This file
+explains how to make sure they still load fast and how to split them up in
+smaller parts.
+
+|52.1| Export and import
+|52.2| Autoloading
+|52.3| Autoloading without import/export
+|52.4| Other mechanisms to use
+|52.5| Using a Vim9 script from legacy script
+
+ Next chapter: |usr_90.txt| Installing Vim
+ Previous chapter: |usr_51.txt| Create a plugin
+Table of contents: |usr_toc.txt|
+
+==============================================================================
+*52.1* Export and import
+
+Vim9 script was designed to make it easier to write large Vim scripts. It
+looks more like other script languages, especially Typescript. Also,
+functions are compiled into instructions that can be executed quickly. This
+makes Vim9 script a lot faster, up to a 100 times.
+
+The basic idea is that a script file has items that are private, only used
+inside the script file, and items that are exported, which can be used by
+scripts that import them. That makes very clear what is defined where.
+
+Let's start with an example, a script that exports one function and has one
+private function: >
+
+ vim9script
+
+ export def GetMessage(count: string): string
+ var nr = str2nr(count)
+ var result = $'To {nr} we say '
+ result ..= GetReply(nr)
+ return result
+ enddef
+
+ def GetReply(nr: number): string
+ if nr == 42
+ return 'yes'
+ elseif nr = 22
+ return 'maybe'
+ else
+ return 'no'
+ endif
+ enddef
+
+The `vim9script` command is required, `export` only works in a |Vim9| script.
+
+The `export def GetMessage(...` line starts with `export`, meaning that this
+function can be called by other scripts. The line `def GetReply(...` does not
+start with `export`, this is a script-local function, it can only be used
+inside this script file.
+
+Now about the script where this is imported. In this example we use this
+layout, which works well for a plugin below the "pack" directory:
+ .../plugin/theplugin.vim
+ .../lib/getmessage.vim
+
+Assuming the "..." directory has been added to 'runtimepath', Vim will look
+for plugins in the "plugin" directory and source "theplugin.vim". Vim does
+not recognize the "lib" directory, you can put any scripts there.
+
+The above script that exports GetMessage() goes in lib/getmessage.vim. The
+GetMessage() function is used in plugin/theplugin.vim: >
+
+ vim9script
+
+ import "../lib/getmessage.vim"
+ command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>)
+
+The `import` command uses a relative path, it starts with "../", which means
+to go one directory up. For other kinds of paths see the `:import` command.
+
+How we can try out the command that the plugin provides: >
+ ShowMessage 1
+< To 1 we say no ~
+>
+ ShowMessage 22
+< To 22 we say maybe ~
+
+Notice that the function GetMessage() is prefixed with the imported script
+name "getmessage". That way, for every imported function used, you know what
+script it was imported from. If you import several scripts each of them could
+define a GetMessage() function: >
+
+ vim9script
+
+ import "../lib/getmessage.vim"
+ import "../lib/getother.vim"
+ command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>)
+ command -nargs=1 ShowOther echomsg getother.GetMessage(<f-args>)
+
+If the imported script name is long or you use it in many places, you can
+shorten it by adding an "as" argument: >
+ import "../lib/getmessage.vim" as msg
+ command -nargs=1 ShowMessage echomsg msg.GetMessage(<f-args>)
+
+
+RELOADING
+
+One thing to keep in mind: the imported "lib/getmessage.vim" script will be
+sourced only once. When it is imported a second time sourcing it will be
+skipped, since the items in it have already been created. It does not matter
+if this import command is in another script, or in the same script that is
+sourced again.
+
+This is efficient when using a plugin, but when still developing a plugin it
+means that changing "lib/getmessage.vim" after it has been imported will have
+no effect. You need to quit Vim and start it again. (Rationale: the items
+defined in the script could be used in a compiled function, sourcing the
+script again may break those functions).
+
+
+USING GLOBALS
+
+Sometimes you will want to use global variables or functions, so that they can
+be used anywhere. A good example is a global variable that passes a
+preference to a plugin. To avoid other scripts using the same name, use a
+prefix that is very unlikely to be used elsewhere. For example, if you have a
+"mytags" plugin, you could use: >
+
+ g:mytags_location = '$HOME/project'
+ g:mytags_style = 'fast'
+
+==============================================================================
+*52.2* Autoloading
+
+After splitting your large script into pieces, all the lines will still be
+loaded and executed the moment the script is used. Every `import` loads the
+imported script to find the items defined there. Although that is good for
+finding errors early, it also takes time. Which is wasted if the
+functionality is not often used.
+
+Instead of having `import` load the script immediately, it can be postponed
+until needed. Using the example above, only one change needs to be made in
+the plugin/theplugin.vim script: >
+ import autoload "../lib/getmessage.vim"
+
+Nothing in the rest of the script needs to change. However, the types will
+not be checked. Not even the existence of the GetMessage() function is
+checked until it is used. You will have to decide what is more important for
+your script: fast startup or getting errors early. You can also add the
+"autoload" argument later, after you have checked everything works.
+
+
+AUTOLOAD DIRECTORY
+
+Another form is to use autoload with a script name that is not an absolute or
+relative path: >
+ import autload "monthlib.vim"
+
+This will search for the script "monthlib.vim" in the autoload directories of
+'runtimepath'. With Unix one of the directories often is "~/.vim/autoload".
+It will also search under 'packpath', under "start".
+
+The main advantage of this is that this script can be easily shared with other
+scripts. You do need to make sure that the script name is unique, since Vim
+will search all the "autoload" directories in 'runtimepath', and if you are
+using several plugins with a plugin manager, it may add a directory to
+'runtimepath', each of which might have an "autoload" directory.
+
+Without autoload: >
+ import "monthlib.vim"
+
+Vim will search for the script "monthlib.vim" in the import directories of
+'runtimepath'. Note that in this case adding or removing "autoload" changes
+where the script is found. With a relative or absolute path the location does
+not change.
+
+==============================================================================
+*52.3* Autoloading without import/export
+
+ *write-library-script*
+A mechanism from before import/export is still useful and some users may find
+it a bit simpler. The idea is that you call a function with a special name.
+That function is then in an autoload script. We will call that one script a
+library script.
+
+The autoload mechanism is based on a function name that has "#" characters: >
+
+ mylib#myfunction(arg)
+
+Vim will recognize the function name by the embedded "#" character and when
+it is not defined yet search for the script "autoload/mylib.vim" in
+'runtimepath'. That script must define the "mylib#myfunction()" function.
+Obviously the name "mylib" is the part before the "#" and is used as the name
+of the script, adding ".vim".
+
+You can put many other functions in the mylib.vim script, you are free to
+organize your functions in library scripts. But you must use function names
+where the part before the '#' matches the script name. Otherwise Vim would
+not know what script to load. This is where it differs from the import/export
+mechanism.
+
+If you get really enthusiastic and write lots of library scripts, you may
+want to use subdirectories. Example: >
+
+ netlib#ftp#read('somefile')
+
+Here the script name is taken from the function name up to the last "#". The
+"#" in the middle are replaced by a slash, the last one by ".vim". Thus you
+get "netlib/ftp.vim". For Unix the library script used for this could be:
+
+ ~/.vim/autoload/netlib/ftp.vim
+
+Where the function is defined like this: >
+
+ def netlib#ftp#read(fname: string)
+ # Read the file fname through ftp
+ enddef
+
+Notice that the name the function is defined with is exactly the same as the
+name used for calling the function. And the part before the last '#'
+exactly matches the subdirectory and script name.
+
+You can use the same mechanism for variables: >
+
+ var weekdays = dutch#weekdays
+
+This will load the script "autoload/dutch.vim", which should contain something
+like: >
+
+ var dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
+ \ 'donderdag', 'vrijdag', 'zaterdag']
+
+Further reading: |autoload|.
+
+==============================================================================
+*52.4* Other mechanisms to use
+
+Some may find the use of several files a hassle and prefer to keep everything
+together in one script. To avoid this resulting in slow startup there is a
+mechanism that only defines a small part and postpones the rest to when it is
+actually used. *write-plugin-quickload*
+
+The basic idea is that the plugin is loaded twice. The first time user
+commands and mappings are defined that offer the functionality. The second
+time the functions that implement the functionality are defined.
+
+It may sound surprising that quickload means loading a script twice. What we
+mean is that it loads quickly the first time, postponing the bulk of the
+script to the second time, which only happens when you actually use it. When
+you always use the functionality it actually gets slower!
+
+This uses a FuncUndefined autocommand. This works differently from the
+|autoload| functionality explained above.
+
+The following example shows how it's done: >
+
+ " Vim global plugin for demonstrating quick loading
+ " Last Change: 2005 Feb 25
+ " Maintainer: Bram Moolenaar <Bram@vim.org>
+ " License: This file is placed in the public domain.
+
+ if !exists("s:did_load")
+ command -nargs=* BNRead call BufNetRead(<f-args>)
+ map <F19> :call BufNetWrite('something')<CR>
+
+ let s:did_load = 1
+ exe 'au FuncUndefined BufNet* source ' .. expand('<sfile>')
+ finish
+ endif
+
+ function BufNetRead(...)
+ echo 'BufNetRead(' .. string(a:000) .. ')'
+ " read functionality here
+ endfunction
+
+ function BufNetWrite(...)
+ echo 'BufNetWrite(' .. string(a:000) .. ')'
+ " write functionality here
+ endfunction
+
+When the script is first loaded "s:did_load" is not set. The commands between
+the "if" and "endif" will be executed. This ends in a |:finish| command, thus
+the rest of the script is not executed.
+
+The second time the script is loaded "s:did_load" exists and the commands
+after the "endif" are executed. This defines the (possible long)
+BufNetRead() and BufNetWrite() functions.
+
+If you drop this script in your plugin directory Vim will execute it on
+startup. This is the sequence of events that happens:
+
+1. The "BNRead" command is defined and the <F19> key is mapped when the script
+ is sourced at startup. A |FuncUndefined| autocommand is defined. The
+ ":finish" command causes the script to terminate early.
+
+2. The user types the BNRead command or presses the <F19> key. The
+ BufNetRead() or BufNetWrite() function will be called.
+
+3. Vim can't find the function and triggers the |FuncUndefined| autocommand
+ event. Since the pattern "BufNet*" matches the invoked function, the
+ command "source fname" will be executed. "fname" will be equal to the name
+ of the script, no matter where it is located, because it comes from
+ expanding "<sfile>" (see |expand()|).
+
+4. The script is sourced again, the "s:did_load" variable exists and the
+ functions are defined.
+
+Notice that the functions that are loaded afterwards match the pattern in the
+|FuncUndefined| autocommand. You must make sure that no other plugin defines
+functions that match this pattern.
+
+==============================================================================
+*52.5* Using a Vim9 script from legacy script *source-vim9-script*
+
+In some cases you have a legacy Vim script where you want to use items from a
+Vim9 script. For example in your .vimrc you want to initialize a plugin. The
+best way to do this is to use `:import`. For example: >
+
+ import 'myNicePlugin.vim'
+ call myNicePlugin.NiceInit('today')
+
+This finds the exported function "NiceInit" in the Vim9 script file and makes
+it available as script-local item "myNicePlugin.NiceInit". `:import` always
+uses the script namespace, even when "s:" is not given. If "myNicePlugin.vim"
+was already sourced it is not sourced again.
+
+Besides avoiding putting any items in the global namespace (where name clashes
+can cause unexpected errors), this also means the script is sourced only once,
+no matter how many times items from it are imported.
+
+In some cases, e.g. for testing, you may just want to source the Vim9 script.
+That is OK, but then only global items will be available. The Vim9 script
+will have to make sure to use a unique name for these global items. Example: >
+ source ~/.vim/extra/myNicePlugin.vim
+ call g:NicePluginTest()
+
+==============================================================================
+
+Next chapter: |usr_90.txt| Installing Vim
+
+
+Copyright: see |manual-copyright| vim:tw=78:ts=8:noet:ft=help:norl: