Patch 7.4.1426
Problem: The "out-io" option for jobs is not implemented yet.
Solution: Implement the "buffer" value: append job output to a buffer.
Files: src/eval.c, src/channel.c, src/structs.h, src/netbeans.c,
runtime/doc/channel.txt
*** ../vim-7.4.1425/src/eval.c 2016-02-26 11:17:42.611009168 +0100
--- src/eval.c 2016-02-27 13:58:43.963921744 +0100
***************
*** 9976,9981 ****
--- 9976,10005 ----
return OK;
}
+ static int
+ handle_io(typval_T *item, int part, jobopt_T *opt)
+ {
+ char_u *val = get_tv_string(item);
+
+ opt->jo_set |= JO_OUT_IO << (part - PART_OUT);
+ if (STRCMP(val, "null") == 0)
+ opt->jo_io[part] = JIO_NULL;
+ else if (STRCMP(val, "pipe") == 0)
+ opt->jo_io[part] = JIO_PIPE;
+ else if (STRCMP(val, "file") == 0)
+ opt->jo_io[part] = JIO_FILE;
+ else if (STRCMP(val, "buffer") == 0)
+ opt->jo_io[part] = JIO_BUFFER;
+ else if (STRCMP(val, "out") == 0 && part == PART_ERR)
+ opt->jo_io[part] = JIO_OUT;
+ else
+ {
+ EMSG2(_(e_invarg2), val);
+ return FAIL;
+ }
+ return OK;
+ }
+
static void
clear_job_options(jobopt_T *opt)
{
***************
*** 9983,9988 ****
--- 10007,10021 ----
}
/*
+ * Get the PART_ number from the first character of an option name.
+ */
+ static int
+ part_from_char(int c)
+ {
+ return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR;
+ }
+
+ /*
* Get the option entries from the dict in "tv", parse them and put the result
* in "opt".
* Only accept options in "supported".
***************
*** 9996,10001 ****
--- 10029,10035 ----
dict_T *dict;
int todo;
hashitem_T *hi;
+ int part;
opt->jo_set = 0;
if (tv->v_type == VAR_UNKNOWN)
***************
*** 10046,10051 ****
--- 10080,10106 ----
== FAIL)
return FAIL;
}
+ else if (STRCMP(hi->hi_key, "in-io") == 0
+ || STRCMP(hi->hi_key, "out-io") == 0
+ || STRCMP(hi->hi_key, "err-io") == 0)
+ {
+ if (!(supported & JO_OUT_IO))
+ break;
+ if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "in-name") == 0
+ || STRCMP(hi->hi_key, "out-name") == 0
+ || STRCMP(hi->hi_key, "err-name") == 0)
+ {
+ part = part_from_char(*hi->hi_key);
+
+ if (!(supported & JO_OUT_IO))
+ break;
+ opt->jo_set |= JO_OUT_NAME << (part - PART_OUT);
+ opt->jo_io_name[part] =
+ get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]);
+ }
else if (STRCMP(hi->hi_key, "callback") == 0)
{
if (!(supported & JO_CALLBACK))
***************
*** 10178,10183 ****
--- 10233,10245 ----
return FAIL;
}
+ for (part = PART_OUT; part <= PART_IN; ++part)
+ if (opt->jo_io[part] == JIO_BUFFER && opt->jo_io_name[part] == NULL)
+ {
+ EMSG(_("E915: Missing name for buffer"));
+ return FAIL;
+ }
+
return OK;
}
#endif
***************
*** 10216,10222 ****
--- 10278,10287 ----
channel_T *channel = get_channel_arg(&argvars[0]);
if (channel != NULL)
+ {
channel_close(channel, FALSE);
+ channel_clear(channel);
+ }
}
# ifdef FEAT_JOB
***************
*** 14948,14954 ****
opt.jo_mode = MODE_NL;
if (get_job_options(&argvars[1], &opt,
JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL
! + JO_STOPONEXIT + JO_EXIT_CB) == FAIL)
return;
job_set_options(job, &opt);
--- 15013,15019 ----
opt.jo_mode = MODE_NL;
if (get_job_options(&argvars[1], &opt,
JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL
! + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL)
return;
job_set_options(job, &opt);
*** ../vim-7.4.1425/src/channel.c 2016-02-26 11:52:35.585454423 +0100
--- src/channel.c 2016-02-27 14:34:40.969601258 +0100
***************
*** 52,57 ****
--- 52,61 ----
# define fd_close(sd) close(sd)
#endif
+ /* Whether a redraw is needed for appending a line to a buffer. */
+ static int channel_need_redraw = FALSE;
+
+
#ifdef WIN32
static int
fd_read(sock_T fd, char *buf, size_t len)
***************
*** 342,347 ****
--- 346,352 ----
channel_free(channel_T *channel)
{
channel_close(channel, TRUE);
+ channel_clear(channel);
if (channel->ch_next != NULL)
channel->ch_next->ch_prev = channel->ch_prev;
if (channel->ch_prev == NULL)
***************
*** 777,782 ****
--- 782,816 ----
}
/*
+ * Find a buffer matching "name" or create a new one.
+ */
+ static buf_T *
+ find_buffer(char_u *name)
+ {
+ buf_T *buf = buflist_findname(name);
+ buf_T *save_curbuf = curbuf;
+
+ if (buf == NULL)
+ {
+ buf = buflist_new(name, NULL, (linenr_T)0, BLN_LISTED);
+ buf_copy_options(buf, BCO_ENTER);
+ #ifdef FEAT_QUICKFIX
+ clear_string_option(&buf->b_p_bt);
+ buf->b_p_bt = vim_strsave((char_u *)"nofile");
+ clear_string_option(&buf->b_p_bh);
+ buf->b_p_bh = vim_strsave((char_u *)"hide");
+ #endif
+ curbuf = buf;
+ ml_open(curbuf);
+ ml_replace(1, (char_u *)"Reading from channel output...", TRUE);
+ changed_bytes(1, 0);
+ curbuf = save_curbuf;
+ }
+
+ return buf;
+ }
+
+ /*
* Set various properties from an "opt" argument.
*/
void
***************
*** 839,844 ****
--- 873,888 ----
else
*cbp = NULL;
}
+
+ if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER)
+ {
+ /* writing output to a buffer. Force mode to NL. */
+ channel->ch_part[PART_OUT].ch_mode = MODE_NL;
+ channel->ch_part[PART_OUT].ch_buffer =
+ find_buffer(opt->jo_io_name[PART_OUT]);
+ ch_logs(channel, "writing to buffer %s",
+ (char *)channel->ch_part[PART_OUT].ch_buffer->b_ffname);
+ }
}
/*
***************
*** 1303,1308 ****
--- 1347,1353 ----
int seq_nr = -1;
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
char_u *callback = NULL;
+ buf_T *buffer = NULL;
if (channel->ch_nb_close_cb != NULL)
/* this channel is handled elsewhere (netbeans) */
***************
*** 1312,1317 ****
--- 1357,1363 ----
callback = channel->ch_part[part].ch_callback;
else
callback = channel->ch_callback;
+ buffer = channel->ch_part[part].ch_buffer;
if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
{
***************
*** 1361,1368 ****
}
else
{
! /* If there is no callback drop the message. */
! if (callback == NULL)
{
while ((msg = channel_get(channel, part)) != NULL)
vim_free(msg);
--- 1407,1414 ----
}
else
{
! /* If there is no callback or buffer drop the message. */
! if (callback == NULL && buffer == NULL)
{
while ((msg = channel_get(channel, part)) != NULL)
vim_free(msg);
***************
*** 1386,1393 ****
return FALSE; /* incomplete message */
}
if (nl[1] == NUL)
! /* get the whole buffer */
msg = channel_get(channel, part);
else
{
/* Copy the message into allocated memory and remove it from
--- 1432,1442 ----
return FALSE; /* incomplete message */
}
if (nl[1] == NUL)
! {
! /* get the whole buffer, drop the NL */
msg = channel_get(channel, part);
+ *nl = NUL;
+ }
else
{
/* Copy the message into allocated memory and remove it from
***************
*** 1431,1441 ****
if (!done)
ch_log(channel, "Dropping message without callback");
}
! else if (callback != NULL)
{
! /* invoke the channel callback */
! ch_log(channel, "Invoking channel callback");
! invoke_callback(channel, callback, argv);
}
else
ch_log(channel, "Dropping message");
--- 1480,1533 ----
if (!done)
ch_log(channel, "Dropping message without callback");
}
! else if (callback != NULL || buffer != NULL)
{
! if (buffer != NULL)
! {
! buf_T *save_curbuf = curbuf;
! linenr_T lnum = buffer->b_ml.ml_line_count;
!
! /* Append to the buffer */
! ch_logn(channel, "appending line %d to buffer", (int)lnum + 1);
!
! curbuf = buffer;
! u_sync(TRUE);
! u_save(lnum, lnum + 1);
!
! ml_append(lnum, msg, 0, FALSE);
! appended_lines_mark(lnum, 1L);
! curbuf = save_curbuf;
!
! if (buffer->b_nwindows > 0)
! {
! win_T *wp;
! win_T *save_curwin;
!
! FOR_ALL_WINDOWS(wp)
! {
! if (wp->w_buffer == buffer
! && wp->w_cursor.lnum == lnum
! && wp->w_cursor.col == 0)
! {
! ++wp->w_cursor.lnum;
! save_curwin = curwin;
! curwin = wp;
! curbuf = curwin->w_buffer;
! scroll_cursor_bot(0, FALSE);
! curwin = save_curwin;
! curbuf = curwin->w_buffer;
! }
! }
! redraw_buf_later(buffer, VALID);
! channel_need_redraw = TRUE;
! }
! }
! if (callback != NULL)
! {
! /* invoke the channel callback */
! ch_log(channel, "Invoking channel callback");
! invoke_callback(channel, callback, argv);
! }
}
else
ch_log(channel, "Dropping message");
***************
*** 1493,1498 ****
--- 1585,1591 ----
/*
* Close channel "channel".
* Trigger the close callback if "invoke_close_cb" is TRUE.
+ * Does not clear the buffers.
*/
void
channel_close(channel_T *channel, int invoke_close_cb)
***************
*** 1548,1554 ****
}
channel->ch_nb_close_cb = NULL;
- channel_clear(channel);
}
/*
--- 1641,1646 ----
***************
*** 2160,2165 ****
--- 2252,2275 ----
# endif /* !WIN32 && HAVE_SELECT */
/*
+ * Return TRUE if "channel" has JSON or other typeahead.
+ */
+ static int
+ channel_has_readahead(channel_T *channel, int part)
+ {
+ ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
+
+ if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
+ {
+ jsonq_T *head = &channel->ch_part[part].ch_json_head;
+ jsonq_T *item = head->jq_next;
+
+ return item != NULL;
+ }
+ return channel_peek(channel, part) != NULL;
+ }
+
+ /*
* Execute queued up commands.
* Invoked from the main loop when it's safe to execute received commands.
* Return TRUE when something was done.
***************
*** 2172,2177 ****
--- 2282,2288 ----
int r;
int part = PART_SOCK;
+ ch_log(NULL, "looking for messages on channels");
while (channel != NULL)
{
if (channel->ch_refcount == 0 && !channel_still_useful(channel))
***************
*** 2182,2188 ****
part = PART_SOCK;
continue;
}
! if (channel->ch_part[part].ch_fd != INVALID_FD)
{
/* Increase the refcount, in case the handler causes the channel
* to be unreferenced or closed. */
--- 2293,2300 ----
part = PART_SOCK;
continue;
}
! if (channel->ch_part[part].ch_fd != INVALID_FD
! || channel_has_readahead(channel, part))
{
/* Increase the refcount, in case the handler causes the channel
* to be unreferenced or closed. */
***************
*** 2208,2213 ****
--- 2320,2335 ----
part = PART_SOCK;
}
}
+
+ if (channel_need_redraw && must_redraw)
+ {
+ channel_need_redraw = FALSE;
+ update_screen(0);
+ setcursor();
+ cursor_on();
+ out_flush();
+ }
+
return ret;
}
*** ../vim-7.4.1425/src/structs.h 2016-02-26 11:17:42.611009168 +0100
--- src/structs.h 2016-02-26 21:26:44.144187324 +0100
***************
*** 1347,1352 ****
--- 1347,1353 ----
cbq_T ch_cb_head; /* dummy node for per-request callbacks */
char_u *ch_callback; /* call when a msg is not handled */
+ buf_T *ch_buffer; /* buffer to read from or write to */
} chanpart_T;
struct channel_S {
***************
*** 1395,1400 ****
--- 1396,1407 ----
#define JO_ID 0x2000 /* "id" */
#define JO_STOPONEXIT 0x4000 /* "stoponexit" */
#define JO_EXIT_CB 0x8000 /* "exit-cb" */
+ #define JO_OUT_IO 0x10000 /* "out-io" */
+ #define JO_ERR_IO 0x20000 /* "err-io" (JO_OUT_IO << 1) */
+ #define JO_IN_IO 0x40000 /* "in-io" (JO_OUT_IO << 2) */
+ #define JO_OUT_NAME 0x80000 /* "out-name" */
+ #define JO_ERR_NAME 0x100000 /* "err-name" (JO_OUT_NAME << 1) */
+ #define JO_IN_NAME 0x200000 /* "in-name" (JO_OUT_NAME << 2) */
#define JO_ALL 0xffffff
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
***************
*** 1402,1407 ****
--- 1409,1422 ----
(JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK + JO_CLOSE_CALLBACK)
#define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT)
+ typedef enum {
+ JIO_NULL,
+ JIO_PIPE,
+ JIO_FILE,
+ JIO_BUFFER,
+ JIO_OUT
+ } job_io_T;
+
/*
* Options for job and channel commands.
*/
***************
*** 1413,1418 ****
--- 1428,1438 ----
ch_mode_T jo_in_mode;
ch_mode_T jo_out_mode;
ch_mode_T jo_err_mode;
+
+ job_io_T jo_io[4]; /* PART_OUT, PART_ERR, PART_IN */
+ char_u jo_io_name_buf[4][NUMBUFLEN];
+ char_u *jo_io_name[4]; /* not allocated! */
+
char_u *jo_callback; /* not allocated! */
char_u *jo_out_cb; /* not allocated! */
char_u *jo_err_cb; /* not allocated! */
*** ../vim-7.4.1425/src/netbeans.c 2016-02-24 20:42:58.077568933 +0100
--- src/netbeans.c 2016-02-27 14:00:13.502997995 +0100
***************
*** 99,106 ****
--- 99,109 ----
{
netbeans_send_disconnect();
if (nb_channel != NULL)
+ {
/* Close the socket and remove the input handlers. */
channel_close(nb_channel, TRUE);
+ channel_clear(nb_channel);
+ }
nb_channel = NULL;
}
*** ../vim-7.4.1425/runtime/doc/channel.txt 2016-02-21 20:08:19.546419633
+0100
--- runtime/doc/channel.txt 2016-02-27 13:59:46.471276881 +0100
***************
*** 1,4 ****
! *channel.txt* For Vim version 7.4. Last change: 2016 Feb 20
VIM REFERENCE MANUAL by Bram Moolenaar
--- 1,4 ----
! *channel.txt* For Vim version 7.4. Last change: 2016 Feb 27
VIM REFERENCE MANUAL by Bram Moolenaar
***************
*** 130,135 ****
--- 130,137 ----
overwritten. Therefore set "mode" first and the part specific
mode later.
+ Note: when writing to a file or buffer NL mode is always used.
+
*channel-callback*
"callback" A function that is called when a message is received that is
not handled otherwise. It gets two arguments: the channel
***************
*** 160,168 ****
func MyCloseHandler(channel)
< *waittime*
"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
--- 162,174 ----
func MyCloseHandler(channel)
< *waittime*
"waittime" The time to wait for the connection to be made in
! milliseconds. A negative number waits forever.
!
! The default is zero, don't wait, which is useful if a local
! server is supposed to be running already. On Unix Vim
! actually uses a 1 msec timeout, that is required on many
! systems. Use a larger value for a remote server, e.g. 10
! msec at least.
"timeout" The time to wait for a request when blocking, E.g. when using
ch_sendexpr(). In milliseconds. The default is 2000 (2
***************
*** 194,199 ****
--- 200,206 ----
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.
+ All readahead is discarded, callbacks will no longer be invoked.
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
***************
*** 253,259 ****
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.
--- 260,267 ----
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. For a JSON and JS mode
! channel quotes are used around DETACH, otherwise there are no quotes.
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.
***************
*** 322,328 ****
[-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.
--- 330,336 ----
[-2, "last line"] ~
The format is:
[{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. Use a different number on
every request to be able to match the request with the response.
***************
*** 392,398 ****
"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)
--- 400,406 ----
"closed" The channel was closed.
TODO:
! To obtain the job associated with a channel: ch_getjob(channel)
To read one message from a channel: >
let output = ch_read(channel)
***************
*** 443,452 ****
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()
--- 451,463 ----
JSON or JS mode you can use ch_sendexpr().
There are several options you can use, see |job-options|.
+ For example, to start a job and write its output in buffer "dummy": >
+ let logjob = job_start("tail -f /tmp/log",
+ \ {'out-io': 'buffer', 'out-name': 'dummy'})
+ sbuf dummy
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()
***************
*** 503,509 ****
*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().
*job-exit-cb*
--- 514,520 ----
*job-err-cb*
"err-cb": handler Callback for when there is something to read on
stderr.
! *job-close-cb*
"close-cb": handler Callback for when the channel is closed. Same as
"close-cb" on ch_open().
*job-exit-cb*
***************
*** 522,549 ****
"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
==============================================================================
11. Controlling a job *job-control*
--- 533,576 ----
"term": "open" Start a terminal and connect the job
stdin/stdout/stderr to it.
! *job-in-io*
! "in-io": "null" disconnect stdin TODO
"in-io": "pipe" stdin is connected to the channel (default)
! "in-io": "file" stdin reads from a file TODO
! "in-io": "buffer" stdin reads from a buffer TODO
! "in-name": "/path/file" the name of he file or buffer to read from
! "in-buf": number the number of the buffer to read from TODO
! *job-out-io*
! "out-io": "null" disconnect stdout TODO
"out-io": "pipe" stdout is connected to the channel (default)
! "out-io": "file" stdout writes to a file TODO
"out-io": "buffer" stdout appends to a buffer
! "out-name": "/path/file" the name of the file or buffer to write to
! "out-buf": number the number of the buffer to write to TODO
!
! *job-err-io*
! "err-io": "out" same as stdout TODO
! "err-io": "null" disconnect stderr TODO
! "err-io": "pipe" stderr is connected to the channel (default)
! "err-io": "file" stderr writes to a file TODO
! "err-io": "buffer" stderr appends to a buffer TODO
! "err-name": "/path/file" the name of the file or buffer to write to
! "err-buf": number the number of the buffer to write to TODO
!
! When the IO mode is "buffer" and there is a callback, the text is appended to
! the buffer before invoking the callback.
! *E915*
! The name of the buffer is compared the full name of existing buffers. If
! there is a match that buffer is used. Otherwise a new buffer is created,
! where 'buftype' is set to "nofile" and 'bufhidden' to "hide". If you prefer
! other settings, create the buffer first and pass the buffer number.
!
! When the buffer written to is displayed in a window and the cursor is in the
! first column of the last line, the cursor will be moved to the newly added
! line and the window is scrolled up to show the cursor if needed.
! Undo is synced for every added line.
==============================================================================
11. Controlling a job *job-control*
*** ../vim-7.4.1425/src/version.c 2016-02-26 19:58:44.039326159 +0100
--- src/version.c 2016-02-26 23:23:32.502332399 +0100
***************
*** 750,751 ****
--- 750,753 ----
{ /* Add new patch number below this line */
+ /**/
+ 1426,
/**/
--
It is illegal for anyone to give lighted cigars to dogs, cats, and other
domesticated animal kept as pets.
[real standing law in Illinois, United States of America]
/// Bram Moolenaar -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.