1
0
forked from aniani/vim

patch 7.4.1310

Problem:    Jobs don't open a channel.
Solution:   Create pipes and add them to the channel.  Add ch_logfile().
            Only Unix for now.
This commit is contained in:
Bram Moolenaar 2016-02-13 17:04:46 +01:00
parent 00af60bbb6
commit 6463ca229c
10 changed files with 935 additions and 346 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 07
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 13
VIM REFERENCE MANUAL by Bram Moolenaar
@ -1416,7 +1416,7 @@ v:exception The value of the exception most recently caught and not
*v:false* *false-variable*
v:false A Number with value zero. Used to put "false" in JSON. See
|jsonencode()|.
|json_encode()|.
When used as a string this evaluates to "false". >
echo v:false
< false ~
@ -1556,7 +1556,7 @@ v:mouse_col Column number for a mouse click obtained with |getchar()|.
*v:none* *none-variable*
v:none An empty String. Used to put an empty item in JSON. See
|jsonencode()|.
|json_encode()|.
When used as a number this evaluates to zero.
When used as a string this evaluates to "none". >
echo v:none
@ -1564,7 +1564,7 @@ v:none An empty String. Used to put an empty item in JSON. See
*v:null* *null-variable*
v:null An empty String. Used to put "null" in JSON. See
|jsonencode()|.
|json_encode()|.
When used as a number this evaluates to zero.
When used as a string this evaluates to "null". >
echo v:null
@ -1737,7 +1737,7 @@ v:throwpoint The point where the exception most recently caught and not
*v:true* *true-variable*
v:true A Number with value one. Used to put "true" in JSON. See
|jsonencode()|.
|json_encode()|.
When used as a string this evaluates to "true". >
echo v:true
< true ~
@ -1816,7 +1816,9 @@ call( {func}, {arglist} [, {dict}])
any call {func} with arguments {arglist}
ceil( {expr}) Float round {expr} up
ch_close( {handle}) none close a channel
ch_logfile( {fname} [, {mode}]) none start logging channel activity
ch_open( {address} [, {argdict})] Number open a channel to {address}
ch_readraw( {handle}) String read from channel {handle}
ch_sendexpr( {handle}, {expr} [, {callback}])
any send {expr} over JSON channel {handle}
ch_sendraw( {handle}, {string} [, {callback}])
@ -1980,9 +1982,9 @@ mapcheck( {name}[, {mode} [, {abbr}]])
String check for mappings matching {name}
match( {expr}, {pat}[, {start}[, {count}]])
Number position where {pat} matches in {expr}
matchadd( {group}, {pattern}[, {priority}[, {id}]])
matchadd( {group}, {pattern}[, {priority}[, {id} [, {dict}]]])
Number highlight {pattern} with {group}
matchaddpos( {group}, {list}[, {priority}[, {id}]])
matchaddpos( {group}, {pos}[, {priority}[, {id}[, {dict}]]])
Number highlight positions with {group}
matcharg( {nr}) List arguments of |:match|
matchdelete( {id}) Number delete match identified by {id}
@ -2274,7 +2276,7 @@ assert_fails({cmd} [, {error}]) *assert_fails()*
assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|.
A value is false when it is zero. When "{actual}" is not a
A value is false when it is zero. When {actual} is not a
number the assert fails.
When {msg} is omitted an error in the form "Expected False but
got {actual}" is produced.
@ -2676,10 +2678,16 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
don't fit, a vertical layout is used anyway. For some systems
the horizontal layout is always used.
ch_close({handle}) *ch_close()*
ch_close({handle}) *ch_close()*
Close channel {handle}. See |channel|.
{only available when compiled with the |+channel| feature}
ch_logfile( {fname} [, {mode}]) *ch_logfile()*
Start logging channel activity to {fname}.
When {mode} is omitted or "a" append to the file.
When {mode} is "w" start with an empty file.
When {fname} is an empty string: stop logging.
ch_open({address} [, {argdict}]) *ch_open()*
Open a channel to {address}. See |channel|.
Returns the channel handle on success. Returns a negative
@ -2703,7 +2711,13 @@ ch_open({address} [, {argdict}]) *ch_open()*
Default: 2000.
{only available when compiled with the |+channel| feature}
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
ch_readraw({handle}) *ch_readraw()*
Read from channel {handle} and return the received message.
This uses the channel timeout. When there is nothing to read
within that time an empty string is returned.
TODO: depends on channel mode.
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
Send {expr} over channel {handle}. The {expr} is encoded
according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|. *E912*
@ -2844,9 +2858,11 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
different from using {expr} directly.
When {expr} is a |List| a full copy is created. This means
that the original |List| can be changed without changing the
copy, and vice versa. When an item is a |List|, a copy for it
is made, recursively. Thus changing an item in the copy does
not change the contents of the original |List|.
copy, and vice versa. When an item is a |List| or
|Dictionary|, a copy for it is made, recursively. Thus
changing an item in the copy does not change the contents of
the original |List|.
A |Dictionary| is copied in a similar way as a |List|.
When {noref} is omitted or zero a contained |List| or
|Dictionary| is only copied once. All references point to
this single copy. With {noref} set to 1 every occurrence of a
@ -2907,6 +2923,14 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
The highlight ID can be used with |synIDattr()| to obtain
syntax information about the highlighting.
*disable_char_avail_for_testing()*
disable_char_avail_for_testing({expr})
When {expr} is 1 the internal char_avail() function will
return FALSE. When {expr} is 0 the char_avail() function will
function normally.
Only use this for a test where typeahead causes the test not
to work. E.g., to trigger the CursorMovedI autocommand event.
empty({expr}) *empty()*
Return the Number 1 if {expr} is empty, zero otherwise.
- A |List| or |Dictionary| is empty when it does not have any
@ -3937,7 +3961,7 @@ glob2regpat({expr}) *glob2regpat()*
empty string.
*globpath()*
globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
Perform glob() on all directories in {path} and concatenate
the results. Example: >
:echo globpath(&rtp, "syntax/c.vim")
@ -3963,7 +3987,7 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
they are separated by <NL> characters. Example: >
:echo globpath(&rtp, "syntax/c.vim", 0, 1)
<
{allinks} is used as with |glob()|.
{alllinks} is used as with |glob()|.
The "**" item can be used to search in a directory tree.
For example, to find all "README.txt" files in the directories
@ -4314,22 +4338,25 @@ job_start({command} [, {options}]) *job_start()*
Start a job and return a Job object. Unlike |system()| and
|:!cmd| this does not wait for the job to finish.
{command} can be a string. This works best on MS-Windows. On
{command} can be a String. This works best on MS-Windows. On
Unix it is split up in white-separated parts to be passed to
execvp(). Arguments in double quotes can contain white space.
{command} can be a list, where the first item is the executable
{command} can be a List, where the first item is the executable
and further items are the arguments. All items are converted
to String. This works best on Unix.
On MS-Windows, job_start() makes a GUI application hidden. If
want to show it, Use |:!start| instead.
The command is executed directly, not through a shell, the
'shell' option is not used. To use the shell: >
let job = job_start(["/bin/sh", "-c", "echo hello"])
< Or: >
let job = job_start('/bin/sh -c "echo hello"')
< However, the status of the job will now be the status of the
shell, and stopping the job means stopping the shell and the
command may continue to run.
< Note that this will start two processes, the shell and the
command it executes. If you don't want this use the "exec"
shell command.
On Unix $PATH is used to search for the executable only when
the command does not contain a slash.
@ -4342,12 +4369,10 @@ job_start({command} [, {options}]) *job_start()*
The returned Job object can be used to get the status with
|job_status()| and stop the job with |job_stop()|.
{options} must be a Dictionary. It can contain these optional
items:
killonexit When non-zero kill the job when Vim
exits. (default: 0, don't kill)
{options} must be a Dictionary. It can contain many optional
items, see |job-options|.
{only available when compiled with the |+channel| feature}
{only available when compiled with the |+job| feature}
job_status({job}) *job_status()*
Returns a String with the status of {job}:
@ -4355,27 +4380,40 @@ job_status({job}) *job_status()*
"fail" job failed to start
"dead" job died or was stopped after running
{only available when compiled with the |+channel| feature}
{only available when compiled with the |+job| feature}
job_stop({job} [, {how}]) *job_stop()*
Stop the {job}. This can also be used to signal the job.
When {how} is omitted or is "term" the job will be terminated
normally. For Unix SIGTERM is sent.
Other values:
normally. For Unix SIGTERM is sent. For MS-Windows
CTRL_BREAK will be sent. This goes to the process group, thus
children may also be affected.
Other values for Unix:
"hup" Unix: SIGHUP
"quit" Unix: SIGQUIT
"kill" Unix: SIGKILL (strongest way to stop)
number Unix: signal with that number
Other values for MS-Windows:
"int" Windows: CTRL_C
"kill" Windows: terminate process forcedly
Others Windows: CTRL_BREAK
On Unix the signal is sent to the process group. This means
that when the job is "sh -c command" it affects both the shell
and the command.
The result is a Number: 1 if the operation could be executed,
0 if "how" is not supported on the system.
Note that even when the operation was executed, whether the
job was actually stopped needs to be checked with
job_status().
The operation will even be done when the job wasn't running.
The status of the job isn't checked, the operation will even
be done when Vim thinks the job isn't running.
{only available when compiled with the |+channel| feature}
{only available when compiled with the |+job| feature}
join({list} [, {sep}]) *join()*
Join the items in {list} together into one String.
@ -4773,7 +4811,7 @@ match({expr}, {pat}[, {start}[, {count}]]) *match()*
done like 'magic' is set and 'cpoptions' is empty.
*matchadd()* *E798* *E799* *E801*
matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
matchadd({group}, {pattern}[, {priority}[, {id}[, {dict}]]])
Defines a pattern to be highlighted in the current window (a
"match"). It will be highlighted with {group}. Returns an
identification number (ID), which can be used to delete the
@ -4809,7 +4847,7 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
highlighted matches. The dict can have the following members:
conceal Special character to show instead of the
match (only for |hl-Conceal| highlighed
match (only for |hl-Conceal| highlighted
matches, see |:syn-cchar|)
The number of matches is not limited, as it is the case with
@ -6808,7 +6846,7 @@ type({expr}) The result is a Number, depending on the type of {expr}:
:if type(myvar) == type({})
:if type(myvar) == type(0.0)
:if type(myvar) == type(v:false)
:if type(myvar) == type(v:none
:if type(myvar) == type(v:none)
undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file

File diff suppressed because it is too large Load Diff

View File

@ -503,8 +503,10 @@ static void f_call(typval_T *argvars, typval_T *rettv);
static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif
#ifdef FEAT_CHANNEL
static void f_ch_open(typval_T *argvars, typval_T *rettv);
static void f_ch_close(typval_T *argvars, typval_T *rettv);
static void f_ch_logfile(typval_T *argvars, typval_T *rettv);
static void f_ch_open(typval_T *argvars, typval_T *rettv);
static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
#endif
@ -624,6 +626,7 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv);
static void f_islocked(typval_T *argvars, typval_T *rettv);
static void f_items(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_JOB
static void f_job_getchannel(typval_T *argvars, typval_T *rettv);
static void f_job_start(typval_T *argvars, typval_T *rettv);
static void f_job_stop(typval_T *argvars, typval_T *rettv);
static void f_job_status(typval_T *argvars, typval_T *rettv);
@ -7720,6 +7723,8 @@ failret:
static void
job_free(job_T *job)
{
if (job->jv_channel >= 0)
channel_close(job->jv_channel);
mch_clear_job(job);
vim_free(job);
}
@ -8083,7 +8088,9 @@ static struct fst
#endif
#ifdef FEAT_CHANNEL
{"ch_close", 1, 1, f_ch_close},
{"ch_logfile", 1, 2, f_ch_logfile},
{"ch_open", 1, 2, f_ch_open},
{"ch_readraw", 1, 2, f_ch_readraw},
{"ch_sendexpr", 2, 3, f_ch_sendexpr},
{"ch_sendraw", 2, 3, f_ch_sendraw},
#endif
@ -8207,6 +8214,7 @@ static struct fst
{"islocked", 1, 1, f_islocked},
{"items", 1, 1, f_items},
#ifdef FEAT_JOB
{"job_getchannel", 1, 1, f_job_getchannel},
{"job_start", 1, 2, f_job_start},
{"job_status", 1, 1, f_job_status},
{"job_stop", 1, 2, f_job_stop},
@ -9788,7 +9796,7 @@ get_channel_arg(typval_T *tv)
}
ch_idx = tv->vval.v_number;
if (!channel_is_open(ch_idx))
if (!channel_can_write_to(ch_idx))
{
EMSGN(_("E906: not an open channel"), ch_idx);
return -1;
@ -9824,6 +9832,32 @@ get_callback(typval_T *arg)
return NULL;
}
/*
* "ch_logfile()" function
*/
static void
f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
{
char_u *fname;
char_u *opt = (char_u *)"";
char_u buf[NUMBUFLEN];
FILE *file = NULL;
fname = get_tv_string(&argvars[0]);
if (argvars[1].v_type == VAR_STRING)
opt = get_tv_string_buf(&argvars[1], buf);
if (*fname != NUL)
{
file = fopen((char *)fname, *opt == 'w' ? "w" : "a");
if (file == NULL)
{
EMSG2(_(e_notopen), fname);
return;
}
}
ch_logfile(file);
}
/*
* "ch_open()" function
*/
@ -9913,6 +9947,27 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = ch_idx;
}
/*
* "ch_readraw()" function
*/
static void
f_ch_readraw(typval_T *argvars, typval_T *rettv)
{
int ch_idx;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
ch_idx = get_channel_arg(&argvars[0]);
if (ch_idx < 0)
{
EMSG(_(e_invarg));
return;
}
rettv->vval.v_string = channel_read_block(ch_idx);
}
/*
* common for "sendexpr()" and "sendraw()"
* Returns the channel index if the caller should read the response.
@ -14299,6 +14354,23 @@ f_items(typval_T *argvars, typval_T *rettv)
}
#ifdef FEAT_JOB
/*
* "job_getchannel()" function
*/
static void
f_job_getchannel(typval_T *argvars, typval_T *rettv)
{
if (argvars[0].v_type != VAR_JOB)
EMSG(_(e_invarg));
else
{
job_T *job = argvars[0].vval.v_job;
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = job->jv_channel;
}
}
/*
* "job_start()" function
*/
@ -14401,7 +14473,7 @@ theend:
* "job_status()" function
*/
static void
f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
f_job_status(typval_T *argvars, typval_T *rettv)
{
char *result;

View File

@ -1780,10 +1780,10 @@ process_message(void)
#ifdef FEAT_CHANNEL
if (msg.message == WM_NETBEANS)
{
int channel_idx = channel_socket2idx((sock_T)msg.wParam);
int channel_idx = channel_fd2idx((sock_T)msg.wParam);
if (channel_idx >= 0)
channel_read(channel_idx);
channel_read(channel_idx, FALSE, "process_message");
return;
}
#endif

View File

@ -3982,6 +3982,42 @@ mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc)
}
return OK;
}
#endif
#if !defined(USE_SYSTEM) || defined(FEAT_JOB)
static void
set_child_environment(void)
{
# ifdef HAVE_SETENV
char envbuf[50];
# else
static char envbuf_Rows[20];
static char envbuf_Columns[20];
# endif
/* Simulate to have a dumb terminal (for now) */
# ifdef HAVE_SETENV
setenv("TERM", "dumb", 1);
sprintf((char *)envbuf, "%ld", Rows);
setenv("ROWS", (char *)envbuf, 1);
sprintf((char *)envbuf, "%ld", Rows);
setenv("LINES", (char *)envbuf, 1);
sprintf((char *)envbuf, "%ld", Columns);
setenv("COLUMNS", (char *)envbuf, 1);
# else
/*
* Putenv does not copy the string, it has to remain valid.
* Use a static array to avoid losing allocated memory.
*/
putenv("TERM=dumb");
sprintf(envbuf_Rows, "ROWS=%ld", Rows);
putenv(envbuf_Rows);
sprintf(envbuf_Rows, "LINES=%ld", Rows);
putenv(envbuf_Rows);
sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
putenv(envbuf_Columns);
# endif
}
#endif
int
@ -4134,12 +4170,6 @@ mch_call_shell(
int fd_toshell[2]; /* for pipes */
int fd_fromshell[2];
int pipe_error = FALSE;
# ifdef HAVE_SETENV
char envbuf[50];
# else
static char envbuf_Rows[20];
static char envbuf_Columns[20];
# endif
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
newcmd = vim_strsave(p_sh);
@ -4349,28 +4379,7 @@ mch_call_shell(
# endif
}
# endif
/* Simulate to have a dumb terminal (for now) */
# ifdef HAVE_SETENV
setenv("TERM", "dumb", 1);
sprintf((char *)envbuf, "%ld", Rows);
setenv("ROWS", (char *)envbuf, 1);
sprintf((char *)envbuf, "%ld", Rows);
setenv("LINES", (char *)envbuf, 1);
sprintf((char *)envbuf, "%ld", Columns);
setenv("COLUMNS", (char *)envbuf, 1);
# else
/*
* Putenv does not copy the string, it has to remain valid.
* Use a static array to avoid losing allocated memory.
*/
putenv("TERM=dumb");
sprintf(envbuf_Rows, "ROWS=%ld", Rows);
putenv(envbuf_Rows);
sprintf(envbuf_Rows, "LINES=%ld", Rows);
putenv(envbuf_Rows);
sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
putenv(envbuf_Columns);
# endif
set_child_environment();
/*
* stderr is only redirected when using the GUI, so that a
@ -5030,13 +5039,34 @@ error:
void
mch_start_job(char **argv, job_T *job)
{
pid_t pid = fork();
pid_t pid;
int fd_in[2]; /* for stdin */
int fd_out[2]; /* for stdout */
int fd_err[2]; /* for stderr */
int ch_idx;
if (pid == -1) /* maybe we should use vfork() */
/* default is to fail */
job->jv_status = JOB_FAILED;
fd_in[0] = -1;
fd_out[0] = -1;
fd_err[0] = -1;
/* Open pipes for stdin, stdout, stderr. */
if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0))
goto failed;
ch_idx = add_channel();
if (ch_idx < 0)
goto failed;
pid = fork(); /* maybe we should use vfork() */
if (pid == -1)
{
job->jv_status = JOB_FAILED;
/* failed to fork */
goto failed;
}
else if (pid == 0)
if (pid == 0)
{
/* child */
reset_signals(); /* handle signals normally */
@ -5048,17 +5078,62 @@ mch_start_job(char **argv, job_T *job)
(void)setsid();
# endif
set_child_environment();
/* set up stdin for the child */
close(fd_in[1]);
close(0);
ignored = dup(fd_in[0]);
close(fd_in[0]);
/* set up stdout for the child */
close(fd_out[0]);
close(1);
ignored = dup(fd_out[1]);
close(fd_out[1]);
/* set up stderr for the child */
close(fd_err[0]);
close(2);
ignored = dup(fd_err[1]);
close(fd_err[1]);
/* See above for type of argv. */
execvp(argv[0], argv);
perror("executing job failed");
_exit(EXEC_FAILED); /* exec failed, return failure code */
}
else
/* parent */
job->jv_pid = pid;
job->jv_status = JOB_STARTED;
job->jv_channel = ch_idx;
/* child stdin, stdout and stderr */
close(fd_in[0]);
close(fd_out[1]);
close(fd_err[1]);
channel_set_pipes(ch_idx, fd_in[1], fd_out[0], fd_err[0]);
channel_set_job(ch_idx, job);
return;
failed:
if (fd_in[0] >= 0)
{
/* parent */
job->jv_pid = pid;
job->jv_status = JOB_STARTED;
close(fd_in[0]);
close(fd_in[1]);
}
if (fd_out[0] >= 0)
{
close(fd_out[0]);
close(fd_out[1]);
}
if (fd_err[0] >= 0)
{
close(fd_err[0]);
close(fd_err[1]);
}
}
@ -5104,8 +5179,8 @@ mch_job_status(job_T *job)
int
mch_stop_job(job_T *job, char_u *how)
{
int sig = -1;
pid_t job_pid;
int sig = -1;
pid_t job_pid;
if (STRCMP(how, "hup") == 0)
sig = SIGHUP;

View File

@ -1,22 +1,27 @@
/* channel.c */
void ch_logfile(FILE *file);
int add_channel(void);
void channel_gui_register_all(void);
int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
void channel_set_pipes(int idx, int in, int out, int err);
void channel_set_job(int idx, job_T *job);
void channel_set_json_mode(int idx, ch_mode_T ch_mode);
void channel_set_timeout(int idx, int timeout);
void channel_set_callback(int idx, char_u *callback);
void channel_set_req_callback(int idx, char_u *callback, int id);
char_u *channel_get(int idx);
int channel_collapse(int idx);
int channel_can_write_to(int idx);
int channel_is_open(int idx);
void channel_close(int idx);
int channel_save(int idx, char_u *buf, int len);
char_u *channel_peek(int idx);
void channel_clear(int idx);
int channel_get_id(void);
void channel_read(int idx);
void channel_read(int idx, int use_stderr, char *func);
char_u *channel_read_block(int idx);
int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
int channel_socket2idx(sock_T fd);
int channel_fd2idx(sock_T fd);
int channel_send(int idx, char_u *buf, char *fun);
int channel_poll_setup(int nfd_in, void *fds_in);
int channel_poll_check(int ret_in, void *fds_in);

View File

@ -1110,7 +1110,12 @@ typedef double float_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
typedef struct jobvar_S job_T;
typedef struct readq_S readq_T;
typedef struct jsonq_S jsonq_T;
typedef struct cbq_S cbq_T;
typedef struct channel_S channel_T;
typedef enum
{
@ -1255,8 +1260,92 @@ struct jobvar_S
jobstatus_T jv_status;
int jv_refcount; /* reference count */
int jv_channel; /* channel for I/O */
};
/*
* Structures to hold info about a Channel.
*/
struct readq_S
{
char_u *buffer;
readq_T *next;
readq_T *prev;
};
struct jsonq_S
{
typval_T *value;
jsonq_T *next;
jsonq_T *prev;
};
struct cbq_S
{
char_u *callback;
int seq_nr;
cbq_T *next;
cbq_T *prev;
};
/* mode for a channel */
typedef enum
{
MODE_RAW = 0,
MODE_JSON,
MODE_JS
} ch_mode_T;
struct channel_S {
sock_T ch_sock; /* the socket, -1 for a closed channel */
#ifdef UNIX
# define CHANNEL_PIPES
int ch_in; /* stdin of the job, -1 if not used */
int ch_out; /* stdout of the job, -1 if not used */
int ch_err; /* stderr of the job, -1 if not used */
# if defined(UNIX) && !defined(HAVE_SELECT)
int ch_sock_idx; /* used by channel_poll_setup() */
int ch_in_idx; /* used by channel_poll_setup() */
int ch_out_idx; /* used by channel_poll_setup() */
int ch_err_idx; /* used by channel_poll_setup() */
# endif
#endif
readq_T ch_head; /* dummy node, header for circular queue */
int ch_error; /* When TRUE an error was reported. Avoids
* giving pages full of error messages when
* the other side has exited, only mention the
* first error until the connection works
* again. */
#ifdef FEAT_GUI_X11
XtInputId ch_inputHandler; /* Cookie for input */
#endif
#ifdef FEAT_GUI_GTK
gint ch_inputHandler; /* Cookie for input */
#endif
#ifdef WIN32
int ch_inputHandler; /* simply ret.value of WSAAsyncSelect() */
#endif
void (*ch_close_cb)(void); /* callback for when channel is closed */
int ch_block_id; /* ID that channel_read_json_block() is
waiting for */
char_u *ch_callback; /* function to call when a msg is not handled */
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
ch_mode_T ch_mode;
jsonq_T ch_json_head; /* dummy node, header for circular queue */
int ch_timeout; /* request timeout in msec */
job_T *ch_job; /* job that uses this channel */
};
/* structure used for explicit stack while garbage collecting hash tables */
typedef struct ht_stack_S
{
@ -2729,11 +2818,3 @@ struct js_reader
void *js_cookie; /* can be used by js_fill */
};
typedef struct js_reader js_read_T;
/* mode for a channel */
typedef enum
{
MODE_RAW = 0,
MODE_JSON,
MODE_JS
} ch_mode_T;

View File

@ -273,3 +273,20 @@ func Test_connect_waittime()
call assert_true(reltimefloat(elapsed) < (has('unix') ? 1.0 : 3.0))
endif
endfunc
func Test_pipe()
if !has('job') || !has('unix')
return
endif
let job = job_start("python test_channel_pipe.py")
call assert_equal("run", job_status(job))
try
let handle = job_getchannel(job)
call ch_sendraw(handle, "echo something\n", 0)
call assert_equal("something\n", ch_readraw(handle))
let reply = ch_sendraw(handle, "quit\n")
call assert_equal("Goodbye!\n", reply)
finally
call job_stop(job)
endtry
endfunc

View File

@ -0,0 +1,24 @@
#!/usr/bin/python
#
# Server that will communicate over stdin/stderr
#
# This requires Python 2.6 or later.
from __future__ import print_function
import sys
if __name__ == "__main__":
if len(sys.argv) > 1:
print(sys.argv[1])
while True:
typed = sys.stdin.readline()
if typed.startswith("quit"):
print("Goodbye!")
sys.stdout.flush()
break
if typed.startswith("echo"):
print(typed[5:-1])
sys.stdout.flush()

View File

@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1310,
/**/
1309,
/**/