diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-02-16 19:25:12 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-02-16 19:25:12 +0100 |
commit | 9a6e33a19b18f20c25b73392cd2faa3ec4890c8c (patch) | |
tree | 2c13d3d751e1e635c4ca4fa8dbc854598a669f89 | |
parent | 5d54a045989599468b7a971fc354b0cba4e2b09d (diff) | |
download | vim-git-9a6e33a19b18f20c25b73392cd2faa3ec4890c8c.tar.gz |
patch 7.4.1336v7.4.1336
Problem: Channel NL mode is not supported yet.
Solution: Add NL mode support to channels.
-rw-r--r-- | src/channel.c | 127 | ||||
-rw-r--r-- | src/os_win32.c | 3 | ||||
-rw-r--r-- | src/proto/channel.pro | 2 | ||||
-rw-r--r-- | src/proto/os_unix.pro | 2 | ||||
-rw-r--r-- | src/proto/os_win32.pro | 2 | ||||
-rw-r--r-- | src/structs.h | 8 | ||||
-rw-r--r-- | src/testdir/test_channel.vim | 34 | ||||
-rw-r--r-- | src/testdir/test_channel_pipe.py | 3 | ||||
-rw-r--r-- | src/version.c | 2 |
9 files changed, 150 insertions, 33 deletions
diff --git a/src/channel.c b/src/channel.c index e0ae267a7..3cfe1bea3 100644 --- a/src/channel.c +++ b/src/channel.c @@ -669,12 +669,12 @@ channel_set_job(channel_T *channel, job_T *job) } /* - * Set the json mode of channel "channel" to "ch_mode". + * Set the mode of channel "channel" to "mode". */ void -channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode) +channel_set_mode(channel_T *channel, ch_mode_T mode) { - channel->ch_mode = ch_mode; + channel->ch_mode = mode; } /* @@ -1057,7 +1057,8 @@ channel_exe_cmd(channel_T *channel, char_u *cmd, typval_T *arg2, typval_T *arg3) /* * Invoke a callback for channel "channel" if needed. - * Return OK when a message was handled, there might be another one. + * TODO: add "which" argument, read stderr. + * Return TRUE when a message was handled, there might be another one. */ static int may_invoke_callback(channel_T *channel) @@ -1074,7 +1075,7 @@ may_invoke_callback(channel_T *channel) /* this channel is handled elsewhere (netbeans) */ return FALSE; - if (ch_mode != MODE_RAW) + if (ch_mode == MODE_JSON || ch_mode == MODE_JS) { /* Get any json message in the queue. */ if (channel_get_json(channel, -1, &listtv) == FAIL) @@ -1113,18 +1114,51 @@ may_invoke_callback(channel_T *channel) } else if (channel_peek(channel) == NULL) { - /* nothing to read on raw channel */ + /* nothing to read on RAW or NL channel */ return FALSE; } else { - /* If there is no callback, don't do anything. */ + /* If there is no callback drop the message. */ if (channel->ch_callback == NULL) + { + while ((msg = channel_get(channel)) != NULL) + vim_free(msg); return FALSE; + } + + if (ch_mode == MODE_NL) + { + char_u *nl; + char_u *buf; + + /* See if we have a message ending in NL in the first buffer. If + * not try to concatenate the first and the second buffer. */ + while (TRUE) + { + buf = channel_peek(channel); + nl = vim_strchr(buf, NL); + if (nl != NULL) + break; + if (channel_collapse(channel) == FAIL) + return FALSE; /* incomplete message */ + } + if (nl[1] == NUL) + /* get the whole buffer */ + msg = channel_get(channel); + else + { + /* Copy the message into allocated memory and remove it from + * the buffer. */ + msg = vim_strnsave(buf, (int)(nl - buf)); + mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1); + } + } + else + /* For a raw channel we don't know where the message ends, just + * get everything we have. */ + msg = channel_get_all(channel); - /* For a raw channel we don't know where the message ends, just get - * everything. */ - msg = channel_get_all(channel); argv[1].v_type = VAR_STRING; argv[1].vval.v_string = msg; } @@ -1276,12 +1310,20 @@ channel_save(channel_T *channel, char_u *buf, int len) return FAIL; /* out of memory */ } - /* TODO: don't strip CR when channel is in raw mode */ - p = node->rq_buffer; - for (i = 0; i < len; ++i) - if (buf[i] != CAR || i + 1 >= len || buf[i + 1] != NL) - *p++ = buf[i]; - *p = NUL; + if (channel->ch_mode == MODE_NL) + { + /* Drop any CR before a NL. */ + p = node->rq_buffer; + for (i = 0; i < len; ++i) + if (buf[i] != CAR || i + 1 >= len || buf[i + 1] != NL) + *p++ = buf[i]; + *p = NUL; + } + else + { + mch_memmove(node->rq_buffer, buf, len); + node->rq_buffer[len] = NUL; + } /* append node to the tail of the queue */ node->rq_next = NULL; @@ -1570,21 +1612,33 @@ channel_read(channel_T *channel, int which, char *func) } /* - * Read from raw channel "channel". Blocks until there is something to read or - * the timeout expires. + * Read from RAW or NL channel "channel". Blocks until there is something to + * read or the timeout expires. + * TODO: add "which" argument and read from stderr. * Returns what was read in allocated memory. * Returns NULL in case of error or timeout. */ char_u * channel_read_block(channel_T *channel) { - ch_log(channel, "Reading raw\n"); - if (channel_peek(channel) == NULL) + char_u *buf; + char_u *msg; + ch_mode_T mode = channel->ch_mode; + sock_T fd = get_read_fd(channel); + char_u *nl; + + ch_logsn(channel, "Blocking %s read, timeout: %d msec\n", + mode == MODE_RAW ? "RAW" : "NL", channel->ch_timeout); + + while (TRUE) { - sock_T fd = get_read_fd(channel); + buf = channel_peek(channel); + if (buf != NULL && (mode == MODE_RAW + || (mode == MODE_NL && vim_strchr(buf, NL) != NULL))) + break; + if (buf != NULL && channel_collapse(channel) == OK) + continue; - /* TODO: read both out and err if they are different */ - ch_log(channel, "No readahead\n"); /* Wait for up to the channel timeout. */ if (fd == CHAN_FD_INVALID || channel_wait(channel, fd, channel->ch_timeout) == FAIL) @@ -1592,9 +1646,30 @@ channel_read_block(channel_T *channel) channel_read(channel, -1, "channel_read_block"); } - /* TODO: only get the first message */ - ch_log(channel, "Returning readahead\n"); - return channel_get_all(channel); + if (mode == MODE_RAW) + { + msg = channel_get_all(channel); + } + else + { + nl = vim_strchr(buf, NL); + if (nl[1] == NUL) + { + /* get the whole buffer */ + msg = channel_get(channel); + *nl = NUL; + } + else + { + /* Copy the message into allocated memory and remove it from the + * buffer. */ + msg = vim_strnsave(buf, (int)(nl - buf)); + mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1); + } + } + if (log_fd != NULL) + ch_logn(channel, "Returning %d bytes\n", (int)STRLEN(msg)); + return msg; } /* diff --git a/src/os_win32.c b/src/os_win32.c index 4fd117f9e..c6f5cc26f 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -5034,7 +5034,7 @@ mch_call_shell( #if defined(FEAT_JOB) || defined(PROTO) void -mch_start_job(char *cmd, job_T *job) +mch_start_job(char *cmd, job_T *job, jobopt_T *options) { STARTUPINFO si; PROCESS_INFORMATION pi; @@ -5121,6 +5121,7 @@ mch_start_job(char *cmd, job_T *job) job->jv_channel = channel; channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]); channel_set_job(channel, job); + channel_set_mode(channel, options->jo_mode); # ifdef FEAT_GUI channel_gui_register(channel); diff --git a/src/proto/channel.pro b/src/proto/channel.pro index c5e61be5c..e1c88627f 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -7,7 +7,7 @@ void channel_gui_register_all(void); channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job); -void channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode); +void channel_set_mode(channel_T *channel, ch_mode_T ch_mode); void channel_set_timeout(channel_T *channel, int timeout); void channel_set_callback(channel_T *channel, char_u *callback); void channel_set_req_callback(channel_T *channel, char_u *callback, int id); diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index c97f7fecf..25d25797e 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -57,7 +57,7 @@ void mch_set_shellsize(void); void mch_new_shellsize(void); int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc); int mch_call_shell(char_u *cmd, int options); -void mch_start_job(char **argv, job_T *job); +void mch_start_job(char **argv, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); int mch_stop_job(job_T *job, char_u *how); void mch_clear_job(job_T *job); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index 2fa6e1024..19c59ec96 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -40,7 +40,7 @@ void mch_set_shellsize(void); void mch_new_shellsize(void); void mch_set_winsize_now(void); int mch_call_shell(char_u *cmd, int options); -void mch_start_job(char *cmd, job_T *job); +void mch_start_job(char *cmd, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); int mch_stop_job(job_T *job, char_u *how); void mch_clear_job(job_T *job); diff --git a/src/structs.h b/src/structs.h index 723876723..eb72f90f2 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1372,6 +1372,14 @@ struct channel_S { int ch_refcount; /* reference count */ }; +/* + * Options for job commands. + */ +typedef struct +{ + ch_mode_T jo_mode; +} jobopt_T; + /* structure used for explicit stack while garbage collecting hash tables */ typedef struct ht_stack_S diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 62b5c89b4..e9c2a98e7 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -284,7 +284,30 @@ func Test_connect_waittime() endif endfunc -func Test_pipe() +func Test_raw_pipe() + if !has('job') + return + endif + let job = job_start(s:python . " test_channel_pipe.py", {'mode': 'raw'}) + call assert_equal("run", job_status(job)) + try + let handle = job_getchannel(job) + call ch_sendraw(handle, "echo something\n", 0) + let msg = ch_readraw(handle) + call assert_equal("something\n", substitute(msg, "\r", "", 'g')) + + call ch_sendraw(handle, "double this\n", 0) + let msg = ch_readraw(handle) + call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) + + let reply = ch_sendraw(handle, "quit\n") + call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g')) + finally + call job_stop(job) + endtry +endfunc + +func Test_nl_pipe() if !has('job') return endif @@ -293,9 +316,14 @@ func Test_pipe() try let handle = job_getchannel(job) call ch_sendraw(handle, "echo something\n", 0) - call assert_equal("something\n", ch_readraw(handle)) + call assert_equal("something", ch_readraw(handle)) + + call ch_sendraw(handle, "double this\n", 0) + call assert_equal("this", ch_readraw(handle)) + call assert_equal("AND this", ch_readraw(handle)) + let reply = ch_sendraw(handle, "quit\n") - call assert_equal("Goodbye!\n", reply) + call assert_equal("Goodbye!", reply) finally call job_stop(job) endtry diff --git a/src/testdir/test_channel_pipe.py b/src/testdir/test_channel_pipe.py index 495fa8012..5994d27ff 100644 --- a/src/testdir/test_channel_pipe.py +++ b/src/testdir/test_channel_pipe.py @@ -21,4 +21,7 @@ if __name__ == "__main__": if typed.startswith("echo"): print(typed[5:-1]) sys.stdout.flush() + if typed.startswith("double"): + print(typed[7:-1] + "\nAND " + typed[7:-1]) + sys.stdout.flush() diff --git a/src/version.c b/src/version.c index 15d40bb65..b07f6b816 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1336, +/**/ 1335, /**/ 1334, |