To: vim_dev@googlegroups.com Subject: Patch 7.4.1376 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1376 Problem: ch_setoptions() cannot set all options. Solution: Support more options. Files: src/channel.c, src/eval.c, src/structs.h, runtime/doc/channel.txt, src/testdir/test_channel.vim *** ../vim-7.4.1375/src/channel.c 2016-02-20 21:38:58.770661562 +0100 --- src/channel.c 2016-02-20 23:19:00.719644632 +0100 *************** *** 737,763 **** } /* ! * Set various properties from an "options" argument. */ void ! channel_set_options(channel_T *channel, jobopt_T *options) { ! int part; ! if (options->jo_set & JO_MODE) for (part = PART_SOCK; part <= PART_IN; ++part) ! channel->ch_part[part].ch_mode = options->jo_mode; ! if (options->jo_set & JO_TIMEOUT) for (part = PART_SOCK; part <= PART_IN; ++part) ! channel->ch_part[part].ch_timeout = options->jo_timeout; ! if (options->jo_set & JO_CALLBACK) { ! vim_free(channel->ch_callback); ! if (options->jo_callback != NULL && *options->jo_callback != NUL) ! channel->ch_callback = vim_strsave(options->jo_callback); else ! channel->ch_callback = NULL; } } --- 737,794 ---- } /* ! * Set various properties from an "opt" argument. */ void ! channel_set_options(channel_T *channel, jobopt_T *opt) { ! int part; ! char_u **cbp; ! if (opt->jo_set & JO_MODE) for (part = PART_SOCK; part <= PART_IN; ++part) ! channel->ch_part[part].ch_mode = opt->jo_mode; ! if (opt->jo_set & JO_IN_MODE) ! channel->ch_part[PART_IN].ch_mode = opt->jo_in_mode; ! if (opt->jo_set & JO_OUT_MODE) ! channel->ch_part[PART_OUT].ch_mode = opt->jo_out_mode; ! if (opt->jo_set & JO_ERR_MODE) ! channel->ch_part[PART_ERR].ch_mode = opt->jo_err_mode; ! ! if (opt->jo_set & JO_TIMEOUT) for (part = PART_SOCK; part <= PART_IN; ++part) ! channel->ch_part[part].ch_timeout = opt->jo_timeout; ! if (opt->jo_set & JO_OUT_TIMEOUT) ! channel->ch_part[PART_OUT].ch_timeout = opt->jo_out_timeout; ! if (opt->jo_set & JO_ERR_TIMEOUT) ! channel->ch_part[PART_ERR].ch_timeout = opt->jo_err_timeout; ! if (opt->jo_set & JO_CALLBACK) ! { ! cbp = &channel->ch_callback; ! vim_free(*cbp); ! if (opt->jo_callback != NULL && *opt->jo_callback != NUL) ! *cbp = vim_strsave(opt->jo_callback); ! else ! *cbp = NULL; ! } ! if (opt->jo_set & JO_OUT_CALLBACK) ! { ! cbp = &channel->ch_part[PART_OUT].ch_callback; ! vim_free(*cbp); ! if (opt->jo_out_cb != NULL && *opt->jo_out_cb != NUL) ! *cbp = vim_strsave(opt->jo_out_cb); ! else ! *cbp = NULL; ! } ! if (opt->jo_set & JO_ERR_CALLBACK) { ! cbp = &channel->ch_part[PART_ERR].ch_callback; ! vim_free(*cbp); ! if (opt->jo_err_cb != NULL && *opt->jo_err_cb != NUL) ! *cbp = vim_strsave(opt->jo_err_cb); else ! *cbp = NULL; } } *** ../vim-7.4.1375/src/eval.c 2016-02-20 22:16:54.078901664 +0100 --- src/eval.c 2016-02-20 23:18:24.492027122 +0100 *************** *** 9873,9878 **** --- 9873,9906 ---- return NULL; } + static int + handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) + { + char_u *val = get_tv_string(item); + + opt->jo_set |= jo; + if (STRCMP(val, "nl") == 0) + *modep = MODE_NL; + else if (STRCMP(val, "raw") == 0) + *modep = MODE_RAW; + else if (STRCMP(val, "js") == 0) + *modep = MODE_JS; + else if (STRCMP(val, "json") == 0) + *modep = MODE_JSON; + else + { + EMSG2(_(e_invarg2), val); + return FAIL; + } + return OK; + } + + static void + clear_job_options(jobopt_T *opt) + { + vim_memset(opt, 0, sizeof(jobopt_T)); + } + /* * Get the option entries from the dict in "tv", parse them and put the result * in "opt". *************** *** 9910,9930 **** { if (!(supported & JO_MODE)) break; ! opt->jo_set |= JO_MODE; ! val = get_tv_string(item); ! if (STRCMP(val, "nl") == 0) ! opt->jo_mode = MODE_NL; ! else if (STRCMP(val, "raw") == 0) ! opt->jo_mode = MODE_RAW; ! else if (STRCMP(val, "js") == 0) ! opt->jo_mode = MODE_JS; ! else if (STRCMP(val, "json") == 0) ! opt->jo_mode = MODE_JSON; ! else ! { ! EMSG2(_(e_invarg2), val); return FAIL; - } } else if (STRCMP(hi->hi_key, "callback") == 0) { --- 9938,9969 ---- { if (!(supported & JO_MODE)) break; ! if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL) ! return FAIL; ! } ! else if (STRCMP(hi->hi_key, "in-mode") == 0) ! { ! if (!(supported & JO_IN_MODE)) ! break; ! if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE) ! == FAIL) ! return FAIL; ! } ! else if (STRCMP(hi->hi_key, "out-mode") == 0) ! { ! if (!(supported & JO_OUT_MODE)) ! break; ! if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE) ! == FAIL) ! return FAIL; ! } ! else if (STRCMP(hi->hi_key, "err-mode") == 0) ! { ! if (!(supported & JO_ERR_MODE)) ! break; ! if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE) ! == FAIL) return FAIL; } else if (STRCMP(hi->hi_key, "callback") == 0) { *************** *** 9938,9943 **** --- 9977,10006 ---- return FAIL; } } + else if (STRCMP(hi->hi_key, "out-cb") == 0) + { + if (!(supported & JO_OUT_CALLBACK)) + break; + opt->jo_set |= JO_OUT_CALLBACK; + opt->jo_out_cb = get_callback(item); + if (opt->jo_out_cb == NULL) + { + EMSG2(_(e_invarg2), "out-db"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "err-cb") == 0) + { + if (!(supported & JO_ERR_CALLBACK)) + break; + opt->jo_set |= JO_ERR_CALLBACK; + opt->jo_err_cb = get_callback(item); + if (opt->jo_err_cb == NULL) + { + EMSG2(_(e_invarg2), "err-cb"); + return FAIL; + } + } else if (STRCMP(hi->hi_key, "waittime") == 0) { if (!(supported & JO_WAITTIME)) *************** *** 9952,9957 **** --- 10015,10034 ---- opt->jo_set |= JO_TIMEOUT; opt->jo_timeout = get_tv_number(item); } + else if (STRCMP(hi->hi_key, "out-timeout") == 0) + { + if (!(supported & JO_OUT_TIMEOUT)) + break; + opt->jo_set |= JO_OUT_TIMEOUT; + opt->jo_out_timeout = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "err-timeout") == 0) + { + if (!(supported & JO_ERR_TIMEOUT)) + break; + opt->jo_set |= JO_ERR_TIMEOUT; + opt->jo_err_timeout = get_tv_number(item); + } else if (STRCMP(hi->hi_key, "part") == 0) { if (!(supported & JO_PART)) *************** *** 10107,10118 **** } /* parse options */ opt.jo_mode = MODE_JSON; - opt.jo_callback = NULL; - opt.jo_waittime = 0; opt.jo_timeout = 2000; if (get_job_options(&argvars[1], &opt, ! JO_MODE + JO_CALLBACK + JO_WAITTIME + JO_TIMEOUT) == FAIL) return; if (opt.jo_timeout < 0) { --- 10184,10194 ---- } /* parse options */ + clear_job_options(&opt); opt.jo_mode = MODE_JSON; opt.jo_timeout = 2000; if (get_job_options(&argvars[1], &opt, ! JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL) return; if (opt.jo_timeout < 0) { *************** *** 10147,10153 **** rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; ! opt.jo_set = 0; if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID) == FAIL) return; --- 10223,10229 ---- rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; ! clear_job_options(&opt); if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID) == FAIL) return; *************** *** 10219,10225 **** part_send = channel_part_send(channel); *part_read = channel_part_read(channel); ! opt.jo_callback = NULL; if (get_job_options(&argvars[2], &opt, JO_CALLBACK) == FAIL) return NULL; --- 10295,10301 ---- part_send = channel_part_send(channel); *part_read = channel_part_read(channel); ! clear_job_options(&opt); if (get_job_options(&argvars[2], &opt, JO_CALLBACK) == FAIL) return NULL; *************** *** 10329,10335 **** channel = get_channel_arg(&argvars[0]); if (channel == NULL) return; ! if (get_job_options(&argvars[1], &opt, JO_CALLBACK + JO_TIMEOUT) == FAIL) return; channel_set_options(channel, &opt); } --- 10405,10413 ---- channel = get_channel_arg(&argvars[0]); if (channel == NULL) return; ! clear_job_options(&opt); ! if (get_job_options(&argvars[1], &opt, ! JO_CB_ALL + JO_TIMEOUT_ALL + JO_MODE_ALL) == FAIL) return; channel_set_options(channel, &opt); } *************** *** 14684,14692 **** rettv->vval.v_job->jv_status = JOB_FAILED; /* Default mode is NL. */ opt.jo_mode = MODE_NL; ! opt.jo_callback = NULL; ! if (get_job_options(&argvars[1], &opt, JO_MODE + JO_CALLBACK) == FAIL) return; #ifndef USE_ARGV --- 14762,14771 ---- rettv->vval.v_job->jv_status = JOB_FAILED; /* Default mode is NL. */ + clear_job_options(&opt); opt.jo_mode = MODE_NL; ! if (get_job_options(&argvars[1], &opt, ! JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL) == FAIL) return; #ifndef USE_ARGV *** ../vim-7.4.1375/src/structs.h 2016-02-20 22:16:54.086901581 +0100 --- src/structs.h 2016-02-20 23:16:17.741365460 +0100 *************** *** 1373,1386 **** int ch_refcount; /* reference count */ }; ! #define JO_MODE 1 /* all modes */ ! #define JO_CALLBACK 2 /* channel callback */ ! #define JO_WAITTIME 4 /* only for ch_open() */ ! #define JO_TIMEOUT 8 /* all timeouts */ ! #define JO_PART 16 /* "part" */ ! #define JO_ID 32 /* "id" */ #define JO_ALL 0xffffff /* * Options for job and channel commands. */ --- 1373,1397 ---- int ch_refcount; /* reference count */ }; ! #define JO_MODE 0x0001 /* channel mode */ ! #define JO_IN_MODE 0x0002 /* stdin mode */ ! #define JO_OUT_MODE 0x0004 /* stdout mode */ ! #define JO_ERR_MODE 0x0008 /* stderr mode */ ! #define JO_CALLBACK 0x0010 /* channel callback */ ! #define JO_OUT_CALLBACK 0x0020 /* stdout callback */ ! #define JO_ERR_CALLBACK 0x0040 /* stderr callback */ ! #define JO_WAITTIME 0x0080 /* only for ch_open() */ ! #define JO_TIMEOUT 0x0100 /* all timeouts */ ! #define JO_OUT_TIMEOUT 0x0200 /* stdout timeouts */ ! #define JO_ERR_TIMEOUT 0x0400 /* stderr timeouts */ ! #define JO_PART 0x0800 /* "part" */ ! #define JO_ID 0x1000 /* "id" */ #define JO_ALL 0xffffff + #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) + #define JO_CB_ALL (JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK) + #define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT) + /* * Options for job and channel commands. */ *************** *** 1389,1397 **** --- 1400,1415 ---- int jo_set; /* JO_ bits for values that were set */ ch_mode_T jo_mode; + ch_mode_T jo_in_mode; + ch_mode_T jo_out_mode; + ch_mode_T jo_err_mode; char_u *jo_callback; /* not allocated! */ + char_u *jo_out_cb; /* not allocated! */ + char_u *jo_err_cb; /* not allocated! */ int jo_waittime; int jo_timeout; + int jo_out_timeout; + int jo_err_timeout; int jo_part; int jo_id; } jobopt_T; *** ../vim-7.4.1375/runtime/doc/channel.txt 2016-02-07 19:16:24.242303692 +0100 --- runtime/doc/channel.txt 2016-02-20 23:28:32.273611285 +0100 *************** *** 1,4 **** ! *channel.txt* For Vim version 7.4. Last change: 2016 Feb 07 VIM REFERENCE MANUAL by Bram Moolenaar --- 1,4 ---- ! *channel.txt* For Vim version 7.4. Last change: 2016 Feb 20 VIM REFERENCE MANUAL by Bram Moolenaar *************** *** 9,44 **** DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT Vim uses channels to communicate with other processes. ! A channel uses a socket. *socket-interface* Vim current supports up to 10 simultaneous channels. The Netbeans interface also uses a channel. |netbeans| ! 1. Demo |channel-demo| ! 2. Opening a channel |channel-open| ! 3. Using a JSON or JS channel |channel-use| ! 4. Vim commands |channel-commands| ! 5. Using a raw channel |channel-use| ! 6. Job control |job-control| {Vi does not have any of these features} ! {only available when compiled with the |+channel| feature} ============================================================================== ! 1. Demo *channel-demo* This requires Python. The demo program can be found in $VIMRUNTIME/tools/demoserver.py Run it in one terminal. We will call this T1. Run Vim in another terminal. Connect to the demo server with: > ! let handle = ch_open('localhost:8765') In T1 you should see: === socket opened === ~ You can now send a message to the server: > ! echo ch_sendexpr(handle, 'hello!') The message is received in T1 and a response is sent back to Vim. You can see the raw messages in T1. What Vim sends is: --- 9,80 ---- DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT Vim uses channels to communicate with other processes. ! A channel uses a socket or pipes *socket-interface* ! Jobs can be used to start processes and communicate with them. Vim current supports up to 10 simultaneous channels. The Netbeans interface also uses a channel. |netbeans| ! 1. Overview |job-channel-overview| ! 2. Channel demo |channel-demo| ! 3. Opening a channel |channel-open| ! 4. Using a JSON or JS channel |channel-use| ! 5. Channel commands |channel-commands| ! 6. Using a RAW or NL channel |channel-raw| ! 7. More channel functions |channel-more| ! 8. Starting a job with a channel |job-start| ! 9. Starting a job without a channel |job-start-nochannel| ! 10. Job options |job-options| ! 11. Controlling a job |job-control| {Vi does not have any of these features} ! {only when compiled with the |+channel| feature for channel stuff} ! {only when compiled with the |+job| feature for job stuff} ============================================================================== ! 1. Overview *job-channel-overview* ! ! There are four main types of jobs: ! 1. A deamon, serving several Vim instances. ! Vim connects to it with a socket. ! 2. One job working with one Vim instance, asynchronously. ! Uses a socket or pipes. ! 3. A job performing some work for a short time, asynchronously. ! Uses a socket or pipes. ! 4. Running a filter, synchronously. ! Uses pipes. ! ! For when using sockets See |job-start|, |job-may-start| and |channel-open|. ! For 2 and 3, one or more jobs using pipes, see |job-start|. ! For 4 use the ":{range}!cmd" command, see |filter|. ! ! Over the socket and pipes these protocols are available: ! RAW nothing known, Vim cannot tell where a message ends ! NL every message ends in a NL (newline) character ! JSON JSON encoding |json_encode()| ! JS JavaScript style JSON-like encoding |js_encode()| ! ! Common combination are: ! - Using a job connected through pipes in NL mode. E.g., to run a style ! checker and receive errors and warnings. ! - Using a deamon, connecting over a socket in JSON mode. E.g. to lookup ! crosss-refrences in a database. ! ! ============================================================================== ! 2. Channel demo *channel-demo* This requires Python. The demo program can be found in $VIMRUNTIME/tools/demoserver.py Run it in one terminal. We will call this T1. Run Vim in another terminal. Connect to the demo server with: > ! let channel = ch_open('localhost:8765') In T1 you should see: === socket opened === ~ You can now send a message to the server: > ! echo ch_sendexpr(channel, 'hello!') The message is received in T1 and a response is sent back to Vim. You can see the raw messages in T1. What Vim sends is: *************** *** 54,100 **** ["normal","w"] ~ To handle asynchronous communication a callback needs to be used: > ! func MyHandler(handle, msg) echo "from the handler: " . a:msg endfunc ! call ch_sendexpr(handle, 'hello!', "MyHandler") Instead of giving a callback with every send call, it can also be specified when opening the channel: > ! call ch_close(handle) ! let handle = ch_open('localhost:8765', {'callback': "MyHandler"}) ! call ch_sendexpr(handle, 'hello!', 0) ============================================================================== ! 2. Opening a channel *channel-open* To open a channel: > ! let handle = ch_open({address} [, {argdict}]) {address} has the form "hostname:port". E.g., "localhost:8765". ! {argdict} is a dictionary with optional entries: "mode" can be: *channel-mode* "json" - Use JSON, see below; most convenient way. Default. ! "js" - Use JavaScript encoding, more efficient than JSON. "raw" - Use raw messages *channel-callback* ! "callback" is a function that is called when a message is received that is not ! handled otherwise. It gets two arguments: the channel handle and the received ! message. Example: > ! func Handle(handle, msg) echo 'Received: ' . a:msg endfunc ! let handle = ch_open("localhost:8765", {"callback": "Handle"}) ! ! "waittime" is the time to wait for the connection to be made in milliseconds. ! The default is zero, don't wait, which is useful if the server is supposed to ! be running already. A negative number waits forever. ! ! "timeout" is the time to wait for a request when blocking, using ! ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds). When "mode" is "json" or "js" the "msg" argument is the body of the received message, converted to Vim types. --- 90,169 ---- ["normal","w"] ~ To handle asynchronous communication a callback needs to be used: > ! func MyHandler(channel, msg) echo "from the handler: " . a:msg endfunc ! call ch_sendexpr(channel, 'hello!', "MyHandler") ! Vim will not wait for a response. Now the server can send the response later ! and MyHandler will be invoked. Instead of giving a callback with every send call, it can also be specified when opening the channel: > ! call ch_close(channel) ! let channel = ch_open('localhost:8765', {'callback': "MyHandler"}) ! call ch_sendexpr(channel, 'hello!', 0) ============================================================================== ! 3. Opening a channel *channel-open* To open a channel: > ! let channel = ch_open({address} [, {options}]) ! ! Use |ch_status()| to see if the channel could be opened. {address} has the form "hostname:port". E.g., "localhost:8765". ! {options} is a dictionary with optional entries: "mode" can be: *channel-mode* "json" - Use JSON, see below; most convenient way. Default. ! "js" - Use JS (JavaScript) encoding, more efficient than JSON. ! "nl" - Use messages that end in a NL character "raw" - Use raw messages + "in-mode" mode specifically for stdin, only when using pipes + "out-mode" mode specifically for stdout, only when using pipes + "err-mode" mode specifically for stderr, only when using pipes + Note: when setting "mode" the part specific mode is + overwritten. Therefore set "mode" first and the part specific + mode later. + *channel-callback* ! "callback" A function that is called when a message is received that is ! not handled otherwise. It gets two arguments: the channel ! handle and the received message. Example: > ! func Handle(channel, msg) echo 'Received: ' . a:msg endfunc ! let channel = ch_open("localhost:8765", {"callback": "Handle"}) ! < ! "out-cb" A function like "callback" but used for stdout. Only for when ! the channel uses pipes. When "out-cb" wasn't set the channel ! callback is used. ! ! "err-cb" A function like "callback" but used for stderr. Only for when ! the channel uses pipes. When "err-cb" wasn't set the channel ! callback is used. ! ! TODO: ! "close-cb" A function that is called when the channel gets closed, other ! than by calling ch_close(). It should be defined like this: > ! func MyCloseHandler(channel) ! ! "waittime" The time to wait for the connection to be made in ! milliseconds. The default is zero, don't wait, which is ! useful if the server is supposed to be running already. A ! negative number waits forever. ! ! "timeout" The time to wait for a request when blocking, E.g. when using ! ch_sendexpr(). In milliseconds. The default is 2000 (2 ! seconds). ! ! "out-timeout" Timeout for stdout. Only when using pipes. ! "err-timeout" Timeout for stderr. Only when using pipes. ! Note: when setting "timeout" the part specific mode is ! overwritten. Therefore set "timeout" first and the part ! specific mode later. When "mode" is "json" or "js" the "msg" argument is the body of the received message, converted to Vim types. *************** *** 103,144 **** When "mode" is "json" or "js" the "callback" is optional. When omitted it is only possible to receive a message after sending one. ! The handler can be added or changed later: > ! call ch_setcallback(handle, {callback}) When "callback" is empty (zero or an empty string) the handler is removed. - NOT IMPLEMENTED YET ! The timeout can be changed later: > ! call ch_settimeout(handle, {msec}) ! NOT IMPLEMENTED YET *E906* Once done with the channel, disconnect it like this: > ! call ch_close(handle) Currently up to 10 channels can be in use at the same time. *E897* ! When the channel can't be opened you will get an error message. *E898* *E899* *E900* *E901* *E902* If there is an error reading or writing a channel it will be closed. *E896* *E630* *E631* ============================================================================== ! 3. Using a JSON or JS channel *channel-use* ! If {mode} is "json" then a message can be sent synchronously like this: > ! let response = ch_sendexpr(handle, {expr}) This awaits a response from the other side. ! When {mode} is "js" this works the same, except that the messages use ! JavaScript encoding. See |jsencode()| for the difference. To send a message, without handling a response: > ! call ch_sendexpr(handle, {expr}, 0) To send a message and letting the response handled by a specific function, asynchronously: > ! call ch_sendexpr(handle, {expr}, {callback}) The {expr} is converted to JSON and wrapped in an array. An example of the message that the receiver will get when {expr} is the string "hello": --- 172,227 ---- When "mode" is "json" or "js" the "callback" is optional. When omitted it is only possible to receive a message after sending one. ! To change the channel options after opening it use ch_setoptions(). The ! arguments are similar to what is passed to ch_open(), but "waittime" cannot be ! given, since that only applies to opening the channel. ! ! The handler can be added or changed: > ! call ch_setoptions(channel, {'callback': callback}) When "callback" is empty (zero or an empty string) the handler is removed. ! The timeout can be changed: > ! call ch_setoptions(channel, {'timeout': msec}) ! < *E906* Once done with the channel, disconnect it like this: > ! call ch_close(channel) ! When a socket is used this will close the socket for both directions. When ! pipes are used (stdin/stdout/stderr) they are all closed. This might not be ! what you want! Stopping the job with job_stop() might be better. + TODO: Currently up to 10 channels can be in use at the same time. *E897* ! When the channel can't be opened you will get an error message. There is a ! difference between MS-Windows and Unix: On Unix when the port doesn't exist ! ch_open() fails quickly. On MS-Windows "waittime" applies. *E898* *E899* *E900* *E901* *E902* If there is an error reading or writing a channel it will be closed. *E896* *E630* *E631* ============================================================================== ! 4. Using a JSON or JS channel *channel-use* ! If mode is JSON then a message can be sent synchronously like this: > ! let response = ch_sendexpr(channel, {expr}) This awaits a response from the other side. ! When mode is JS this works the same, except that the messages use ! JavaScript encoding. See |js_encode()| for the difference. To send a message, without handling a response: > ! call ch_sendexpr(channel, {expr}, 0) To send a message and letting the response handled by a specific function, asynchronously: > ! call ch_sendexpr(channel, {expr}, {callback}) ! ! Vim will match the response with the request using the message ID. Once the ! response is received the callback will be invoked. Further responses with the ! same ID will be ignored. If your server sends back multiple responses you ! need to send them with ID zero, they will be passed to the channel callback. The {expr} is converted to JSON and wrapped in an array. An example of the message that the receiver will get when {expr} is the string "hello": *************** *** 166,188 **** Then channel handler will then get {response} converted to Vim types. If the channel does not have a handler the message is dropped. ! On read error or ch_close() the string "DETACH" is sent, if still possible. ! The channel will then be inactive. ! ============================================================================== ! 4. Vim commands *channel-commands* ! PARTLY IMPLEMENTED: only "ex" and "normal" work ! With a "json" channel the process can send commands to Vim that will be handled by Vim internally, it does not require a handler for the channel. Possible commands are: *E903* *E904* *E905* ["redraw" {forced}] ["ex", {Ex command}] ["normal", {Normal mode command}] ! ["eval", {expression}, {number}] ["expr", {expression}] With all of these: Be careful what these commands do! You can easily interfere with what the user is doing. To avoid trouble use |mode()| to check --- 249,274 ---- Then channel handler will then get {response} converted to Vim types. If the channel does not have a handler the message is dropped. ! On read error or ch_close(), when using a socket, the string "DETACH" is sent, ! if still possible. The channel will then be inactive. ! It is also possible to use ch_sendraw() on a JSON or JS channel. The caller ! is then completely responsible for correct encoding and decoding. ! ============================================================================== ! 5. Channel commands *channel-commands* ! With a JSON channel the process can send commands to Vim that will be handled by Vim internally, it does not require a handler for the channel. Possible commands are: *E903* *E904* *E905* ["redraw" {forced}] ["ex", {Ex command}] ["normal", {Normal mode command}] ! ["expr", {expression}, {number}] ["expr", {expression}] + ["call", {func name}, {argument list}, {number}] + ["call", {func name}, {argument list}] With all of these: Be careful what these commands do! You can easily interfere with what the user is doing. To avoid trouble use |mode()| to check *************** *** 223,286 **** ["normal" "zO"] ! Command "eval" ~ ! The "eval" command an be used to get the result of an expression. For example, to get the number of lines in the current buffer: ! ["eval","line('$')"] ~ ! it will send back the result of the expression: [{number}, {result}] Here {number} is the same as what was in the request. Use a negative number ! to avoid confusion with message that Vim sends. {result} is the result of the evaluation and is JSON encoded. If the evaluation fails or the result can't be encoded in JSON it is the string "ERROR". ! Command "expr" ~ ! The "expr" command is similar to "eval", but does not send back any response. Example: ["expr","setline('$', ['one', 'two', 'three'])"] ~ ============================================================================== ! 5. Using a raw channel *channel-raw* - If {mode} is "raw" then a message can be send like this: > - let response = ch_sendraw(handle, {string}) The {string} is sent as-is. The response will be what can be read from the channel right away. Since Vim doesn't know how to recognize the end of the ! message you need to take care of it yourself. To send a message, without expecting a response: > ! call ch_sendraw(handle, {string}, 0) The process can send back a response, the channel handler will be called with it. To send a message and letting the response handled by a specific function, asynchronously: > ! call ch_sendraw(handle, {string}, {callback}) ! This {string} can also be JSON, use |jsonencode()| to create it and ! |jsondecode()| to handle a received JSON message. ============================================================================== ! 6. Job control *job-control* ! NOT IMPLEMENTED YET ! To start another process: > ! call startjob({command}) ! This does not wait for {command} to exit. TODO: ! let handle = startjob({command}, 's') # uses stdin/stdout ! let handle = startjob({command}, '', {address}) # uses socket ! let handle = startjob({command}, 'd', {address}) # start if connect fails vim:tw=78:ts=8:ft=help:norl: --- 309,552 ---- ["normal" "zO"] ! Command "expr" with response ~ ! The "expr" command can be used to get the result of an expression. For example, to get the number of lines in the current buffer: ! ["expr","line('$')", -2] ~ ! It will send back the result of the expression: ! [-2, "last line"] ~ ! The format is: [{number}, {result}] + *E915* Here {number} is the same as what was in the request. Use a negative number ! to avoid confusion with message that Vim sends. Use a different number on ! every request to be able to match the request with the response. {result} is the result of the evaluation and is JSON encoded. If the evaluation fails or the result can't be encoded in JSON it is the string "ERROR". ! Command "expr" without a response ~ ! This command is similar to "expr" above, but does not send back any response. Example: ["expr","setline('$', ['one', 'two', 'three'])"] ~ + There is no third argument in the request. + + + Command "call" ~ + + This is similar to "expr", but instead of passing the whole expression as a + string this passes the name of a function and a list of arguments. This + avoids the conversion of the arguments to a string and escaping and + concatenating them. Example: + ["call", "line", ["$"], -2] ~ + + Leave out the fourth argument if no response is to be sent: + ["call", "setline", ["$", ["one", "two", "three"]]] ~ ============================================================================== ! 6. Using a RAW or NL channel *channel-raw* ! ! If mode is RAW or NL then a message can be send like this: > ! let response = ch_sendraw(channel, {string}) The {string} is sent as-is. The response will be what can be read from the channel right away. Since Vim doesn't know how to recognize the end of the ! message you need to take care of it yourself. The timeout applies for reading ! the first byte, after that it will not wait for anything more. ! ! If mode is "nl" you can send a message in a similar way. You are expected ! to put in the NL after each message. Thus you can also send several messages ! ending in a NL at once. The response will be the text up to and including the ! first NL. This can also be just the NL for an empty response. ! If no NL was read before the channel timeout an empty string is returned. To send a message, without expecting a response: > ! call ch_sendraw(channel, {string}, 0) The process can send back a response, the channel handler will be called with it. To send a message and letting the response handled by a specific function, asynchronously: > ! call ch_sendraw(channel, {string}, {callback}) ! ! This {string} can also be JSON, use |json_encode()| to create it and ! |json_decode()| to handle a received JSON message. ! ! It is not possible to use |ch_sendexpr()| on a raw channel. ! ! ============================================================================== ! 7. More channel functions *channel-more* ! ! To obtain the status of a channel: ch_status(channel). The possible results ! are: ! "fail" Failed to open the channel. ! "open" The channel can be used. ! "closed" The channel was closed. ! ! TODO: ! To objain the job associated with a channel: ch_getjob(channel) ! To read one message from a channel: > ! let output = ch_read(channel) ! This uses the channel timeout. To read without a timeout, just get any ! message that is available: > ! let output = ch_read(channel, {'timeout': 0}) ! When no message was available then the result is v:none for a JSON or JS mode ! channels, an empty string for a RAW or NL channel. ! ! To read all output from a RAW channel that is available: > ! let output = ch_readraw(channel) ! To read the error output: > ! let output = ch_readraw(channel, {"part": "err"}) ============================================================================== ! 8. Starting a job with a channel *job-start* *job* ! To start a job and open a channel for stdin/stdout/stderr: > ! let job = job_start(command, {options}) ! You can get the channel with: > ! let channel = job_getchannel(job) ! The channel will use NL mode. If you want another mode it's best to specify ! this in {options}. When changing the mode later some text may have already ! been received and not parsed correctly. ! ! If the command produces a line of output that you want to deal with, specify ! a handler for stdout: > ! let job = job_start(command, {"out-cb": "MyHandler"}) ! The function will be called with the channel and a message. You would define ! it like this: > ! func MyHandler(channel, msg) ! ! Without the handler you need to read the output with ch_read(). ! ! The handler defined for "out-cb" will also receive stderr. If you want to ! handle that separately, add an "err-cb" handler: > ! let job = job_start(command, {"out-cb": "MyHandler", ! \ "err-cb": "ErrHandler"}) ! ! You can send a message to the command with ch_sendraw(). If the channel is in ! JSON or JS mode you can use ch_sendexpr(). ! ! There are several options you can use, see |job-options|. ! ! TODO: ! To run a job and read its output once it is done: > ! ! let job = job_start({command}, {'exit-cb': 'MyHandler'}) ! func MyHandler(job, status) ! let channel = job_getchannel() ! let output = ch_readall(channel) ! " parse output ! endfunc ! ! ============================================================================== ! 9. Starting a job without a channel *job-start-nochannel* ! ! To start another process without creating a channel: > ! let job = job_start(command, {"in-io": "null", "out-io": "null"}) ! ! This starts {command} in the background, Vim does not wait for it to finish. TODO: + When Vim sees that neither stdin, stdout or stderr are connected, no channel + will be created. Often you will want to include redirection in the command to + avoid it getting stuck. + + There are several options you can use, see |job-options|. + + TODO: *job-may-start* + To start a job only when connecting to an address does not work use + job_maystart('command', {address}, {options}), For Example: > + let job = job_maystart(command, address, {"waittime": 1000}) + let channel = job_gethandle(job) + + This comes down to: > + let channel = ch_open(address, {"waittime": 0}) + if ch_status(channel) == "fail" + let job = job_start(command) + let channel = ch_open(address, {"waittime": 1000}) + call job_sethandle(channel) + endif + Note that the specified waittime applies to when the job has been started. + This gives the job some time to make the port available. + + ============================================================================== + 10. Job options *job-options* + + The {options} argument in job_start() is a dictionary. All entries are + optional. The same options can be used with job_setoptions(job, {options}). + + *job-callback* + "callback": handler Callback for something to read on any part of the + channel. + *job-out-cb* + "out-cb": handler Callback for when there is something to read on + stdout. + *job-err-cb* + "err-cb": handler Callback for when there is something to read on + stderr. + TODO: *job-close-cb* + "close-cb": handler Callback for when the channel is closed. Same as + "close-cb" on ch_open(). + TODO: *job-exit-cb* + "exit-cb": handler Callback for when the job ends. The arguments are the + job and the exit status. + TODO: *job-killonexit* + "killonexit": 1 Stop the job when Vim exits. + "killonexit": 0 Do not stop the job when Vim exits. + The default is 1. + TODO: *job-term* + "term": "open" Start a terminal and connect the job + stdin/stdout/stderr to it. + + TODO: *job-in-io* + "in-io": "null" disconnect stdin + "in-io": "pipe" stdin is connected to the channel (default) + "in-io": "file" stdin reads from a file + "in-file": "/path/file" the file to read from + + TODO: *job-out-io* + "out-io": "null" disconnect stdout + "out-io": "pipe" stdout is connected to the channel (default) + "out-io": "file" stdout writes to a file + "out-file": "/path/file" the file to write to + "out-io": "buffer" stdout appends to a buffer + "out-buffer": "name" buffer to append to + + TODO: *job-err-io* + "err-io": "out" same type as stdout (default) + "err-io": "null" disconnect stderr + "err-io": "pipe" stderr is connected to the channel + "err-io": "file" stderr writes to a file + "err-file": "/path/file" the file to write to + "err-io": "buffer" stderr appends to a buffer + "err-buffer": "name" buffer to append to + + TODO: more options + + + ============================================================================== + 11. Controlling a job *job-control* + + To get the status of a job: > + echo job_status(job) + + To make a job stop running: > + job_stop(job) + + This is the normal way to end a job. On Unix it sends a SIGTERM to the job. + It is possible to use other ways to stop the job, or even send arbitrary + signals. E.g. to force a job to stop, "kill it": > + job_stop(job, "kill") ! For more options see |job_stop()|. vim:tw=78:ts=8:ft=help:norl: *** ../vim-7.4.1375/src/testdir/test_channel.vim 2016-02-20 21:48:21.092789354 +0100 --- src/testdir/test_channel.vim 2016-02-20 23:23:59.700488487 +0100 *************** *** 147,154 **** " check setting options (without testing the effect) call ch_setoptions(handle, {'callback': 's:NotUsed'}) call ch_setoptions(handle, {'timeout': 1111}) call assert_fails("call ch_setoptions(handle, {'waittime': 111})", "E475") - call assert_fails("call ch_setoptions(handle, {'mode': 'json'})", "E475") call ch_setoptions(handle, {'callback': ''}) " Send an eval request that works. --- 147,154 ---- " check setting options (without testing the effect) call ch_setoptions(handle, {'callback': 's:NotUsed'}) call ch_setoptions(handle, {'timeout': 1111}) + call ch_setoptions(handle, {'mode': 'json'}) call assert_fails("call ch_setoptions(handle, {'waittime': 111})", "E475") call ch_setoptions(handle, {'callback': ''}) " Send an eval request that works. *** ../vim-7.4.1375/src/version.c 2016-02-20 22:16:54.086901581 +0100 --- src/version.c 2016-02-20 23:20:52.038469410 +0100 *************** *** 749,750 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1376, /**/ -- MORTICIAN: Bring out your dead! [clang] Bring out your dead! [clang] Bring out your dead! CUSTOMER: Here's one -- nine pence. DEAD PERSON: I'm not dead! The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///