diff options
-rw-r--r-- | runtime/doc/autocmd.txt | 7 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 54 | ||||
-rw-r--r-- | runtime/doc/job_control.txt | 101 | ||||
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 245 | ||||
-rw-r--r-- | runtime/doc/nvim_clipboard.txt | 47 | ||||
-rw-r--r-- | runtime/doc/nvim_intro.txt | 22 | ||||
-rw-r--r-- | runtime/doc/nvim_python.txt | 40 | ||||
-rw-r--r-- | runtime/doc/options.txt | 9 | ||||
-rw-r--r-- | runtime/plugin/python_setup.vim | 21 | ||||
-rw-r--r-- | runtime/syntax/help.vim | 2 | ||||
-rw-r--r-- | runtime/syntax/vim.vim | 6 | ||||
-rwxr-xr-x | scripts/run-api-tests.exp | 4 | ||||
-rw-r--r-- | src/nvim/eval.c | 409 | ||||
-rw-r--r-- | src/nvim/os/server.c | 7 |
14 files changed, 749 insertions, 225 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 2a8becebff..8f70279310 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -303,6 +303,7 @@ Name triggered by ~ |InsertLeave| when leaving Insert mode |InsertCharPre| when a character was typed in Insert mode, before inserting it +|JobActivity| when something interesting happen with a job |TextChanged| after a change was made to the text in Normal mode |TextChangedI| after a change was made to the text in Insert mode @@ -712,6 +713,10 @@ InsertEnter Just before starting Insert mode. Also for *InsertLeave* InsertLeave When leaving Insert mode. Also when using CTRL-O |i_CTRL-O|. But not for |i_CTRL-C|. + {Nvim} *JobActivity* +JobActivity When something interesting happens with a job + spawned by |jobstart()|. See |job-control| for + details. *MenuPopup* MenuPopup Just before showing the popup menu (under the right mouse button). Useful for adjusting the @@ -1383,4 +1388,4 @@ This will write the file without triggering the autocommands defined by the gzip plugin. - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index cdad6a9d7e..bbd43f9b9a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1865,6 +1865,10 @@ invert( {expr}) Number bitwise invert isdirectory( {directory}) Number TRUE if {directory} is a directory islocked( {expr}) Number TRUE if {expr} is locked items( {dict}) List key-value pairs in {dict} +job_close({job}) Closes a job with id {job} +job_send({job}, {data}) Writes {data} to {job}'s stdin +job_spawn({name}, {prog}[, {argv}]) + Spawns {prog} as a job associated with {name} join( {list} [, {sep}]) String join {list} items into one String keys( {dict}) List keys in {dict} len( {expr}) Number the length of {expr} @@ -1934,6 +1938,12 @@ repeat( {expr}, {count}) String repeat {expr} {count} times resolve( {filename}) String get filename a shortcut points to reverse( {list}) List reverse {list} in-place round( {expr}) Float round off {expr} +rpcnotify({channel}, {event}[, {args}...]) + Sends a |msgpack-rpc| notification to {channel} +rpcrequest({channel}, {method}[, {args}...]) + Sends a |msgpack-rpc| request to {channel} +rpcstart({prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel +rpcstop({channel}) Closes a |msgpack-rpc| {channel} screenattr( {row}, {col}) Number attribute at screen position screenchar( {row}, {col}) Number character at screen position screencol() Number current cursor column @@ -4002,6 +4012,22 @@ items({dict}) *items()* entry and the value of this entry. The |List| is in arbitrary order. +jobsend({job}, {data}) {Nvim} *jobsend()* + Send data to {job} by writing it to the stdin of the process. + See |job-control| for more information. + +jobstart({name}, {prog}[, {argv}]) {Nvim} *jobstart()* + Spawns {prog} as a job and associate it with the {name} string, + which will be used to match the "filename pattern" in + |JobActivity| events. See |job-control| for more information. + +jobstop({job}) {Nvim} *jobstop()* + Stop a job created with |jobstart| by sending a `SIGTERM` + to the corresponding process. If the process doesn't exit + cleanly soon, a `SIGKILL` will be sent. When the job is + finally closed, a |JobActivity| event will trigger with + `v:job_data[0]` set to `exited`. See |job-control| for more + information. join({list} [, {sep}]) *join()* Join the items in {list} together into one String. @@ -5043,6 +5069,32 @@ round({expr}) *round()* < -5.0 {only available when compiled with the |+float| feature} +rpcnotify({channel}, {event}[, {args}...]) {Nvim} *rpcnotify()* + Sends {event} to {channel} via |msgpack-rpc| and returns + immediately. If {channel} is 0, the event is broadcast to all + channels. Example: > + :au VimLeave call rpcnotify(0, "leaving") + +rpcrequest({channel}, {method}[, {args}...]) {Nvim} *rpcrequest()* + Sends a request to {channel} to invoke {method} via + |msgpack-rpc| and blocks until a response is received. + Example: > + :let result = rpcrequest(rpc_chan, "func", 1, 2, 3) + +rpcstart({prog}[, {argv}]) {Nvim} *rpcstart()* + Spawns {prog} as a job(optionally passing the {argv} list), + and open a |msgpack-rpc| channel with the spawned process + stdin/stdout. Returns the channel id, which is used by + |rpcrequest()|, |rpcnotify()| and |rpcstop()| + It expects the rpc channel id as argument. Example: > + :let rpc_chan = rpcstart('prog', ['arg1', 'arg2']) + +rpcstop({channel}) {Nvim} *rpcstop()* + Closes a |msgpack-rpc| channel, possibly created via + |rpcspawn()| (Though it will also close channels created by + connections to |NVIM_LISTEN_ADDRESS|). It accepts the rpc + channel id as only argument. + screenattr(row, col) *screenattr()* Like screenchar(), but return the attribute. This is a rather arbitrary number that can only be used to compare to the @@ -8874,4 +8926,4 @@ This is not allowed when the textlock is active: - etc. - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/job_control.txt b/runtime/doc/job_control.txt new file mode 100644 index 0000000000..c76b4f460b --- /dev/null +++ b/runtime/doc/job_control.txt @@ -0,0 +1,101 @@ +*job_control.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +Nvim's facilities for job control *job-control* + +1. Introduction |job-control-intro| +2. Usage |job-control-usage| + +============================================================================== +1. Introduction *job-control-intro* + +Job control is a simple way to perform multitasking in vimscript. Wikipedia +contains a more generic/detailed description: + +"Job control in computing refers to the control of multiple tasks or Jobs on a +computer system, ensuring that they each have access to adequate resources to +perform correctly, that competition for limited resources does not cause a +deadlock where two or more jobs are unable to complete, resolving such +situations where they do occur, and terminating jobs that, for any reason, are +not performing as expected." + +In a few words: It allows a vimscript programmer to concurrently spawn and +control multiple processes without blocking the current Nvim instance. + +Nvim's job control was designed to be simple and familiar to vimscript +programmers, instead of being very powerful but complex. Unlike Vim's +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 +for details + +============================================================================== +2. Usage *job-control-usage* + +Job control is achieved by calling a combination of the |jobstart()|, +|jobsend()| and |jobstop()| functions, and by listening to the |JobActivity| +event. The best way to understand is with a complete example: +> + set nocp + let job1 = jobstart('shell1', 'bash') + let job2 = jobstart('shell2', 'bash', ['-c', 'for ((i = 0; i < 10; i++)); do echo -n hello $i!; sleep 2; done']) + + function JobHandler() + if v:job_data[1] == 'stdout' + let str = 'shell '. v:job_data[0].' stdout: '.v:job_data[2] + elseif v:job_data[1] == 'stderr' + let str = 'shell '.v:job_data[0].' stderr: '.v:job_data[2] + else + let str = 'shell '.v:job_data[0].' exited' + endif + + call append(line('$'), str) + endfunction + + au JobActivity shell* call JobHandler() +< +To test the above, copy it to the ~/jobcontrol.vim file and start with a clean +nvim instance: + > + nvim -u NONE -S ~/jobcontrol.vim +< +Here's what is happening: + +- Two bash instances are spawned |jobstart()| and their stdin/stdout/stderr + are connected to nvim. +- The first shell is idle, waiting to read commands from it's stdin +- The second shell is passed the -c option to execute a command and exit. In + our case, the command is a for loop that will print numbers and exit after + a while. +- The JobHandler function is called by the JobActivity autocommand(notice how + it the shell* pattern matches the `shell1` and `shell2` names passed to + |jobstart()|), and it takes care of displaying stdout/stderr received from + the shells. +- The v:job_data is an array set by the JobActivity event. It has the + following elements: + 0: The job id + 1: The kind of activity: one of "stdout", "stderr" or "exit" + 2: When "activity" is "stdout" or "stderr", this will contain the data read + from stdout or stderr + +To send data to the job's stdin, one can use the |jobsend()| function, like +this: +> + :call jobsend(job1, 'ls\n')<cr> + :call jobsend(job1, 'invalid-command\n')<cr> + :call jobsend(job1, 'exit\n')<cr> +< +A job may be killed at any time with the |jobstop()| function: +> + :call jobstop(job1) +< +When |jobstop()| is called, it will send `SIGTERM` to the job. If a job +doesn't exit after a while, `SIGKILL` will be sent. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt new file mode 100644 index 0000000000..5e926b7318 --- /dev/null +++ b/runtime/doc/msgpack_rpc.txt @@ -0,0 +1,245 @@ +*msgpack_rpc.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +The Msgpack-RPC Interface to Nvim *msgpack-rpc* + +1. Introduction |msgpack-rpc-intro| +2. API |msgpack-rpc-api| +3. Connecting |msgpack-rpc-connecting| +4. Clients |msgpack-rpc-clients| +5. Types |msgpack-rpc-types| +6. Wrapping methods |msgpack-rpc-wrap-methods| +7. Vimscript functions |msgpack-rpc-vim-functions| + +============================================================================== +1. Introduction *msgpack-rpc-intro* + +The primary means of controlling a running nvim instance is through +MessagePack-RPC, a messaging protocol that uses the MessagePack serialization +format: https://github.com/msgpack/msgpack/blob/7498cf3/spec.md. +From now on, we'll be referring to the protocol as msgpack-rpc. + +At this point, only plugins use msgpack-rpc, but eventually even user +interaction will be achieved through the protocol, since user interfaces will +be separate programs that control a headless nvim instance. + +This is what can be achieved by connecting to the msgpack-rpc interface: + +- Call any nvim API function +- Listen for nvim events +- Receive remote calls from nvim + +Nvim's msgpack-rpc interface can be seen as a more powerful version of Vim's +`clientserver` feature. + +============================================================================== +2. API *msgpack-rpc-api* + +Nvim C API is automatically exposed to the msgpack-rpc interface by the +build system, which parses headers at src/nvim/api from the project root. A +dispatch function is generated, and it will match msgpack-rpc method names +with non-static API functions, converting/validating arguments and return +values back to msgpack. + +Client libraries will normally provide wrappers that hide msgpack-rpc details +from programmers, which can be automatically generated by reading bundled api +metadata from a compiled nvim instance. + +There are two ways to obtain API metadata: + +- By connecting to a running nvim instance and calling `vim_get_api_metadata` + via msgpack-rpc. This is the preferred way for clients written in + dynamically-typed languages, which can define functions at runtime. +- Through the `--api-info` command-line option, which makes nvim to dump a + msgpack blob containing the metadata to stdout and exit. This is preferred + when writing clients for statically-typed languages, which require a + separate compilation step. + +Here's a simple way to get human-readable description of the API(requires +python and the pyyaml/msgpack-python pip packages): +> + nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' > api.yaml + +============================================================================== +3. Connecting *msgpack-rpc-connecting* + + +There are four ways to open msgpack-rpc streams to nvim: + +1. Through nvim's stdin/stdout when started with the `--embed` option. This + how other programs can embed nvim. + +2. Through stdin/stdout of a program spawned by the |rpcstart()| function. + +3. Through the socket automatically created with every instance. To find out + the socket location(which is random by default) from a running nvim + instance, one can inspect the *$NVIM_LISTEN_ADDRESS* environment variable + like this: +> + :echo $NVIM_LISTEN_ADDRESS +< +4. Through a tcp/ip socket. To make nvim listen on a tcp/ip socket, you need + to set the NVIM_LISTEN_ADDRESS environment variable before starting, like + this: +> + NVIM_LISTEN_ADDRESS=127.0.0.1:6666 nvim +< +Connecting to the socket is the easiest way a programmer can test the API, +which can be done through any msgpack-rpc client library or a fully-featured +Nvim client(which we'll see below). Here's a ruby script that will print the +string 'hello world!' on the current nvim instance: +> + #!/usr/bin/env ruby + # Requires msgpack-rpc: gem install msgpack-rpc + # + # To run this script, execute it from a running nvim instance(notice the + # trailing '&' which is required since nvim won't process events while + # running a blocking command): + # + # :!./hello.rb & + # + # Or from another shell by setting NVIM_LISTEN_ADDRESS: + # $ NVIM_LISTEN_ADDRESS=[address] ./hello.rb + + require 'msgpack/rpc' + require 'msgpack/rpc/transport/unix' + + vim = MessagePack::RPC::Client.new(MessagePack::RPC::UNIXTransport.new, ENV['NVIM_LISTEN_ADDRESS']) + result = vim.call(:vim_command, 'echo "hello world!"') +< +A better way is to use the python REPL with the `neovim` package, where API +functions can be called interactively: +> + >>> import neovim; vim = neovim.connect('[address]') + >>> vim.command('echo "hello world!"') +< +============================================================================== +4. Implementing new clients *msgpack-rpc-clients* + +Nvim is still alpha and there's no in-depth documentation explaining how to +properly implement a client library. The python client(neovim pip package) +will be always up-to-date with the latest API changes, so it's source code is +best documentation currently available. There are some guidelines however: + +- Separate the transport layer from the rest of the library(See + |msgpack-rpc-connecting| for details of how a client can connect to nvim). +- Use a msgpack library that implements the spec version 5, Nvim uses the + BIN/EXT types. +- Read api metadata in order to create client-side wrappers for all + msgpack-rpc methods. +- Use a single-threaded event loop library/pattern. +- Use a fiber/coroutine library for the language you are implementing a client + for. These greatly simplify concurrency and allow the library to expose a + blocking API on top of a non-blocking event loop without the complexity + that comes with preemptive multi-tasking. +- Don't assume anything about the order that responses to msgpack-rpc requests + will arrive. +- Clients should expect to receive msgpack-rpc requests, which need to be + handled immediately since Nvim is blocked while waiting for the client + response. +- Clients should expect to receive msgpack-rpc notifications, but these don't + need to be handled immediately because they won't block Nvim(Though you + probably want to handle them immediately anyway). + + +Most of the complexity could be handled by a msgpack-rpc library that supports +server->client requests and notifications, but it's not clear if this is part +of the msgpack-rpc spec. At least the ruby msgpack-rpc library does not seem +to support it: +https://github.com/msgpack-rpc/msgpack-rpc-ruby/blob/master/lib/msgpack/rpc/transport/tcp.rb#L150-L158 + +============================================================================== +5. Types *msgpack-rpc-types* + +Nvim's C API uses custom types for all functions(some are just typedefs +around C99 standard types). The types can be split into two groups: + +- Basic types that map natively to msgpack(and probably have a default + representation in msgpack-supported programming languages) +- Special Nvim types that map to msgpack ext with custom type codes. + +Basic type mapping: + +Nil -> msgpack nil +Boolean -> msgpack boolean +Integer (signed 64-bit integer) -> msgpack integer +Float (IEEE 754 double precision) -> msgpack float +String -> msgpack binary +Array -> msgpack array +Dictionary -> msgpack map + +Special Nvim types that use msgpack ext: + +Buffer -> enum value kObjectTypeBuffer +Window -> enum value kObjectTypeWindow +Tabpage -> enum value kObjectTypeTabpage + +The most reliable way of determining the type codes for the special nvim types +is at runtime by inspecting the `types` key of metadata dictionary returned by +`vim_get_api_metadata` method. Here's an example json representation of the +`types` object: +> + "types": { + "Buffer": { + "id": 0 + }, + "Window": { + "id": 1 + }, + "Tabpage": { + "id": 2 + } + } +< +Even for statically compiled clients, it's a good practice to avoid hardcoding +the type codes, because a client may build for a Nvim version and connect to +another that may have different type codes. + +============================================================================== +6. Wrapping methods *msgpack-rpc-wrap-methods* + +As mentioned before, clients should provide an API that hides msgpack-rpc +details from programmers, and the API metadata object contains information +that makes this task easier: + +- The "functions" key contains a list of metadata objects for individual + functions. +- Each function metadata object has type information about the return value + and parameters. These can be used for generating strongly-typed APIs in + static languages. +- Container types may be decorated with type/size constraints, eg: + ArrayOf(Buffer) or ArrayOf(Integer, 2). This can be useful to generate even + more strongly-typed APIs. +- Methods that operate instances of Nvim's types are prefixed with the type + name in lower case. Eg: `buffer_get_line` represents the `get_line` method + of a Buffer instance. +- Global methods are prefixed with `vim`. Eg: `vim_list_buffers` + +So, for a object-oriented language, a client library would have the classes +that represent Nvim's types, and the methods of each class could be defined +by inspecting the method name prefix. There could also be a singleton Vim +class with methods mapped to functions prefixed with `vim_` + +============================================================================== +7. Vimscript functions *msgpack-rpc-vim-functions* + +Four functions related to msgpack-rpc are available to vimscript: + +- |rpcstart()|: Similarly to |jobstart()|, this will spawn a co-process with + it's standard handles connected to Nvim, the difference is that it's not + possible to process raw data to/from the process stdin/stdout/stderr(Since + the job's stdin/stdout combo are used as a msgpack channgel that is + processed directly by Nvim C code). +- |rpcstop()|: Same as |jobstop()|, but operates on handles returned by + |rpcstart().| +- |rpcrequest()|: Sends a msgpack-rpc request to the process. +- |rpcnotify()|: Sends a msgpack-rpc notification to the process. + +The last two functions may also be used with channels created from +connections to |NVIM_LISTEN_ADDRESS|. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/nvim_clipboard.txt b/runtime/doc/nvim_clipboard.txt new file mode 100644 index 0000000000..ab7a8c3423 --- /dev/null +++ b/runtime/doc/nvim_clipboard.txt @@ -0,0 +1,47 @@ +*nvim_clipboard.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +Clipboard integration for Nvim *nvim-clipboard* + +By default, Nvim has no connection to the system clipboard. Eventually that +will be implemented by UI programs, which connect to Nvim via |msgpack-rpc|. + +Even though externalized UIs are not available yet, there's a workaround that +enables clipboard usage through the python interface(which also uses +|msgpack-rpc| and consequently can implement the clipboard methods required +by Nvim): + +- Make sure you follow the setup instructions in |nvim-python-quickstart|. +- Install the `xerox` python module: + > + $ pip install xerox +< +- Create a ~/.vim/pythonx/nvim_clipboard.py file with the following contents: + > + import xerox + + class NvimClipboard(object): + def __init__(self, vim): + self.provides = ['clipboard'] + + def clipboard_get(self): + return xerox.paste().split('\n') + + def clipboard_set(self, lines): + xerox.copy(u'\n'.join([line.decode('utf-8') for line in lines])) +< +This should enable the '+' and '*' registers. As an optional step, set the +'unnamedclip' option to transparently access clipboard using the unnamed +register. If you use the same |vimrc| for both Vim and Nvim, make sure you +only set the option when `has('neovim')` is true: +> + if has('neovim') + set unnamedclip + endif +< + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/nvim_intro.txt b/runtime/doc/nvim_intro.txt new file mode 100644 index 0000000000..8989f35a79 --- /dev/null +++ b/runtime/doc/nvim_intro.txt @@ -0,0 +1,22 @@ +*nvim_intro.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +Introduction to Nvim *nvim-intro* + +This is an introduction new Nvim users. It is meant for experienced Vim users +that want to get started with Nvim. For a basic introduction to Vim, see +|help.txt|. + +For now, it is just an index with the most relevant topics/features that +differentiate Nvim from Vim: + +1. Msgpack-RPC |msgpack-rpc| +2. Job control |job-control| +3. Python plugins |nvim-python| +4. Clipboard integration |nvim-clipboard| + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/nvim_python.txt b/runtime/doc/nvim_python.txt new file mode 100644 index 0000000000..bb644507f7 --- /dev/null +++ b/runtime/doc/nvim_python.txt @@ -0,0 +1,40 @@ +*nvim_python.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +Python plugins and scripting in Nvim *nvim-python* + +1. Introduction |nvim-python-intro| +2. Quickstart |nvim-python-quickstart| + +============================================================================== +1. Introduction *nvim-python-intro* + +Through an external python interpreter connected via |msgpack-rpc|, Nvim +offers some support for the classic |python-vim| interface. For now only the +old Vim 7.3 API is supported. + +============================================================================== +2. Quickstart *nvim-python-quickstart* + +If you just want to start using python plugins with Nvim quickly, here's a +simple step-by-step: + +- Make sure python 2.6 or 2.7 is available on your `$PATH` +- Install the `neovim` python package: + > + $ pip install neovim +< +- Add the following snippet to your `vimrc`, before any python plugins are + loaded: +> + if has('neovim') + runtime! plugin/python_setup.vim + endif +< +Most python plugins created for Vim 7.3 should work after these steps. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d38fb84f10..293d113c1f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7647,6 +7647,13 @@ A jump table for the options with a short description can be found at |Q_op|. Note that this causes the whole buffer to be stored in memory. Set this option to a lower value if you run out of memory. + {Nvim} *'unnamedclip'* *ucp'* +'unnamedclip' 'ucp' boolean (default off) + global + Use the unnamed register to access the clipboard(when available). + This option has the same effect of setting 'clipboard' to + 'unnamed/unnamedplus' in Vim. + *'updatecount'* *'uc'* 'updatecount' 'uc' number (default: 200) global @@ -8311,4 +8318,4 @@ A jump table for the options with a short description can be found at |Q_op|. screen. When non-zero, characters are sent to the terminal one by one. For MS-DOS pcterm this does not work. For debugging purposes. - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:ft=help:noet:norl: diff --git a/runtime/plugin/python_setup.vim b/runtime/plugin/python_setup.vim index 7b2d800f74..1f0f07b7fa 100644 --- a/runtime/plugin/python_setup.vim +++ b/runtime/plugin/python_setup.vim @@ -6,18 +6,21 @@ let did_python_setup = 1 let s:get_version = - \ ' -c "import sys; sys.stdout.write(str(sys.version_info.major))"' + \ ' -c "import sys; sys.stdout.write(str(sys.version_info.major) + '. + \ '\".\" + str(sys.version_info.minor))"' + +let s:supported = ['2.6', '2.7'] " To load the python host a python 2 executable must be available if exists('python_interpreter') \ && executable(g:python_interpreter) - \ && system(g:python_interpreter.s:get_version) == "2" + \ && index(s:supported, system(g:python_interpreter.s:get_version)) >= 0 let s:python_interpreter = g:python_interpreter -elseif executable('python') && - \ system('python'.s:get_version) == "2" +elseif executable('python') + \ && index(s:supported, system('python'.s:get_version)) >= 0 let s:python_interpreter = 'python' -elseif executable('python2') && - \ system('python2'.s:get_version) == "2" +elseif executable('python2') + \ && index(s:supported, system('python2'.s:get_version)) >= 0 " In some distros, python3 is the default python let s:python_interpreter = 'python2' else @@ -33,12 +36,12 @@ if s:import_result != 'ok' finish endif -let s:pyhost_id = api_spawn(s:python_interpreter, +let s:pyhost_id = rpcstart(s:python_interpreter, \ ['-c', 'import neovim; neovim.start_host()']) " Evaluate an expression in the script host as an additional sanity check, and " to block until all providers have been registered(or else some plugins loaded " by the user's vimrc would not get has('python') == 1 -if send_call(s:pyhost_id, 'python_eval', '"o" + "k"') != 'ok' || !has('python') +if rpcrequest(s:pyhost_id, 'python_eval', '"o"+"k"') != 'ok' || !has('python') " Something went wrong - api_close(s:pyhost_id) + rpcstop(s:pyhost_id) endif diff --git a/runtime/syntax/help.vim b/runtime/syntax/help.vim index 3368b1ddc5..c55d32ef7c 100644 --- a/runtime/syntax/help.vim +++ b/runtime/syntax/help.vim @@ -42,6 +42,8 @@ syn match helpNormal "|||" syn match helpNormal ":|vim:|" " for :help modeline syn match helpVim "\<Vim version [0-9][0-9.a-z]*" syn match helpVim "VIM REFERENCE.*" +syn match helpVim "\<Nvim\." +syn match helpVim "NVIM REFERENCE.*" syn match helpOption "'[a-z]\{2,\}'" syn match helpOption "'t_..'" syn match helpCommand "`[^` \t]\+`"hs=s+1,he=e-1 contains=helpBacktick diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 8d98f7d7d8..55b04bc2d8 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -33,7 +33,7 @@ syn keyword vimOption contained akm anti arshape awa backupskip bex bioskey brow syn keyword vimOption contained al antialias autochdir background balloondelay bexpr bk bs casemap cf cink cmdheight colorcolumn completefunc copyindent cryptmethod cscopeverbose csverb debug dict dir eb enc errorbells expandtab fdl fenc fileencodings fkmap foldenable foldminlines formatprg gdefault gp guifontset helpfile hidden hl ignorecase imcmdline imsf indentexpr is isp keywordprg lazyredraw lispwords ls makeef maxmapdepth mfd mmd modified mousemodel msm numberwidth operatorfunc pastetoggle pexpr pmbfn printexpr pt re restorescreen rnu rulerformat scr sect sft shellredir shiftwidth showmatch siso smc spc spl ss statusline suffixesadd sws tabline tags tbs textmode timeoutlen to tsl ttyfast uc undoreload vdir viewoptions warn wfh wig wildmode winfixwidth wiw wrap writedelay syn keyword vimOption contained aleph ar autoindent backspace ballooneval bg bkc bsdir cb cfu cinkeys cmdwinheight columns completeopt cot cscopepathcomp cspc cuc deco dictionary directory ed encoding errorfile exrc fdls fencs fileformat flp foldexpr foldnestmax fp gfm grepformat guifontwide helpheight highlight hlg im imd imstatusfunc indentkeys isf isprint km lbr list lsp makeprg maxmem mh mmp more mouses mzq nuw opfunc patchexpr pfn popt printfont pumheight readonly revins ro runtimepath scroll sections sh shellslash shm showmode sj smd spell splitbelow ssl stl sw sxe tabpagemax tagstack tenc textwidth title toolbar tsr ttym udf updatecount ve viminfo wb wfw wildchar wildoptions winheight wm wrapmargin ws syn keyword vimOption contained allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display edcompatible endofline errorformat fcl fdm fex fileformats fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatefunc imdisable inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev osfiletype patchmode ph preserveindent printheader pvh redrawtime ri rs sb scrollbind secure shcf shelltemp shortmess showtabline slm sn spellcapcheck splitright ssop stmp swapfile sxq tabstop tal term tf titlelen toolbariconsize ttimeout ttymouse udir updatetime verbose virtualedit wc wh wildcharm wim winminheight wmh wrapscan ww -syn keyword vimOption contained altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fileignorecase fmr foldlevel foldtext fsync gfs gtl guioptions hf hk hlsearch imactivatekey imi include inex isi js kp linebreak lm lz matchpairs maxmemtot mkspellmem mod mousef mouset nf oft pa path pheader previewheight printmbcharset pvw regexpengine rightleft rtp sbo scrolljump sel shell shelltype shortname shq sm so spellfile spr st sts swapsync syn tag tb termbidi tgst titleold top ttimeoutlen ttyscroll ul ur verbosefile visualbell +syn keyword vimOption contained altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fileignorecase fmr foldlevel foldtext fsync gfs gtl guioptions hf hk hlsearch imactivatekey imi include inex isi js kp linebreak lm lz matchpairs maxmemtot mkspellmem mod mousef mouset nf oft pa path pheader previewheight printmbcharset pvw regexpengine rightleft rtp sbo scrolljump sel shell shelltype shortname shq sm so spellfile spr st sts swapsync syn tag tb termbidi tgst titleold top ttimeoutlen ttyscroll ul ur unnamedclip unc verbosefile visualbell " vimOptions: These are the turn-off setting variants {{{2 syn keyword vimOption contained noacd noallowrevins noantialias noarabic noarshape noautoread noaw noballooneval nobinary nobk nobuflisted nocin noconfirm nocopyindent nocscoperelative nocsre nocuc nocursorcolumn nodelcombine nodigraph noed noendofline noerrorbells noex nofen nofk nogd nohid nohkmap nohkp nohlsearch noicon noim noimcmdline noimdisable noinf noinsertmode nojoinspaces nolazyredraw nolinebreak nolist nolpl noma nomagic noml nomodeline nomodified nomousef nomousehide nonumber noopendevice nopi nopreviewwindow nopvw norelativenumber norestorescreen nori norl noro noru nosb noscb noscs nosft noshelltemp noshortname noshowfulltag noshowmode nosm nosmartindent nosmd nosol nosplitbelow nospr nossl nostartofline noswapfile nota notagrelative notbi notbs noterse notextmode notgst notimeout noto notr nottybuiltin notx noundofile novisualbell nowarn noweirdinvert nowfw nowildignorecase nowinfixheight nowiv nowrap nowrite nowritebackup @@ -62,7 +62,7 @@ syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600 " AutoCmd Events {{{2 syn case ignore -syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre Cmd-event CmdwinEnter CmdwinLeave ColorScheme CompleteDone CursorHold CursorHoldI CursorMoved CursorMovedI EncodingChanged FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave MenuPopup QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SessionLoadPost ShellCmdPost ShellFilterPost SourceCmd SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabEnter TabLeave TermChanged TermResponse TextChanged TextChangedI User UserGettingBored VimEnter VimLeave VimLeavePre VimResized WinEnter WinLeave +syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre Cmd-event CmdwinEnter CmdwinLeave ColorScheme CompleteDone CursorHold CursorHoldI CursorMoved CursorMovedI EncodingChanged FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave JobActivity MenuPopup QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SessionLoadPost ShellCmdPost ShellFilterPost SourceCmd SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabEnter TabLeave TermChanged TermResponse TextChanged TextChangedI User UserGettingBored VimEnter VimLeave VimLeavePre VimResized WinEnter WinLeave " Highlight commonly used Groupnames {{{2 syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo @@ -75,7 +75,7 @@ syn case match " Function Names {{{2 syn keyword vimFuncName contained abs and argidx atan browsedir bufloaded bufwinnr byteidxcomp changenr clearmatches complete_add copy count deepcopy diff_filler escape executable expand feedkeys filter float2nr fnameescape foldclosedend foldtextresult garbagecollect getbufvar getcmdline getcwd getfsize getline getpid getreg gettabwinvar getwinvar has hasmapto histget hlID indent inputdialog inputsave invert items len line localtime luaeval mapcheck matcharg matchlist min mzeval or prevnonblank py3eval readfile remote_expr remote_read rename reverse screenchar search searchpairpos serverlist setcmdpos setloclist setpos setreg settabwinvar sha256 shiftwidth sin sort spellbadword split str2float strchars strftime string strpart strtrans submatch synconcealed synIDattr synstack tabpagebuflist tabpagewinnr taglist tanh tolower tr type undotree virtcol wildmenumode wincol winline winrestcmd winsaveview writefile syn keyword vimFuncName contained acos append argv atan2 bufexists bufname byte2line call char2nr col complete_check cos cscope_connection delete diff_hlID eval exists expr8 filereadable finddir floor fnamemodify foldlevel foreground get getchar getcmdpos getfontname getftime getloclist getpos getregtype getwinposx glob has_key histadd histnr hostname index inputlist inputsecret isdirectory join libcall line2byte log map match matchdelete matchstr mkdir nextnonblank pathshorten printf pyeval reltime remote_foreground remote_send repeat round screencol searchdecl searchpos setbufvar setline setmatches setqflist settabvar setwinvar shellescape simplify sinh soundfold spellsuggest sqrt str2nr strdisplaywidth stridx strlen strridx strwidth substitute synID synIDtrans system tabpagenr tagfiles tan tempname toupper trunc undofile values visualmode winbufnr winheight winnr winrestview winwidth xor -syn keyword vimFuncName contained add argc asin browse buflisted bufnr byteidx ceil cindent complete confirm cosh cursor did_filetype empty eventhandler exp extend filewritable findfile fmod foldclosed foldtext function getbufline getcharmod getcmdtype getfperm getftype getmatches getqflist gettabvar getwinposy globpath haslocaldir histdel hlexists iconv input inputrestore insert islocked keys libcallnr lispindent log10 maparg matchadd matchend max mode nr2char pow pumvisible range reltimestr remote_peek remove resolve screenattr screenrow searchpair server2client +syn keyword vimFuncName contained add argc asin browse buflisted bufnr byteidx ceil cindent complete confirm cosh cursor did_filetype empty eventhandler exp extend filewritable findfile fmod foldclosed foldtext function getbufline getcharmod getcmdtype getfperm getftype getmatches getqflist gettabvar getwinposy globpath haslocaldir histdel hlexists iconv input inputrestore insert islocked keys libcallnr lispindent log10 maparg matchadd matchend max mode nr2char pow pumvisible range reltimestr remote_peek remove resolve screenattr screenrow searchpair server2client jobsend jobstart jobstop rpcnotify rpcrequest rpcstart rpcstop "--- syntax here and above generated by mkvimvim --- " Special Vim Highlighting (not automatic) {{{1 diff --git a/scripts/run-api-tests.exp b/scripts/run-api-tests.exp index 6209a77cfc..27c9c963e5 100755 --- a/scripts/run-api-tests.exp +++ b/scripts/run-api-tests.exp @@ -11,8 +11,8 @@ set run_nvim [split [lindex $argv 1] " "] # don't echo to stdout log_user 0 -# set NEOVIM_LISTEN_ADDRESS, so nvim will listen on a known socket -set env(NEOVIM_LISTEN_ADDRESS) "/tmp/nvim-[exec date +%s%N].sock" +# set NVIM_LISTEN_ADDRESS, so nvim will listen on a known socket +set env(NVIM_LISTEN_ADDRESS) "/tmp/nvim-[exec date +%s%N].sock" # start nvim spawn {*}$run_nvim # save the job descriptor diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d8c2e73150..284d5367fc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6306,8 +6306,6 @@ static struct fst { {"acos", 1, 1, f_acos}, /* WJMc */ {"add", 2, 2, f_add}, {"and", 2, 2, f_and}, - {"api_close", 1, 1, f_api_close}, - {"api_spawn", 1, 2, f_api_spawn}, {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, {"argidx", 0, 0, f_argidx}, @@ -6436,9 +6434,9 @@ static struct fst { {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, {"items", 1, 1, f_items}, - {"jobstart", 2, 3, f_job_start}, - {"jobstop", 1, 1, f_job_stop}, - {"jobwrite", 2, 2, f_job_write}, + {"jobsend", 2, 2, f_jobsend}, + {"jobstart", 2, 3, f_jobstart}, + {"jobstop", 1, 1, f_jobstop}, {"join", 1, 2, f_join}, {"keys", 1, 1, f_keys}, {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */ @@ -6485,6 +6483,10 @@ static struct fst { {"resolve", 1, 1, f_resolve}, {"reverse", 1, 1, f_reverse}, {"round", 1, 1, f_round}, + {"rpcnotify", 2, 64, f_rpcnotify}, + {"rpcrequest", 2, 64, f_rpcrequest}, + {"rpcstart", 1, 2, f_rpcstart}, + {"rpcstop", 1, 1, f_rpcstop}, {"screenattr", 2, 2, f_screenattr}, {"screenchar", 2, 2, f_screenchar}, {"screencol", 0, 0, f_screencol}, @@ -6494,8 +6496,6 @@ static struct fst { {"searchpair", 3, 7, f_searchpair}, {"searchpairpos", 3, 7, f_searchpairpos}, {"searchpos", 1, 4, f_searchpos}, - {"send_call", 2, 64, f_send_call}, - {"send_event", 2, 64, f_send_event}, {"setbufvar", 3, 3, f_setbufvar}, {"setcmdpos", 1, 1, f_setcmdpos}, {"setline", 2, 2, f_setline}, @@ -7055,83 +7055,6 @@ static void f_and(typval_T *argvars, typval_T *rettv) & get_tv_number_chk(&argvars[1], NULL); } -// "api_close(prog, argv)" function -static void f_api_close(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER) { - // Wrong argument types - EMSG(_(e_invarg)); - return; - } - - rettv->vval.v_number = channel_close(argvars[0].vval.v_number); -} - - -// "api_spawn(prog, argv)" function -static void f_api_spawn(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_STRING - || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) { - // Wrong argument types - EMSG(_(e_invarg)); - return; - } - - list_T *args = NULL; - int argsl = 0; - if (argvars[1].v_type == VAR_LIST) { - args = argvars[1].vval.v_list; - argsl = args->lv_len; - // Assert that all list items are strings - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - if (arg->li_tv.v_type != VAR_STRING) { - EMSG(_(e_invarg)); - return; - } - } - } - - // Allocate extra memory for the argument vector and the NULL pointer - int argvl = argsl + 2; - char **argv = xmalloc(sizeof(char_u *) * argvl); - - // Copy program name - argv[0] = xstrdup((char *)argvars[0].vval.v_string); - - int i = 1; - // Copy arguments to the vector - if (argsl > 0) { - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); - } - } - - // The last item of argv must be NULL - argv[i] = NULL; - uint64_t channel_id = channel_from_job(argv); - - if (!channel_id) { - EMSG(_(e_api_spawn_failed)); - } - - rettv->vval.v_number = (varnumber_T)channel_id; -} - /* * "append(lnum, string/list)" function */ @@ -10558,8 +10481,40 @@ static void f_items(typval_T *argvars, typval_T *rettv) dict_list(argvars, rettv, 2); } +// "jobsend()" function +static void f_jobsend(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { + // First argument is the job id and second is the string to write to + // the job's stdin + EMSG(_(e_invarg)); + return; + } + + Job *job = job_find(argvars[0].vval.v_number); + + if (!job) { + // Invalid job id + EMSG(_(e_invjob)); + return; + } + + WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), + strlen((char *)argvars[1].vval.v_string), + 1, + free); + rettv->vval.v_number = job_write(job, buf); +} + // "jobstart()" function -static void f_job_start(typval_T *argvars, typval_T *rettv) +static void f_jobstart(typval_T *argvars, typval_T *rettv) { list_T *args = NULL; listitem_T *arg; @@ -10637,7 +10592,7 @@ static void f_job_start(typval_T *argvars, typval_T *rettv) } // "jobstop()" function -static void f_job_stop(typval_T *argvars, typval_T *rettv) +static void f_jobstop(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -10664,38 +10619,6 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; } -// "jobwrite()" function -static void f_job_write(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { - // First argument is the job id and second is the string to write to - // the job's stdin - EMSG(_(e_invarg)); - return; - } - - Job *job = job_find(argvars[0].vval.v_number); - - if (!job) { - // Invalid job id - EMSG(_(e_invjob)); - return; - } - - WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), - strlen((char *)argvars[1].vval.v_string), - 1, - free); - rettv->vval.v_number = job_write(job, buf); -} - /* * "join()" function */ @@ -12420,6 +12343,169 @@ static void f_round(typval_T *argvars, typval_T *rettv) rettv->vval.v_float = 0.0; } +// "rpcnotify()" function +static void f_rpcnotify(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Event type must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + if (!channel_send_event((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + rettv->vval.v_number = 1; +} + +// "rpcrequest()" function +static void f_rpcrequest(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Method name must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + bool errored; + Object result; + if (!channel_send_call((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args, + &result, + &errored)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + if (errored) { + vim_report_error(result.data.string); + goto end; + } + + Error conversion_error = {.set = false}; + if (!object_to_vim(result, rettv, &conversion_error)) { + EMSG(_("Error converting the call result")); + } + +end: + api_free_object(result); +} + +// "rpcstart()" function +static void f_rpcstart(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + list_T *args = NULL; + int argsl = 0; + if (argvars[1].v_type == VAR_LIST) { + args = argvars[1].vval.v_list; + argsl = args->lv_len; + // Assert that all list items are strings + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + if (arg->li_tv.v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + } + } + + // Allocate extra memory for the argument vector and the NULL pointer + int argvl = argsl + 2; + char **argv = xmalloc(sizeof(char_u *) * argvl); + + // Copy program name + argv[0] = xstrdup((char *)argvars[0].vval.v_string); + + int i = 1; + // Copy arguments to the vector + if (argsl > 0) { + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); + } + } + + // The last item of argv must be NULL + argv[i] = NULL; + uint64_t channel_id = channel_from_job(argv); + + if (!channel_id) { + EMSG(_(e_api_spawn_failed)); + } + + rettv->vval.v_number = (varnumber_T)channel_id; +} + +// "rpcstop()" function +static void f_rpcstop(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + rettv->vval.v_number = channel_close(argvars[0].vval.v_number); +} + /* * "screenattr()" function */ @@ -12759,93 +12845,6 @@ do_searchpair ( return retval; } -// "send_call()" function -static void f_send_call(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Method name must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - bool errored; - Object result; - if (!channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args, - &result, - &errored)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - if (errored) { - vim_report_error(result.data.string); - goto end; - } - - Error conversion_error = {.set = false}; - if (!object_to_vim(result, rettv, &conversion_error)) { - EMSG(_("Error converting the call result")); - } - -end: - api_free_object(result); -} - -// "send_event()" function -static void f_send_event(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Event type must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - rettv->vval.v_number = 1; -} - /* * "searchpos()" function */ diff --git a/src/nvim/os/server.c b/src/nvim/os/server.c index 2e8934ecfb..66dd0ecd88 100644 --- a/src/nvim/os/server.c +++ b/src/nvim/os/server.c @@ -17,6 +17,7 @@ #define MAX_CONNECTIONS 32 #define ADDRESS_MAX_SIZE 256 #define NEOVIM_DEFAULT_TCP_PORT 7450 +#define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS" typedef enum { kServerTypeTcp, @@ -51,13 +52,13 @@ void server_init(void) { servers = pmap_new(cstr_t)(); - if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) { + if (!os_getenv(LISTEN_ADDRESS_ENV_VAR)) { char *listen_address = (char *)vim_tempname(); - os_setenv("NEOVIM_LISTEN_ADDRESS", listen_address, 1); + os_setenv(LISTEN_ADDRESS_ENV_VAR, listen_address, 1); free(listen_address); } - server_start((char *)os_getenv("NEOVIM_LISTEN_ADDRESS")); + server_start((char *)os_getenv(LISTEN_ADDRESS_ENV_VAR)); } /// Teardown the server module |