changeset 8386:3b9a306724ec v7.4.1485

commit https://github.com/vim/vim/commit/014069a7ac51557e531eb3c8b94e36f2193f6c21 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Mar 3 22:51:40 2016 +0100 patch 7.4.1485 Problem: Job input from buffer is not implemented. Solution: Implement it. Add "in-top" and "in-bot" options.
author Christian Brabandt <cb@256bit.org>
date Thu, 03 Mar 2016 23:00:05 +0100
parents 4ee533cc650f
children e0b156d7b321
files src/channel.c src/eval.c src/os_unix.c src/os_win32.c src/proto/channel.pro src/structs.h src/testdir/test_channel.vim src/version.c
diffstat 8 files changed, 178 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -819,13 +819,32 @@ channel_set_pipes(channel_T *channel, so
 #endif
 
 /*
- * Sets the job the channel is associated with.
+ * Sets the job the channel is associated with and associated options.
  * This does not keep a refcount, when the job is freed ch_job is cleared.
  */
     void
-channel_set_job(channel_T *channel, job_T *job)
+channel_set_job(channel_T *channel, job_T *job, jobopt_T *options)
 {
     channel->ch_job = job;
+
+    channel_set_options(channel, options);
+
+    if (job->jv_in_buf != NULL)
+    {
+	chanpart_T *in_part = &channel->ch_part[PART_IN];
+
+	in_part->ch_buffer = job->jv_in_buf;
+	ch_logs(channel, "reading from buffer '%s'",
+					(char *)in_part->ch_buffer->b_ffname);
+	if (options->jo_set & JO_IN_TOP)
+	    in_part->ch_buf_top = options->jo_in_top;
+	else
+	    in_part->ch_buf_top = 1;
+	if (options->jo_set & JO_IN_BOT)
+	    in_part->ch_buf_bot = options->jo_in_bot;
+	else
+	    in_part->ch_buf_bot = in_part->ch_buffer->b_ml.ml_line_count;
+    }
 }
 
 /*
@@ -964,6 +983,47 @@ channel_set_req_callback(
 }
 
 /*
+ * Write any lines to the in channel.
+ */
+    void
+channel_write_in(channel_T *channel)
+{
+    chanpart_T *in_part = &channel->ch_part[PART_IN];
+    linenr_T    lnum;
+    buf_T	*buf = in_part->ch_buffer;
+
+    if (buf == NULL)
+	return;
+    if (!buf_valid(buf) || buf->b_ml.ml_mfp == NULL)
+    {
+	/* buffer was wiped out or unloaded */
+	in_part->ch_buffer = NULL;
+	return;
+    }
+    if (in_part->ch_fd == INVALID_FD)
+	/* pipe was closed */
+	return;
+
+    for (lnum = in_part->ch_buf_top; lnum <= in_part->ch_buf_bot
+				   && lnum <= buf->b_ml.ml_line_count; ++lnum)
+    {
+	char_u *line = ml_get_buf(buf, lnum, FALSE);
+	int	len = STRLEN(line);
+	char_u *p;
+
+	/* TODO: check if channel can be written to */
+	if ((p = alloc(len + 2)) == NULL)
+	    break;
+	STRCPY(p, line);
+	p[len] = NL;
+	p[len + 1] = NUL;
+	channel_send(channel, PART_IN, p, "channel_write_in()");
+	vim_free(p);
+    }
+    in_part->ch_buf_top = lnum;
+}
+
+/*
  * Invoke the "callback" on channel "channel".
  */
     static void
--- a/src/eval.c
+++ b/src/eval.c
@@ -9662,28 +9662,13 @@ f_bufloaded(typval_T *argvars, typval_T 
     rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
 }
 
-static buf_T *get_buf_tv(typval_T *tv, int curtab_only);
-
-/*
- * Get buffer by number or pattern.
- */
     static buf_T *
-get_buf_tv(typval_T *tv, int curtab_only)
-{
-    char_u	*name = tv->vval.v_string;
+buflist_find_by_name(char_u *name, int curtab_only)
+{
     int		save_magic;
     char_u	*save_cpo;
     buf_T	*buf;
 
-    if (tv->v_type == VAR_NUMBER)
-	return buflist_findnr((int)tv->vval.v_number);
-    if (tv->v_type != VAR_STRING)
-	return NULL;
-    if (name == NULL || *name == NUL)
-	return curbuf;
-    if (name[0] == '$' && name[1] == NUL)
-	return lastbuf;
-
     /* Ignore 'magic' and 'cpoptions' here to make scripts portable */
     save_magic = p_magic;
     p_magic = TRUE;
@@ -9695,6 +9680,28 @@ get_buf_tv(typval_T *tv, int curtab_only
 
     p_magic = save_magic;
     p_cpo = save_cpo;
+    return buf;
+}
+
+/*
+ * Get buffer by number or pattern.
+ */
+    static buf_T *
+get_buf_tv(typval_T *tv, int curtab_only)
+{
+    char_u	*name = tv->vval.v_string;
+    buf_T	*buf;
+
+    if (tv->v_type == VAR_NUMBER)
+	return buflist_findnr((int)tv->vval.v_number);
+    if (tv->v_type != VAR_STRING)
+	return NULL;
+    if (name == NULL || *name == NUL)
+	return curbuf;
+    if (name[0] == '$' && name[1] == NUL)
+	return lastbuf;
+
+    buf = buflist_find_by_name(name, curtab_only);
 
     /* If not found, try expanding the name, like done for bufexists(). */
     if (buf == NULL)
@@ -10110,6 +10117,30 @@ get_job_options(typval_T *tv, jobopt_T *
 		opt->jo_io_name[part] =
 		       get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]);
 	    }
+	    else if (STRCMP(hi->hi_key, "in-top") == 0
+		    || STRCMP(hi->hi_key, "in-bot") == 0)
+	    {
+		linenr_T *lp;
+
+		if (!(supported & JO_OUT_IO))
+		    break;
+		if (hi->hi_key[3] == 't')
+		{
+		    lp = &opt->jo_in_top;
+		    opt->jo_set |= JO_IN_TOP;
+		}
+		else
+		{
+		    lp = &opt->jo_in_bot;
+		    opt->jo_set |= JO_IN_BOT;
+		}
+		*lp = get_tv_number(item);
+		if (*lp < 0)
+		{
+		    EMSG2(_(e_invarg2), get_tv_string(item));
+		    return FAIL;
+		}
+	    }
 	    else if (STRCMP(hi->hi_key, "callback") == 0)
 	    {
 		if (!(supported & JO_CALLBACK))
@@ -15103,6 +15134,29 @@ f_job_start(typval_T *argvars UNUSED, ty
 	    JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL
 			    + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL)
 	return;
+
+    if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER)
+    {
+	buf_T *buf;
+
+	/* check that we can find the buffer before starting the job */
+	if (!(opt.jo_set & JO_IN_NAME))
+	{
+	    EMSG(_("E915: in-io buffer requires in-name to be set"));
+	    return;
+	}
+	buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE);
+	if (buf == NULL)
+	    return;
+	if (buf->b_ml.ml_mfp == NULL)
+	{
+	    EMSG2(_("E918: buffer must be loaded: %s"),
+						     opt.jo_io_name[PART_IN]);
+	    return;
+	}
+	job->jv_in_buf = buf;
+    }
+
     job_set_options(job, &opt);
 
 #ifndef USE_ARGV
@@ -15194,6 +15248,10 @@ f_job_start(typval_T *argvars UNUSED, ty
     mch_start_job((char *)cmd, job, &opt);
 #endif
 
+#ifdef FEAT_CHANNEL
+    channel_write_in(job->jv_channel);
+#endif
+
 theend:
 #ifdef USE_ARGV
     vim_free(argv);
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -5141,8 +5141,7 @@ mch_start_job(char **argv, job_T *job, j
 # ifdef FEAT_CHANNEL
     channel_set_pipes(channel, fd_in[1], fd_out[0],
 				    use_out_for_err ? INVALID_FD : fd_err[0]);
-    channel_set_job(channel, job);
-    channel_set_options(channel, options);
+    channel_set_job(channel, job, options);
 #  ifdef FEAT_GUI
     channel_gui_register(channel);
 #  endif
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -5083,9 +5083,7 @@ mch_start_job(char *cmd, job_T *job, job
     job->jv_channel = channel;
     channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0],
 			       use_out_for_err ? INVALID_FD : (sock_T)efd[0]);
-    channel_set_job(channel, job);
-    channel_set_options(channel, options);
-
+    channel_set_job(channel, job, options);
 #   ifdef FEAT_GUI
      channel_gui_register(channel);
 #   endif
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -10,9 +10,10 @@ void channel_gui_register(channel_T *cha
 void channel_gui_register_all(void);
 channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_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_job(channel_T *channel, job_T *job, jobopt_T *options);
 void channel_set_options(channel_T *channel, jobopt_T *opt);
 void channel_set_req_callback(channel_T *channel, int part, char_u *callback, int id);
+void channel_write_in(channel_T *channel);
 char_u *channel_get(channel_T *channel, int part);
 int channel_collapse(channel_T *channel, int part);
 int channel_can_write_to(channel_T *channel);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1267,6 +1267,8 @@ struct jobvar_S
     int		jv_exitval;
     char_u	*jv_exit_cb;	/* allocated */
 
+    buf_T	*jv_in_buf;	/* buffer from "in-name" */
+
     int		jv_refcount;	/* reference count */
     channel_T	*jv_channel;	/* channel for I/O, reference counted */
 };
@@ -1347,7 +1349,10 @@ typedef struct {
 
     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 */
+    linenr_T	ch_buf_top;	/* next line to send */
+    linenr_T	ch_buf_bot;	/* last line to send */
 } chanpart_T;
 
 struct channel_S {
@@ -1402,6 +1407,8 @@ struct channel_S {
 #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_IN_TOP	    0x400000	/* "in-top" */
+#define JO_IN_BOT	    0x800000	/* "in-bot" */
 #define JO_ALL		    0xffffff
 
 #define JO_MODE_ALL	(JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
@@ -1433,6 +1440,9 @@ typedef struct
     char_u	jo_io_name_buf[4][NUMBUFLEN];
     char_u	*jo_io_name[4];	/* not allocated! */
 
+    linenr_T	jo_in_top;
+    linenr_T	jo_in_bot;
+
     char_u	*jo_callback;	/* not allocated! */
     char_u	*jo_out_cb;	/* not allocated! */
     char_u	*jo_err_cb;	/* not allocated! */
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -479,6 +479,31 @@ func Test_pipe_to_buffer()
   endtry
 endfunc
 
+func Test_pipe_from_buffer()
+  if !has('job')
+    return
+  endif
+call ch_logfile('channellog', 'w')
+  call ch_log('Test_pipe_from_buffer()')
+
+  sp pipe-input
+  call setline(1, ['echo one', 'echo two', 'echo three'])
+
+  let job = job_start(s:python . " test_channel_pipe.py",
+	\ {'in-io': 'buffer', 'in-name': 'pipe-input'})
+  call assert_equal("run", job_status(job))
+  try
+    let handle = job_getchannel(job)
+    call assert_equal('one', ch_read(handle))
+    call assert_equal('two', ch_read(handle))
+    call assert_equal('three', ch_read(handle))
+    bwipe!
+  finally
+    call job_stop(job)
+  endtry
+call ch_logfile('')
+endfunc
+
 func Test_pipe_to_nameless_buffer()
   if !has('job')
     return
--- a/src/version.c
+++ b/src/version.c
@@ -744,6 +744,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1485,
+/**/
     1484,
 /**/
     1483,