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.
diff --git a/src/channel.c b/src/channel.c
index 9f6cdc7..ebf8b52 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -52,6 +52,10 @@
 # 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,6 +346,7 @@
 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,6 +782,35 @@
 }
 
 /*
+ * 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,6 +873,16 @@
 	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,6 +1347,7 @@
     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,6 +1357,7 @@
 	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,8 +1407,8 @@
     }
     else
     {
-	/* If there is no callback drop the message. */
-	if (callback == NULL)
+	/* 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,8 +1432,11 @@
 		    return FALSE; /* incomplete message */
 	    }
 	    if (nl[1] == NUL)
-		/* get the whole buffer */
+	    {
+		/* 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,11 +1480,54 @@
 	if (!done)
 	    ch_log(channel, "Dropping message without callback");
     }
-    else if (callback != NULL)
+    else if (callback != NULL || buffer != NULL)
     {
-	/* invoke the channel callback */
-	ch_log(channel, "Invoking channel callback");
-	invoke_callback(channel, callback, argv);
+	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,6 +1585,7 @@
 /*
  * 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,7 +1641,6 @@
     }
 
     channel->ch_nb_close_cb = NULL;
-    channel_clear(channel);
 }
 
 /*
@@ -2160,6 +2252,24 @@
 # 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,6 +2282,7 @@
     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,7 +2293,8 @@
 	    part = PART_SOCK;
 	    continue;
 	}
-	if (channel->ch_part[part].ch_fd != INVALID_FD)
+	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,6 +2320,16 @@
 	    part = PART_SOCK;
 	}
     }
+
+    if (channel_need_redraw && must_redraw)
+    {
+	channel_need_redraw = FALSE;
+	update_screen(0);
+	setcursor();
+	cursor_on();
+	out_flush();
+    }
+
     return ret;
 }
 
diff --git a/src/eval.c b/src/eval.c
index f9e8517..a648637 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -9976,6 +9976,30 @@
     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,6 +10007,15 @@
 }
 
 /*
+ * 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,6 +10029,7 @@
     dict_T	*dict;
     int		todo;
     hashitem_T	*hi;
+    int		part;
 
     opt->jo_set = 0;
     if (tv->v_type == VAR_UNKNOWN)
@@ -10046,6 +10080,27 @@
 								      == 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,6 +10233,13 @@
 	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,7 +10278,10 @@
     channel_T *channel = get_channel_arg(&argvars[0]);
 
     if (channel != NULL)
+    {
 	channel_close(channel, FALSE);
+	channel_clear(channel);
+    }
 }
 
 # ifdef FEAT_JOB
@@ -14948,7 +15013,7 @@
     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)
+			    + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL)
 	return;
     job_set_options(job, &opt);
 
diff --git a/src/netbeans.c b/src/netbeans.c
index 441ca76..b0e7d9e 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -99,8 +99,11 @@
     {
 	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;
     }
 
diff --git a/src/structs.h b/src/structs.h
index c0339de..f375330 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1347,6 +1347,7 @@
 
     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,6 +1396,12 @@
 #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,6 +1409,14 @@
     (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,6 +1428,11 @@
     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! */
diff --git a/src/version.c b/src/version.c
index ee083ad..84ede58 100644
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1426,
+/**/
     1425,
 /**/
     1424,