diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoload/provider/clipboard.vim | 32 | ||||
-rw-r--r-- | runtime/autoload/provider/python.vim | 6 | ||||
-rw-r--r-- | runtime/autoload/remote/define.vim (renamed from runtime/autoload/rpc/define.vim) | 36 | ||||
-rw-r--r-- | runtime/autoload/remote/host.vim (renamed from runtime/autoload/rpc/host.vim) | 63 | ||||
-rw-r--r-- | runtime/doc/Makefile | 4 | ||||
-rw-r--r-- | runtime/doc/external_plugin.txt | 104 | ||||
-rw-r--r-- | runtime/doc/job_control.txt | 2 | ||||
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 3 | ||||
-rw-r--r-- | runtime/doc/nvim_intro.txt | 2 | ||||
-rw-r--r-- | runtime/doc/remote_plugin.txt | 136 | ||||
-rw-r--r-- | runtime/plugin/external_plugins.vim | 5 | ||||
-rw-r--r-- | runtime/plugin/rplugin.vim | 5 |
12 files changed, 224 insertions, 174 deletions
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 615a80ca6d..46c05a882c 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -4,6 +4,15 @@ let s:copy = '' let s:paste = '' +function! s:try_cmd(cmd, ...) + let out = a:0 ? systemlist(a:cmd, a:1) : systemlist(a:cmd) + if v:shell_error + echo "clipboard: error: ".(len(out) ? out[0] : '') + return '' + endif + return out +endfunction + if executable('pbcopy') let s:copy = 'pbcopy' let s:paste = 'pbpaste' @@ -13,28 +22,21 @@ elseif executable('xsel') elseif executable('xclip') let s:copy = 'xclip -i -selection clipboard' let s:paste = 'xclip -o -selection clipboard' -endif - -if s:copy == '' - echom 'No shell command for communicating with the clipboard found.' +else + echom 'clipboard: No shell command for communicating with the clipboard found.' finish endif -let s:methods = {} +let s:clipboard = {} -function! s:ClipboardGet(...) - return systemlist(s:paste) +function! s:clipboard.get(...) + return s:try_cmd(s:paste) endfunction -function! s:ClipboardSet(...) - call systemlist(s:copy, a:1) +function! s:clipboard.set(...) + call s:try_cmd(s:copy, a:1) endfunction -let s:methods = { - \ 'get': function('s:ClipboardGet'), - \ 'set': function('s:ClipboardSet') - \ } - function! provider#clipboard#Call(method, args) - return s:methods[a:method](a:args) + return s:clipboard[a:method](a:args) endfunction diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim index 9ca81c35f4..53b984dfe2 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -11,11 +11,11 @@ let s:loaded_python_provider = 1 let s:plugin_path = expand('<sfile>:p:h').'/script_host.py' " The python provider plugin will run in a separate instance of the python " host. -call rpc#host#RegisterClone('legacy-python-provider', 'python') -call rpc#host#RegisterPlugin('legacy-python-provider', s:plugin_path, []) +call remote#host#RegisterClone('legacy-python-provider', 'python') +call remote#host#RegisterPlugin('legacy-python-provider', s:plugin_path, []) " Ensure that we can load the python host before bootstrapping try - let s:host = rpc#host#Require('legacy-python-provider') + let s:host = remote#host#Require('legacy-python-provider') catch echomsg v:exception finish diff --git a/runtime/autoload/rpc/define.vim b/runtime/autoload/remote/define.vim index e7817c5fac..dd2482998d 100644 --- a/runtime/autoload/rpc/define.vim +++ b/runtime/autoload/remote/define.vim @@ -1,4 +1,4 @@ -function! rpc#define#CommandOnHost(host, method, sync, name, opts) +function! remote#define#CommandOnHost(host, method, sync, name, opts) let prefix = '' if has_key(a:opts, 'range') @@ -28,7 +28,7 @@ function! rpc#define#CommandOnHost(host, method, sync, name, opts) endif exe s:GetCommandPrefix(a:name, a:opts) - \ .' call rpc#define#CommandBootstrap("'.a:host.'"' + \ .' call remote#define#CommandBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' \ . ', "'.a:sync.'"' \ . ', "'.a:name.'"' @@ -38,11 +38,11 @@ function! rpc#define#CommandOnHost(host, method, sync, name, opts) endfunction -function! rpc#define#CommandBootstrap(host, method, sync, name, opts, forward) - let channel = rpc#host#Require(a:host) +function! remote#define#CommandBootstrap(host, method, sync, name, opts, forward) + let channel = remote#host#Require(a:host) if channel - call rpc#define#CommandOnChannel(channel, a:method, a:sync, a:name, a:opts) + call remote#define#CommandOnChannel(channel, a:method, a:sync, a:name, a:opts) exe a:forward else exe 'delcommand '.a:name @@ -51,7 +51,7 @@ function! rpc#define#CommandBootstrap(host, method, sync, name, opts, forward) endfunction -function! rpc#define#CommandOnChannel(channel, method, sync, name, opts) +function! remote#define#CommandOnChannel(channel, method, sync, name, opts) let rpcargs = [a:channel, '"'.a:method.'"'] if has_key(a:opts, 'nargs') " -nargs, pass arguments in a list @@ -87,12 +87,12 @@ function! rpc#define#CommandOnChannel(channel, method, sync, name, opts) endfunction -function! rpc#define#AutocmdOnHost(host, method, sync, name, opts) +function! remote#define#AutocmdOnHost(host, method, sync, name, opts) let group = s:GetNextAutocmdGroup() let forward = '"doau '.group.' '.a:name.' ".'.'expand("<amatch>")' let a:opts.group = group let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts) - \ .' call rpc#define#AutocmdBootstrap("'.a:host.'"' + \ .' call remote#define#AutocmdBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' \ . ', "'.a:sync.'"' \ . ', "'.a:name.'"' @@ -103,12 +103,12 @@ function! rpc#define#AutocmdOnHost(host, method, sync, name, opts) endfunction -function! rpc#define#AutocmdBootstrap(host, method, sync, name, opts, forward) - let channel = rpc#host#Require(a:host) +function! remote#define#AutocmdBootstrap(host, method, sync, name, opts, forward) + let channel = remote#host#Require(a:host) exe 'autocmd! '.a:opts.group if channel - call rpc#define#AutocmdOnChannel(channel, a:method, a:sync, a:name, + call remote#define#AutocmdOnChannel(channel, a:method, a:sync, a:name, \ a:opts) exe eval(a:forward) else @@ -118,7 +118,7 @@ function! rpc#define#AutocmdBootstrap(host, method, sync, name, opts, forward) endfunction -function! rpc#define#AutocmdOnChannel(channel, method, sync, name, opts) +function! remote#define#AutocmdOnChannel(channel, method, sync, name, opts) let rpcargs = [a:channel, '"'.a:method.'"'] call s:AddEval(rpcargs, a:opts) @@ -128,10 +128,10 @@ function! rpc#define#AutocmdOnChannel(channel, method, sync, name, opts) endfunction -function! rpc#define#FunctionOnHost(host, method, sync, name, opts) +function! remote#define#FunctionOnHost(host, method, sync, name, opts) let group = s:GetNextAutocmdGroup() exe 'autocmd! '.group.' FuncUndefined '.a:name - \ .' call rpc#define#FunctionBootstrap("'.a:host.'"' + \ .' call remote#define#FunctionBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' \ . ', "'.a:sync.'"' \ . ', "'.a:name.'"' @@ -141,13 +141,13 @@ function! rpc#define#FunctionOnHost(host, method, sync, name, opts) endfunction -function! rpc#define#FunctionBootstrap(host, method, sync, name, opts, group) - let channel = rpc#host#Require(a:host) +function! remote#define#FunctionBootstrap(host, method, sync, name, opts, group) + let channel = remote#host#Require(a:host) exe 'autocmd! '.a:group exe 'augroup! '.a:group if channel - call rpc#define#FunctionOnChannel(channel, a:method, a:sync, a:name, + call remote#define#FunctionOnChannel(channel, a:method, a:sync, a:name, \ a:opts) else echoerr 'Host "'a:host.'" for "'.a:name.'" function is not available' @@ -155,7 +155,7 @@ function! rpc#define#FunctionBootstrap(host, method, sync, name, opts, group) endfunction -function! rpc#define#FunctionOnChannel(channel, method, sync, name, opts) +function! remote#define#FunctionOnChannel(channel, method, sync, name, opts) let rpcargs = [a:channel, '"'.a:method.'"', 'a:000'] call s:AddEval(rpcargs, a:opts) diff --git a/runtime/autoload/rpc/host.vim b/runtime/autoload/remote/host.vim index 177d816df0..b07593f39b 100644 --- a/runtime/autoload/rpc/host.vim +++ b/runtime/autoload/remote/host.vim @@ -2,11 +2,12 @@ let s:hosts = {} let s:plugin_patterns = { \ 'python': '*.py' \ } -let s:external_plugins = fnamemodify($MYVIMRC, ':p:h').'/.external_plugins~' +let s:remote_plugins_manifest = fnamemodify($MYVIMRC, ':p:h') + \.'/.'.fnamemodify($MYVIMRC, ':t').'-rplugin~' " Register a host by associating it with a factory(funcref) -function! rpc#host#Register(name, factory) +function! remote#host#Register(name, factory) let s:hosts[a:name] = {'factory': a:factory, 'channel': 0, 'initialized': 0} if type(a:factory) == type(1) && a:factory " Passed a channel directly @@ -19,7 +20,7 @@ endfunction " as `source`, but it will run as a different process. This can be used by " plugins that should run isolated from other plugins created for the same host " type -function! rpc#host#RegisterClone(name, orig_name) +function! remote#host#RegisterClone(name, orig_name) if !has_key(s:hosts, a:orig_name) throw 'No host named "'.a:orig_name.'" is registered' endif @@ -29,7 +30,7 @@ endfunction " Get a host channel, bootstrapping it if necessary -function! rpc#host#Require(name) +function! remote#host#Require(name) if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -42,7 +43,7 @@ function! rpc#host#Require(name) endfunction -function! rpc#host#IsRunning(name) +function! remote#host#IsRunning(name) if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -54,7 +55,7 @@ endfunction " autocmd(async) and one function(sync): " " let s:plugin_path = expand('<sfile>:p:h').'/nvim_plugin.py' -" call rpc#host#RegisterPlugin('python', s:plugin_path, [ +" call remote#host#RegisterPlugin('python', s:plugin_path, [ " \ {'type': 'command', 'name': 'PyCmd', 'sync': 1, 'opts': {}}, " \ {'type': 'command', 'name': 'PyAsyncCmd', 'sync': 0, 'opts': {'eval': 'cursor()'}}, " \ {'type': 'autocmd', 'name': 'BufEnter', 'sync': 0, 'opts': {'eval': 'expand("<afile>")'}}, @@ -63,7 +64,7 @@ endfunction " " The third item in a declaration is a boolean: non zero means the command, " autocommand or function will be executed synchronously with rpcrequest. -function! rpc#host#RegisterPlugin(host, path, specs) +function! remote#host#RegisterPlugin(host, path, specs) let plugins = s:PluginsForHost(a:host) for plugin in plugins @@ -72,7 +73,7 @@ function! rpc#host#RegisterPlugin(host, path, specs) endif endfor - if rpc#host#IsRunning(a:host) + if remote#host#IsRunning(a:host) " For now we won't allow registration of plugins when the host is already " running. throw 'Host "'.a:host.'" is already running' @@ -86,7 +87,7 @@ function! rpc#host#RegisterPlugin(host, path, specs) let rpc_method = a:path if type == 'command' let rpc_method .= ':command:'.name - call rpc#define#CommandOnHost(a:host, rpc_method, sync, name, opts) + call remote#define#CommandOnHost(a:host, rpc_method, sync, name, opts) elseif type == 'autocmd' " Since multiple handlers can be attached to the same autocmd event by a " single plugin, we need a way to uniquely identify the rpc method to @@ -94,10 +95,10 @@ function! rpc#host#RegisterPlugin(host, path, specs) " name(This still has a limit: one handler per event/pattern combo, but " there's no need to allow plugins define multiple handlers in that case) let rpc_method .= ':autocmd:'.name.':'.get(opts, 'pattern', '*') - call rpc#define#AutocmdOnHost(a:host, rpc_method, sync, name, opts) + call remote#define#AutocmdOnHost(a:host, rpc_method, sync, name, opts) elseif type == 'function' let rpc_method .= ':function:'.name - call rpc#define#FunctionOnHost(a:host, rpc_method, sync, name, opts) + call remote#define#FunctionOnHost(a:host, rpc_method, sync, name, opts) else echoerr 'Invalid declaration type: '.type endif @@ -107,9 +108,9 @@ function! rpc#host#RegisterPlugin(host, path, specs) endfunction -function! rpc#host#LoadExternalPlugins() - if filereadable(s:external_plugins) - exe 'source '.s:external_plugins +function! remote#host#LoadRemotePlugins() + if filereadable(s:remote_plugins_manifest) + exe 'source '.s:remote_plugins_manifest endif endfunction @@ -117,17 +118,22 @@ endfunction function! s:RegistrationCommands(host) " Register a temporary host clone for discovering specs let host_id = a:host.'-registration-clone' - call rpc#host#RegisterClone(host_id, a:host) + call remote#host#RegisterClone(host_id, a:host) let pattern = s:plugin_patterns[a:host] - let paths = globpath(&rtp, 'plugin/external/'.a:host.'/'.pattern, 0, 1) + let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1) + if len(paths) < 1 + echom "Could not find any plugins when attempting to register plugin " + \ ."commands. See :he remote-plugin" + return [] + endif for path in paths - call rpc#host#RegisterPlugin(host_id, path, []) + call remote#host#RegisterPlugin(host_id, path, []) endfor - let channel = rpc#host#Require(host_id) + let channel = remote#host#Require(host_id) let lines = [] for path in paths let specs = rpcrequest(channel, 'specs', path) - call add(lines, "call rpc#host#RegisterPlugin('".a:host + call add(lines, "call remote#host#RegisterPlugin('".a:host \ ."', '".path."', [") for spec in specs call add(lines, " \\ ".string(spec).",") @@ -142,7 +148,7 @@ function! s:RegistrationCommands(host) endfunction -function! s:UpdateExternalPlugins() +function! s:UpdateRemotePlugins() let commands = [] let hosts = keys(s:hosts) for host in hosts @@ -153,11 +159,11 @@ function! s:UpdateExternalPlugins() \ + ['', ''] endif endfor - call writefile(commands, s:external_plugins) + call writefile(commands, s:remote_plugins_manifest) endfunction -command! UpdateExternalPlugins call s:UpdateExternalPlugins() +command! UpdateRemotePlugins call s:UpdateRemotePlugins() let s:plugins_for_host = {} @@ -213,7 +219,9 @@ function! s:RequirePythonHost(name) " In some distros, python3 is the default python let python_host_prog = 'python2' else - throw 'No python interpreter found' + throw 'No python interpreter found.' . + \ " Try setting 'let g:python_host_prog=/path/to/python' in your '.nvimrc'" . + \ " or see ':help nvim-python'." endif " Make sure we pick correct python version on path. @@ -225,7 +233,8 @@ function! s:RequirePythonHost(name) let import_result = system(python_host_prog . \ ' -c "import neovim, sys; sys.stdout.write(\"ok\")"') if import_result != 'ok' - throw 'No neovim module found for ' . python_version + throw 'No neovim module found for ' . python_version . '.' . + \ " Try installing it with 'pip install neovim' or see ':help nvim-python'." endif try @@ -235,8 +244,10 @@ function! s:RequirePythonHost(name) endif catch endtry - throw 'Failed to load python host' + throw 'Failed to load python host.' . + \ " Try upgrading the Neovim python module with 'pip install --upgrade neovim'" . + \ " or see ':help nvim-python'." endfunction -call rpc#host#Register('python', function('s:RequirePythonHost')) +call remote#host#Register('python', function('s:RequirePythonHost')) " }}} diff --git a/runtime/doc/Makefile b/runtime/doc/Makefile index 48674a773f..82fd921038 100644 --- a/runtime/doc/Makefile +++ b/runtime/doc/Makefile @@ -21,6 +21,7 @@ DOCS = \ digraph.txt \ editing.txt \ eval.txt \ + remote_plugin.txt \ farsi.txt \ filetype.txt \ fold.txt \ @@ -53,6 +54,7 @@ DOCS = \ msgpack_rpc.txt \ nvim_clipboard.txt \ nvim_intro.txt \ + nvim_provider.txt \ nvim_python.txt \ options.txt \ os_dos.txt \ @@ -140,6 +142,7 @@ HTMLS = \ digraph.html \ editing.html \ eval.html \ + remote_plugin.html \ farsi.html \ filetype.html \ fold.html \ @@ -171,6 +174,7 @@ HTMLS = \ msgpack_rpc.html \ nvim_clipboard.html \ nvim_intro.html \ + nvim_provider.html \ nvim_python.html \ options.html \ os_dos.html \ diff --git a/runtime/doc/external_plugin.txt b/runtime/doc/external_plugin.txt deleted file mode 100644 index 3f7772c906..0000000000 --- a/runtime/doc/external_plugin.txt +++ /dev/null @@ -1,104 +0,0 @@ -*external_plugin.txt* For Nvim. {Nvim} - - - NVIM REFERENCE MANUAL by Thiago de Arruda - - -Nvim support for external plugins *external-plugin* - -1. Introduction |external-plugin-intro| -2. Plugin Hosts |external-plugin-hosts| -3. Example |external-plugin-example| - -============================================================================== -1. Introduction *external-plugin-intro* - -A big Nvim goal is to allow extensibility in arbitrary programming languages -without requiring direct support from the editor. This is achieved with -external plugins, coprocesses that have a direct communication channel(via -|msgpack-rpc|) with the Nvim process. - -Even though these plugins are running in separate processes, they can call, be -called, and receive events just as if the code was being executed in the main -process. - -============================================================================== -2. Plugin Hosts *external-plugin-hosts* - -While plugins can be implemented as arbitrary programs that communicate -directly with Nvim API and are called via |rpcrequest()| and |rpcnotify()|, -that is not the best approach available. Instead, developers should first -check if a plugin host implementation is available for their favorite -programming language. - -Plugin hosts are programs that provide a high level environment for plugins, -and also take care of most boilerplate involved in defining commands, autocmds -and functions that are implemented over msgpack-rpc connections. They are -loaded the first time one of it's registered plugins are required, keeping -Nvim startup as fast a possible despite the number of installed plugins/hosts. - -============================================================================== -3. Example *external-plugin-example* - -The best way to learn how to create external plugins is with an example, so -let's see how to implement a very useless python plugin that exports a -command, a function and an autocmd(requires configuration detailed in -|nvim-python| to work). - -The plugin is called 'Limit', and all it does is limit the number of "calls" -made to it. Here's the plugin source code: -> - import neovim - - @neovim.plugin - class Limit(object): - def __init__(self, vim): - self.vim = vim - self.calls = 0 - - @neovim.command('Cmd', range='', nargs='*', sync=True) - def command_handler(self, args, range): - self._increment_calls() - self.vim.current.line = ( - 'Command: Called %d times, args: %s, range: %s' % (self.calls, - args, - range)) - - @neovim.autocmd('BufEnter', pattern='*.py', eval='expand("<afile>")', - sync=True) - def autocmd_handler(self, filename): - self._increment_calls() - self.vim.current.line = ( - 'Autocmd: Called %s times, file: %s' % (self.calls, filename)) - - @neovim.function('Func') - def function_handler(self, args): - self._increment_calls() - self.vim.current.line = ( - 'Function: Called %d times, args: %s' % (self.calls, args)) - - def _increment_calls(self): - if self.calls == 5: - raise Exception('Too many calls!') - self.calls += 1 -< - -This code needs to be saved to "external/python/limit.py" in a runtime -directory(~/.nvim/plugin/external/python/limit.py for example). - -As can be seen, the plugin is implemented using pure python idioms(classes, -methods and decorators). It is the host's responsibility to translate -language-specific idioms to vimscript entities. Notice that the exported -command and autocmd are defined with the "sync" flag, which tells Nvim to call -it using |rpcrequest()|. Since the "Func" doesn't set "sync", it will be -called using |rpcnotify()|. - -Just installing the plugin to ~/.nvim/plugin/external/python/limit.py won't -make Nvim load it at startup. That is because external plugins are loaded -only when required, and for that Nvim must be fed with information about -installed external plugins with the `:UpdateExternalPlugins` command(must be -called whenever plugins are updated, this is analogous to the |:helptags| -command but for external plugins). - -============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/job_control.txt b/runtime/doc/job_control.txt index 226244875d..f381d5ad66 100644 --- a/runtime/doc/job_control.txt +++ b/runtime/doc/job_control.txt @@ -31,7 +31,7 @@ facilities for calling with external commands, job control does not depend on installed shells, calling OS functions for process management directly. Internally, Nvim job control is powered by libuv, which has a nice -cross-platform API for managing processes. See https://github.com/joyent/libuv +cross-platform API for managing processes. See https://github.com/libuv/libuv for details ============================================================================== diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index 41f387b452..0fb419e5d5 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -113,7 +113,8 @@ string 'hello world!' on the current nvim instance: A better way is to use the python REPL with the `neovim` package, where API functions can be called interactively: > - >>> import neovim; nvim = neovim.connect('[address]') + >>> from neovim import attach + >>> nvim = attach('socket', path='[address]') >>> nvim.command('echo "hello world!"') < ============================================================================== diff --git a/runtime/doc/nvim_intro.txt b/runtime/doc/nvim_intro.txt index 177c906db6..8a82a09890 100644 --- a/runtime/doc/nvim_intro.txt +++ b/runtime/doc/nvim_intro.txt @@ -17,7 +17,7 @@ differentiate Nvim from Vim: 2. Job control |job-control| 3. Python plugins |nvim-python| 4. Clipboard integration |nvim-clipboard| -5. External plugins |external-plugin| +5. Remote plugins |remote-plugin| 6. Provider infrastructure |nvim-provider| ============================================================================== diff --git a/runtime/doc/remote_plugin.txt b/runtime/doc/remote_plugin.txt new file mode 100644 index 0000000000..ca7e763d1b --- /dev/null +++ b/runtime/doc/remote_plugin.txt @@ -0,0 +1,136 @@ +*remote_plugin.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +Nvim support for remote plugins *remote-plugin* + +1. Introduction |remote-plugin-intro| +2. Plugin hosts |remote-plugin-hosts| +3. Example |remote-plugin-example| +4. Plugin manifest |remote-plugin-manifest| + +============================================================================== +1. Introduction *remote-plugin-intro* + +A big Nvim goal is to allow extensibility in arbitrary programming languages +without requiring direct support from the editor. This is achieved with +remote plugins, coprocesses that have a direct communication channel(via +|msgpack-rpc|) with the Nvim process. + +Even though these plugins are running in separate processes, they can call, be +called, and receive events just as if the code was being executed in the main +process. + +============================================================================== +2. Plugin hosts *remote-plugin-hosts* + +While plugins can be implemented as arbitrary programs that communicate +directly with Nvim API and are called via |rpcrequest()| and |rpcnotify()|, +that is not the best approach available. Instead, developers should first +check if a plugin host implementation is available for their favorite +programming language. + +Plugin hosts are programs that provide a high level environment for plugins, +and also take care of most boilerplate involved in defining commands, autocmds +and functions that are implemented over msgpack-rpc connections. They are +loaded the first time one of its registered plugins are required, keeping +Nvim startup as fast a possible despite the number of installed plugins/hosts. + +============================================================================== +3. Example *remote-plugin-example* + +The best way to learn about remote plugins is with an example, so let's see +how a very useless python plugin looks like. This plugin exports a command, a +function and an autocmd. The plugin is called 'Limit', and all it does is +limit the number of requests made to it. Here's the plugin source code: +> + import neovim + + @neovim.plugin + class Limit(object): + def __init__(self, vim): + self.vim = vim + self.calls = 0 + + @neovim.command('Cmd', range='', nargs='*', sync=True) + def command_handler(self, args, range): + self._increment_calls() + self.vim.current.line = ( + 'Command: Called %d times, args: %s, range: %s' % (self.calls, + args, + range)) + + @neovim.autocmd('BufEnter', pattern='*.py', eval='expand("<afile>")', + sync=True) + def autocmd_handler(self, filename): + self._increment_calls() + self.vim.current.line = ( + 'Autocmd: Called %s times, file: %s' % (self.calls, filename)) + + @neovim.function('Func') + def function_handler(self, args): + self._increment_calls() + self.vim.current.line = ( + 'Function: Called %d times, args: %s' % (self.calls, args)) + + def _increment_calls(self): + if self.calls == 5: + raise Exception('Too many calls!') + self.calls += 1 +< + +As can be seen, the plugin is implemented using pure python idioms(classes, +methods and decorators), the translation between these language-specific +idioms to vimscript occurs while the plugin manifest is being generated(see +below). + +Notice that the exported command and autocmd are defined with the "sync" flag, +which affects how Nvim calls the plugin: with "sync" the |rpcrequest()| +function is used, which will block Nvim until the handler function returns a +value. Without the "sync" flag, the call is made using a fire and forget +approach with |rpcnotify()|(return values or exceptions raised in the handler +function are ignored) + +To test the above plugin, it must be saved in "rplugin/python" in a +'runtimepath' directory(~/.nvim/rplugin/python/limit.py for example). +Then, the remote plugin manifest must be generated with +`:UpdateRemotePlugins`. + +============================================================================== +4. remote plugin manifest *remote-plugin-manifest* + +Just installing remote plugins to "rplugin/{host}" isn't enough to +load them at startup. The `:UpdateRemotePlugins` command must be executed +every time a remote plugin is installed, updated, or deleted. + +`:UpdateRemotePlugins` will generate the remote plugin manifest, a special +vimscript file containing declarations for all vimscript entities +(commands/autocommands/functions) defined by all remote plugins, with each +entity associated with the host and plugin path. The manifest can be seen as a +generated extension to the user's vimrc(it even has the vimrc filename +prepended). + +The manifest declarations are nothing but calls to the remote#host#RegisterPlugin +function, which will take care of bootstrapping the host as soon as the +declared command, autocommand or function is used for the first time. + +The manifest generation step is necessary to keep editor startup fast in +situations where a user has remote plugins with different hosts. For +example, imagine a user that has three plugins, for python, java and .NET +hosts respectively, if we were to load all three plugins at startup, then +three language runtimes would also be spawned which could take seconds! + +With the manifest, each host will only be loaded when required. Continuing +with the example, imagine the java plugin is a semantic completion engine for +java files, if it defines an BufEnter *.java autocommand then the java host +will only be spawned when java source files are loaded. + +If the explicit call to `:UpdateRemotePlugins` seems incovenient, try +to see it like this: Its a way to give IDE-like capabilities to nvim while +still keeping it a fast/lightweight editor for general use. It can also be +seen as an analogous to the |:helptags| facility. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/plugin/external_plugins.vim b/runtime/plugin/external_plugins.vim deleted file mode 100644 index 1c5fa1ab8e..0000000000 --- a/runtime/plugin/external_plugins.vim +++ /dev/null @@ -1,5 +0,0 @@ -if exists('loaded_external_plugins') || &cp - finish -endif -let loaded_external_plugins = 1 -call rpc#host#LoadExternalPlugins() diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim new file mode 100644 index 0000000000..2b2d333738 --- /dev/null +++ b/runtime/plugin/rplugin.vim @@ -0,0 +1,5 @@ +if exists('loaded_remote_plugins') || &cp + finish +endif +let loaded_remote_plugins = 1 +call remote#host#LoadRemotePlugins() |