diff src/channel.c @ 8267:108d30ed34ba v7.4.1426

commit https://github.com/vim/vim/commit/187db50d0499aecf4cfd42fb4db0a1bebf61c8cd Author: Bram Moolenaar <Bram@vim.org> Date: Sat Feb 27 14:44:26 2016 +0100 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.
author Christian Brabandt <cb@256bit.org>
date Sat, 27 Feb 2016 14:45:05 +0100
parents a412b466bedc
children ac0c43e7af20
line wrap: on
line diff
--- 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_may_free(channel_T *channel)
 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 @@ channel_set_job(channel_T *channel, job_
 }
 
 /*
+ * 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 @@ channel_set_options(channel_T *channel, 
 	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 @@ may_invoke_callback(channel_T *channel, 
     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 @@ may_invoke_callback(channel_T *channel, 
 	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 @@ may_invoke_callback(channel_T *channel, 
     }
     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 @@ may_invoke_callback(channel_T *channel, 
 		    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 @@ may_invoke_callback(channel_T *channel, 
 	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 @@ channel_status(channel_T *channel)
 /*
  * 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_close(channel_T *channel, int in
     }
 
     channel->ch_nb_close_cb = NULL;
-    channel_clear(channel);
 }
 
 /*
@@ -2160,6 +2252,24 @@ channel_select_check(int ret_in, void *r
 # 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 @@ channel_parse_messages(void)
     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 @@ channel_parse_messages(void)
 	    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 @@ channel_parse_messages(void)
 	    part = PART_SOCK;
 	}
     }
+
+    if (channel_need_redraw && must_redraw)
+    {
+	channel_need_redraw = FALSE;
+	update_screen(0);
+	setcursor();
+	cursor_on();
+	out_flush();
+    }
+
     return ret;
 }