| Patch 7.4.1274 | Bram Moolenaar | 07/02/16 05:28 | Patch 7.4.1274 Problem: Cannot run a job. Solution: Add job_start(), job_status() and job_stop(). Currently only works for Unix. Files: eval.c, structs.h, runtime/doc/eval.txt, src/os_unix.c, src/proto/os_unix.pro, src/feature.h, src/version.c, src/testdir/test_channel.vim *** ../vim-7.4.1274/src/eval.c 2016-02-07 00:00:30.576064995 +0100 --- src/eval.c 2016-02-07 13:59:00.784058601 +0100 *************** *** 451,456 **** --- 451,459 ---- static long dict_len(dict_T *d); static char_u *dict2string(typval_T *tv, int copyID); static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); + #ifdef FEAT_JOB + static void job_free(job_T *job); + #endif static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static char_u *string_quote(char_u *str, int function); *************** *** 619,624 **** --- 622,632 ---- 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_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); + #endif static void f_join(typval_T *argvars, typval_T *rettv); static void f_jsondecode(typval_T *argvars, typval_T *rettv); static void f_jsonencode(typval_T *argvars, typval_T *rettv); *************** *** 3062,3071 **** { switch (tv1->v_type) { case VAR_DICT: case VAR_FUNC: case VAR_SPECIAL: ! case VAR_UNKNOWN: break; case VAR_LIST: --- 3070,3080 ---- { switch (tv1->v_type) { + case VAR_UNKNOWN: case VAR_DICT: case VAR_FUNC: case VAR_SPECIAL: ! case VAR_JOB: break; case VAR_LIST: *************** *** 3844,3849 **** --- 3853,3859 ---- case VAR_FUNC: case VAR_FLOAT: case VAR_SPECIAL: + case VAR_JOB: break; case VAR_LIST: *************** *** 5339,5344 **** --- 5349,5355 ---- return FAIL; #endif case VAR_SPECIAL: + case VAR_JOB: if (verbose) EMSG(_("E909: Cannot index a special variable")); return FAIL; *************** *** 5446,5455 **** switch (rettv->v_type) { ! case VAR_SPECIAL: case VAR_FUNC: case VAR_FLOAT: ! case VAR_UNKNOWN: break; /* not evaluating, skipping over subscript */ case VAR_NUMBER: --- 5457,5467 ---- switch (rettv->v_type) { ! case VAR_UNKNOWN: case VAR_FUNC: case VAR_FLOAT: ! case VAR_SPECIAL: ! case VAR_JOB: break; /* not evaluating, skipping over subscript */ case VAR_NUMBER: *************** *** 6167,6175 **** switch (tv1->v_type) { - case VAR_UNKNOWN: - break; - case VAR_LIST: ++recursive_cnt; r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); --- 6179,6184 ---- *************** *** 6190,6200 **** case VAR_NUMBER: return tv1->vval.v_number == tv2->vval.v_number; - #ifdef FEAT_FLOAT - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - #endif - case VAR_STRING: s1 = get_tv_string_buf(tv1, buf1); s2 = get_tv_string_buf(tv2, buf2); --- 6199,6204 ---- *************** *** 6202,6207 **** --- 6206,6222 ---- case VAR_SPECIAL: return tv1->vval.v_number == tv2->vval.v_number; + + case VAR_FLOAT: + #ifdef FEAT_FLOAT + return tv1->vval.v_float == tv2->vval.v_float; + #endif + case VAR_JOB: + #ifdef FEAT_JOB + return tv1->vval.v_job == tv2->vval.v_job; + #endif + case VAR_UNKNOWN: + break; } /* VAR_UNKNOWN can be the result of a invalid expression, let's say it *************** *** 6924,6930 **** } /* ! * Free lists and dictionaries that are no longer referenced. */ static int free_unref_items(int copyID) --- 6939,6945 ---- } /* ! * Free lists, dictionaries and jobs that are no longer referenced. */ static int free_unref_items(int copyID) *************** *** 6969,6974 **** --- 6984,6990 ---- } ll = ll_next; } + return did_free; } *************** *** 7694,7699 **** --- 7710,7749 ---- return OK; } + #ifdef FEAT_JOB + static void + job_free(job_T *job) + { + /* TODO: free any handles */ + + vim_free(job); + } + + static void + job_unref(job_T *job) + { + if (job != NULL && --job->jv_refcount <= 0) + job_free(job); + } + + /* + * Allocate a job. Sets the refcount to one. + */ + static job_T * + job_alloc(void) + { + job_T *job; + + job = (job_T *)alloc_clear(sizeof(job_T)); + if (job != NULL) + { + job->jv_refcount = 1; + } + return job; + } + + #endif + static char * get_var_special_name(int nr) { *************** *** 7789,7800 **** case VAR_STRING: case VAR_NUMBER: case VAR_UNKNOWN: *tofree = NULL; r = get_tv_string_buf(tv, numbuf); break; - #ifdef FEAT_FLOAT case VAR_FLOAT: *tofree = NULL; vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float); r = numbuf; --- 7839,7851 ---- case VAR_STRING: case VAR_NUMBER: case VAR_UNKNOWN: + case VAR_JOB: *tofree = NULL; r = get_tv_string_buf(tv, numbuf); break; case VAR_FLOAT: + #ifdef FEAT_FLOAT *tofree = NULL; vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float); r = numbuf; *************** *** 7844,7849 **** --- 7895,7901 ---- case VAR_LIST: case VAR_DICT: case VAR_SPECIAL: + case VAR_JOB: case VAR_UNKNOWN: break; } *************** *** 8148,8153 **** --- 8200,8210 ---- {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, {"items", 1, 1, f_items}, + #ifdef FEAT_CHANNEL + {"job_start", 1, 2, f_job_start}, + {"job_status", 1, 1, f_job_status}, + {"job_stop", 1, 1, f_job_stop}, + #endif {"join", 1, 2, f_join}, {"jsondecode", 1, 1, f_jsondecode}, {"jsonencode", 1, 1, f_jsonencode}, *************** *** 10535,10542 **** case VAR_NUMBER: n = argvars[0].vval.v_number == 0; break; - #ifdef FEAT_FLOAT case VAR_FLOAT: n = argvars[0].vval.v_float == 0.0; break; #endif --- 10592,10599 ---- case VAR_NUMBER: n = argvars[0].vval.v_number == 0; break; case VAR_FLOAT: + #ifdef FEAT_FLOAT n = argvars[0].vval.v_float == 0.0; break; #endif *************** *** 10552,10557 **** --- 10609,10619 ---- n = argvars[0].vval.v_number != VVAL_TRUE; break; + case VAR_JOB: + #ifdef FEAT_JOB + n = argvars[0].vval.v_job->jv_status != JOB_STARTED; + break; + #endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); n = TRUE; *************** *** 13060,13065 **** --- 13122,13130 ---- #ifdef FEAT_INS_EXPAND "insert_expand", #endif + #ifdef FEAT_JOB + "job", + #endif #ifdef FEAT_JUMPLIST "jumplist", #endif *************** *** 14188,14193 **** --- 14253,14413 ---- dict_list(argvars, rettv, 2); } + #ifdef FEAT_JOB + /* + * "job_start()" function + */ + static void + f_job_start(typval_T *argvars UNUSED, typval_T *rettv) + { + job_T *job; + char_u *cmd = NULL; + #if defined(UNIX) + # define USE_ARGV + char **argv = NULL; + int argc = 0; + #else + garray_T ga; + #endif + + rettv->v_type = VAR_JOB; + job = job_alloc(); + rettv->vval.v_job = job; + if (job == NULL) + return; + + rettv->vval.v_job->jv_status = JOB_FAILED; + #ifndef USE_ARGV + ga_init2(&ga, 200); + #endif + + if (argvars[0].v_type == VAR_STRING) + { + /* Command is a string. */ + cmd = argvars[0].vval.v_string; + #ifdef USE_ARGV + if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) + return; + argv[argc] = NULL; + #endif + } + else if (argvars[0].v_type != VAR_LIST + || argvars[0].vval.v_list == NULL + || argvars[0].vval.v_list->lv_len < 1) + { + EMSG(_(e_invarg)); + return; + } + else + { + list_T *l = argvars[0].vval.v_list; + listitem_T *li; + char_u *s; + + #ifdef USE_ARGV + /* Pass argv[] to mch_call_shell(). */ + argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1)); + if (argv == NULL) + return; + #endif + for (li = l->lv_first; li != NULL; li = li->li_next) + { + s = get_tv_string_chk(&li->li_tv); + if (s == NULL) + goto theend; + #ifdef USE_ARGV + argv[argc++] = (char *)s; + #else + if (li != l->lv_first) + { + s = vim_strsave_shellescape(s, FALSE, TRUE); + if (s == NULL) + goto theend; + } + ga_concat(&ga, s); + vim_free(s); + if (li->li_next != NULL) + ga_append(&ga, ' '); + #endif + } + #ifdef USE_ARGV + argv[argc] = NULL; + #else + cmd = ga.ga_data; + #endif + } + #ifdef USE_ARGV + mch_start_job(argv, job); + #else + mch_start_job(cmd, job); + #endif + + theend: + #ifdef USE_ARGV + if (argv != NULL) + vim_free(argv); + #else + vim_free(ga.ga_data); + #endif + } + + /* + * "job_status()" function + */ + static void + f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED) + { + char *result; + + if (argvars[0].v_type != VAR_JOB) + EMSG(_(e_invarg)); + else + { + job_T *job = argvars[0].vval.v_job; + + if (job->jv_status == JOB_ENDED) + /* No need to check, dead is dead. */ + result = "dead"; + else if (job->jv_status == JOB_FAILED) + result = "fail"; + else + result = mch_job_status(job); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave((char_u *)result); + } + } + + /* + * "job_stop()" function + */ + static void + f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED) + { + if (argvars[0].v_type != VAR_JOB) + EMSG(_(e_invarg)); + else + { + char_u *arg; + + if (argvars[1].v_type == VAR_UNKNOWN) + arg = (char_u *)""; + else + { + arg = get_tv_string_chk(&argvars[1]); + if (arg == NULL) + { + EMSG(_(e_invarg)); + return; + } + } + if (mch_stop_job(argvars[0].vval.v_job, arg) == FAIL) + rettv->vval.v_number = 0; + else + rettv->vval.v_number = 1; + } + } + #endif + /* * "join()" function */ *************** *** 14295,14300 **** --- 14515,14521 ---- case VAR_SPECIAL: case VAR_FLOAT: case VAR_FUNC: + case VAR_JOB: EMSG(_("E701: Invalid type for len()")); break; } *************** *** 19658,19663 **** --- 19879,19887 ---- else n = 7; break; + #ifdef FEAT_JOB + case VAR_JOB: n = 8; break; + #endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_type(UNKNOWN)"); n = -1; *************** *** 21024,21033 **** case VAR_DICT: dict_unref(varp->vval.v_dict); break; case VAR_NUMBER: - #ifdef FEAT_FLOAT case VAR_FLOAT: - #endif case VAR_UNKNOWN: case VAR_SPECIAL: break; --- 21248,21260 ---- case VAR_DICT: dict_unref(varp->vval.v_dict); break; + case VAR_JOB: + #ifdef FEAT_JOB + job_unref(varp->vval.v_job); + break; + #endif case VAR_NUMBER: case VAR_FLOAT: case VAR_UNKNOWN: case VAR_SPECIAL: break; *************** *** 21065,21075 **** case VAR_SPECIAL: varp->vval.v_number = 0; break; - #ifdef FEAT_FLOAT case VAR_FLOAT: varp->vval.v_float = 0.0; break; #endif case VAR_UNKNOWN: break; } --- 21292,21308 ---- case VAR_SPECIAL: varp->vval.v_number = 0; break; case VAR_FLOAT: + #ifdef FEAT_FLOAT varp->vval.v_float = 0.0; break; #endif + case VAR_JOB: + #ifdef FEAT_JOB + job_unref(varp->vval.v_job); + varp->vval.v_job = NULL; + #endif + break; case VAR_UNKNOWN: break; } *************** *** 21112,21119 **** { case VAR_NUMBER: return (long)(varp->vval.v_number); - #ifdef FEAT_FLOAT case VAR_FLOAT: EMSG(_("E805: Using a Float as a Number")); break; #endif --- 21345,21352 ---- { case VAR_NUMBER: return (long)(varp->vval.v_number); case VAR_FLOAT: + #ifdef FEAT_FLOAT EMSG(_("E805: Using a Float as a Number")); break; #endif *************** *** 21134,21139 **** --- 21367,21377 ---- case VAR_SPECIAL: return varp->vval.v_number == VVAL_TRUE ? 1 : 0; break; + case VAR_JOB: + #ifdef FEAT_JOB + EMSG(_("E910: Using a Job as a Number")); + break; + #endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); break; *************** *** 21153,21162 **** { case VAR_NUMBER: return (float_T)(varp->vval.v_number); - #ifdef FEAT_FLOAT case VAR_FLOAT: return varp->vval.v_float; - #endif case VAR_FUNC: EMSG(_("E891: Using a Funcref as a Float")); break; --- 21391,21398 ---- *************** *** 21172,21177 **** --- 21408,21418 ---- case VAR_SPECIAL: EMSG(_("E907: Using a special value as a Float")); break; + case VAR_JOB: + # ifdef FEAT_JOB + EMSG(_("E911: Using a Job as a Float")); + break; + # endif case VAR_UNKNOWN: EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); break; *************** *** 21272,21279 **** case VAR_DICT: EMSG(_("E731: using Dictionary as a String")); break; - #ifdef FEAT_FLOAT case VAR_FLOAT: EMSG(_(e_float_as_string)); break; #endif --- 21513,21520 ---- case VAR_DICT: EMSG(_("E731: using Dictionary as a String")); break; case VAR_FLOAT: + #ifdef FEAT_FLOAT EMSG(_(e_float_as_string)); break; #endif *************** *** 21284,21289 **** --- 21525,21548 ---- case VAR_SPECIAL: STRCPY(buf, get_var_special_name(varp->vval.v_number)); return buf; + case VAR_JOB: + #ifdef FEAT_JOB + { + job_T *job = varp->vval.v_job; + char *status = job->jv_status == JOB_FAILED ? "fail" + : job->jv_status == JOB_ENDED ? "dead" + : "run"; + # ifdef UNIX + vim_snprintf((char *)buf, NUMBUFLEN, + "process %ld %s", (long)job->jv_pid, status); + # else + /* TODO */ + vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status); + # endif + return buf; + } + #endif + break; case VAR_UNKNOWN: EMSG(_("E908: using an invalid value as a String")); break; *************** *** 21903,21913 **** case VAR_SPECIAL: to->vval.v_number = from->vval.v_number; break; - #ifdef FEAT_FLOAT case VAR_FLOAT: to->vval.v_float = from->vval.v_float; break; #endif case VAR_STRING: case VAR_FUNC: if (from->vval.v_string == NULL) --- 22162,22178 ---- case VAR_SPECIAL: to->vval.v_number = from->vval.v_number; break; case VAR_FLOAT: + #ifdef FEAT_FLOAT to->vval.v_float = from->vval.v_float; break; #endif + case VAR_JOB: + #ifdef FEAT_FLOAT + to->vval.v_job = from->vval.v_job; + ++to->vval.v_job->jv_refcount; + break; + #endif case VAR_STRING: case VAR_FUNC: if (from->vval.v_string == NULL) *************** *** 21970,21981 **** switch (from->v_type) { case VAR_NUMBER: - #ifdef FEAT_FLOAT case VAR_FLOAT: - #endif case VAR_STRING: case VAR_FUNC: case VAR_SPECIAL: copy_tv(from, to); break; case VAR_LIST: --- 22235,22245 ---- switch (from->v_type) { case VAR_NUMBER: case VAR_FLOAT: case VAR_STRING: case VAR_FUNC: case VAR_SPECIAL: + case VAR_JOB: copy_tv(from, to); break; case VAR_LIST: *************** *** 24649,24654 **** --- 24913,24919 ---- case VAR_UNKNOWN: case VAR_FUNC: + case VAR_JOB: continue; } fprintf(fp, "!%s\t%s\t", this_var->di_key, s); *** ../vim-7.4.1274/src/structs.h 2016-02-06 18:09:53.067952958 +0100 --- src/structs.h 2016-02-07 13:56:50.617415037 +0100 *************** *** 1110,1126 **** typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef enum { VAR_UNKNOWN = 0, ! VAR_NUMBER, /* "v_number" is used */ ! VAR_STRING, /* "v_string" is used */ ! VAR_FUNC, /* "v_string" is function name */ ! VAR_LIST, /* "v_list" is used */ ! VAR_DICT, /* "v_dict" is used */ ! VAR_FLOAT, /* "v_float" is used */ ! VAR_SPECIAL /* "v_number" is used */ } vartype_T; /* --- 1110,1128 ---- typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; + typedef struct jobvar_S job_T; typedef enum { VAR_UNKNOWN = 0, ! VAR_NUMBER, /* "v_number" is used */ ! VAR_STRING, /* "v_string" is used */ ! VAR_FUNC, /* "v_string" is function name */ ! VAR_LIST, /* "v_list" is used */ ! VAR_DICT, /* "v_dict" is used */ ! VAR_FLOAT, /* "v_float" is used */ ! VAR_SPECIAL, /* "v_number" is used */ ! VAR_JOB /* "v_job" is used */ } vartype_T; /* *************** *** 1139,1144 **** --- 1141,1149 ---- char_u *v_string; /* string value (can be NULL!) */ list_T *v_list; /* list value (can be NULL!) */ dict_T *v_dict; /* dict value (can be NULL!) */ + #ifdef FEAT_JOB + job_T *v_job; /* job value (can be NULL!) */ + #endif } vval; } typval_T; *************** *** 1204,1210 **** char_u di_flags; /* flags (only used for variable) */ char_u di_key[1]; /* key (actually longer!) */ }; - typedef struct dictitem_S dictitem_T; #define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */ --- 1209,1214 ---- *************** *** 1228,1233 **** --- 1232,1261 ---- dict_T *dv_used_prev; /* previous dict in used dicts list */ }; + typedef enum + { + JOB_FAILED, + JOB_STARTED, + JOB_ENDED + } jobstatus_T; + + /* + * Structure to hold info about a Job. + */ + struct jobvar_S + { + #ifdef UNIX + pid_t jv_pid; + int jv_exitval; + #endif + #ifdef WIN32 + PROCESS_INFORMATION jf_pi; + #endif + jobstatus_T jv_status; + + int jv_refcount; /* reference count */ + }; + /* structure used for explicit stack while garbage collecting hash tables */ typedef struct ht_stack_S { *** ../vim-7.4.1274/runtime/doc/eval.txt 2016-02-07 14:23:21.744830821 +0100 --- runtime/doc/eval.txt 2016-02-07 14:10:33.428839814 +0100 *************** *** 59,64 **** --- 56,68 ---- value. |Dictionary| Example: {'blue': "#0000ff", 'red': "#ff0000"} + Funcref A reference to a function |Funcref|. + Example: function("strlen") + + Special v:false, v:true, v:none and v:null + + Job Used for job control, see |job_start()|. + The Number and String types are converted automatically, depending on how they are used. *************** *** 1922,1927 **** --- 1952,1960 ---- isdirectory( {directory}) Number TRUE if {directory} is a directory islocked( {expr}) Number TRUE if {expr} is locked items( {dict}) List key-value pairs in {dict} + job_start({command} [, {options}]) Job start a job + job_status({job}) String get the status of a job + job_stop({job} [, {how}]) Number stop a job join( {list} [, {sep}]) String join {list} items into one String jsondecode( {string}) any decode JSON jsonencode( {expr}) String encode JSON *************** *** 4200,4205 **** --- 4303,4375 ---- order. + 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 + 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 + and further items are the arguments. All items are converted + to String. This works best on Unix. + + 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. + + On Unix $PATH is used to search for the executable only when + the command does not contain a slash. + + The job will use the same terminal as Vim. If it reads from + stdin the job and Vim will be fighting over input, that + doesn't work. Redirect stdin and stdout to avoid problems: > + let job = job_start(['sh', '-c', "myserver </dev/null >/dev/null"]) + < + 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) + + {only available when compiled with the |+channel| feature} + + job_status({job}) *job_status()* + Returns a String with the status of {job}: + "run" job is running + "fail" job failed to start + "dead" job died or was stopped after running + + {only available when compiled with the |+channel| 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: + "hup" Unix: SIGHUP + "quit" Unix: SIGQUIT + "kill" Unix: SIGKILL (strongest way to stop) + number Unix: signal with that number + + 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. + + {only available when compiled with the |+channel| feature} + join({list} [, {sep}]) *join()* Join the items in {list} together into one String. When {sep} is specified it is put in between the items. If *** ../vim-7.4.1274/src/os_unix.c 2016-02-07 14:23:21.744830821 +0100 --- src/os_unix.c 2016-02-07 13:46:32.215854387 +0100 *************** *** 3919,3924 **** --- 3919,3984 ---- return wait_pid; } + #if defined(FEAT_JOB) || !defined(USE_SYSTEM) || defined(PROTO) + int + mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc) + { + int i; + char_u *p; + int inquote; + + /* + * Do this loop twice: + * 1: find number of arguments + * 2: separate them and build argv[] + */ + for (i = 0; i < 2; ++i) + { + p = cmd; + inquote = FALSE; + *argc = 0; + for (;;) + { + if (i == 1) + (*argv)[*argc] = (char *)p; + ++*argc; + while (*p != NUL && (inquote || (*p != ' ' && *p != TAB))) + { + if (*p == '"') + inquote = !inquote; + ++p; + } + if (*p == NUL) + break; + if (i == 1) + *p++ = NUL; + p = skipwhite(p); + } + if (*argv == NULL) + { + if (use_shcf) + { + /* Account for possible multiple args in p_shcf. */ + p = p_shcf; + for (;;) + { + p = skiptowhite(p); + if (*p == NUL) + break; + ++*argc; + p = skipwhite(p); + } + } + + *argv = (char **)alloc((unsigned)((*argc + 4) * sizeof(char *))); + if (*argv == NULL) /* out of memory */ + return FAIL; + } + } + return OK; + } + #endif + int mch_call_shell( char_u *cmd, *************** *** 4046,4052 **** # define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use 127, some shells use that already */ ! char_u *newcmd = NULL; pid_t pid; pid_t wpid = 0; pid_t wait_pid = 0; --- 4106,4112 ---- # define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use 127, some shells use that already */ ! char_u *newcmd; pid_t pid; pid_t wpid = 0; pid_t wait_pid = 0; *************** *** 4061,4067 **** char_u *p_shcf_copy = NULL; int i; char_u *p; - int inquote; int pty_master_fd = -1; /* for pty's */ # ifdef FEAT_GUI int pty_slave_fd = -1; --- 4121,4126 ---- *************** *** 4086,4138 **** if (options & SHELL_COOKED) settmode(TMODE_COOK); /* set to normal mode */ ! /* ! * Do this loop twice: ! * 1: find number of arguments ! * 2: separate them and build argv[] ! */ ! for (i = 0; i < 2; ++i) ! { ! p = newcmd; ! inquote = FALSE; ! argc = 0; ! for (;;) ! { ! if (i == 1) ! argv[argc] = (char *)p; ! ++argc; ! while (*p && (inquote || (*p != ' ' && *p != TAB))) ! { ! if (*p == '"') ! inquote = !inquote; ! ++p; ! } ! if (*p == NUL) ! break; ! if (i == 1) ! *p++ = NUL; ! p = skipwhite(p); ! } ! if (argv == NULL) ! { ! /* ! * Account for possible multiple args in p_shcf. ! */ ! p = p_shcf; ! for (;;) ! { ! p = skiptowhite(p); ! if (*p == NUL) ! break; ! ++argc; ! p = skipwhite(p); ! } - argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *))); - if (argv == NULL) /* out of memory */ - goto error; - } - } if (cmd != NULL) { char_u *s; --- 4145,4153 ---- if (options & SHELL_COOKED) settmode(TMODE_COOK); /* set to normal mode */ ! if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL) ! goto error; if (cmd != NULL) { char_u *s; *************** *** 5006,5011 **** --- 5021,5117 ---- #endif /* USE_SYSTEM */ } + #if defined(FEAT_JOB) || defined(PROTO) + void + mch_start_job(char **argv, job_T *job) + { + pid_t pid = fork(); + + if (pid == -1) /* maybe we should use vfork() */ + { + job->jv_status = JOB_FAILED; + } + else if (pid == 0) + { + /* child */ + reset_signals(); /* handle signals normally */ + + # ifdef HAVE_SETSID + /* Create our own process group, so that the child and all its + * children can be kill()ed. Don't do this when using pipes, + * because stdin is not a tty, we would lose /dev/tty. */ + (void)setsid(); + # endif + + /* 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; + } + } + + char * + mch_job_status(job_T *job) + { + # ifdef HAVE_UNION_WAIT + union wait status; + # else + int status = -1; + # endif + pid_t wait_pid = 0; + + # ifdef __NeXT__ + wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0); + # else + wait_pid = waitpid(job->jv_pid, &status, WNOHANG); + # endif + if (wait_pid == -1) + { + /* process must have exited */ + job->jv_status = JOB_ENDED; + return "dead"; + } + if (wait_pid == 0) + return "run"; + if (WIFEXITED(status)) + { + /* LINTED avoid "bitwise operation on signed value" */ + job->jv_exitval = WEXITSTATUS(status); + job->jv_status = JOB_ENDED; + return "dead"; + } + return "run"; + } + + int + mch_stop_job(job_T *job, char_u *how) + { + int sig = -1; + + if (STRCMP(how, "hup") == 0) + sig = SIGHUP; + else if (*how == NUL || STRCMP(how, "term") == 0) + sig = SIGTERM; + else if (STRCMP(how, "quit") == 0) + sig = SIGQUIT; + else if (STRCMP(how, "kill") == 0) + sig = SIGKILL; + else if (isdigit(*how)) + sig = atoi((char *)how); + else + return FAIL; + kill(job->jv_pid, sig); + return OK; + } + #endif + /* * Check for CTRL-C typed by reading all available characters. * In cooked mode we should get SIGINT, no need to check. *** ../vim-7.4.1274/src/proto/os_unix.pro 2016-02-07 14:23:21.744830821 +0100 --- src/proto/os_unix.pro 2016-02-07 13:42:04.306643148 +0100 *************** *** 55,61 **** --- 55,65 ---- int mch_get_shellsize(void); void mch_set_shellsize(void); void mch_new_shellsize(void); + int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc); int mch_call_shell(char_u *cmd, int options); + void mch_start_job(char **argv, job_T *job); + char *mch_job_status(job_T *job); + int mch_stop_job(job_T *job, char_u *how); void mch_breakcheck(void); int mch_expandpath(garray_T *gap, char_u *path, int flags); int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags); *** ../vim-7.4.1274/src/feature.h 2016-02-07 14:23:21.744830821 +0100 --- src/feature.h 2016-02-06 22:07:35.019457345 +0100 *************** *** 1255,1267 **** #endif /* ! * The Channel feature requires +eval. */ #if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL) # undef FEAT_CHANNEL #endif /* * +signs Allow signs to be displayed to the left of text lines. * Adds the ":sign" command. */ --- 1255,1274 ---- #endif /* ! * The +channel feature requires +eval. */ #if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL) # undef FEAT_CHANNEL #endif /* + * The +job feature requires Unix and +eval. + */ + #if defined(UNIX) && defined(FEAT_EVAL) + # define FEAT_JOB + #endif + + /* * +signs Allow signs to be displayed to the left of text lines. * Adds the ":sign" command. */ *** ../vim-7.4.1274/src/version.c 2016-02-07 14:23:21.744830821 +0100 --- src/version.c 2016-02-07 14:15:33.405713009 +0100 *************** *** 289,294 **** --- 289,299 ---- #else "-insert_expand", #endif + #ifdef FEAT_JOB + "+job", + #else + "-job", + #endif #ifdef FEAT_JUMPLIST "+jumplist", #else *** ../vim-7.4.1274/src/testdir/test_channel.vim 2016-02-07 14:23:21.744830821 +0100 --- src/testdir/test_channel.vim 2016-02-07 14:08:57.777836779 +0100 *************** *** 8,15 **** " This test requires the Python command to run the test server. " This most likely only works on Unix and Windows. if has('unix') ! " We also need the pkill command to make sure the server can be stopped. ! if !executable('python') || !executable('pkill') finish endif elseif has('win32') --- 8,16 ---- " This test requires the Python command to run the test server. " This most likely only works on Unix and Windows. if has('unix') ! " We also need the job feature or the pkill command to make sure the server ! " can be stopped. ! if !(executable('python') && (has('job') || executable('pkill'))) finish endif elseif has('win32') *************** *** 27,33 **** " The Python program writes the port number in Xportnr. call delete("Xportnr") ! if has('win32') silent !start cmd /c start "test_channel" py test_channel.py else silent !python test_channel.py& --- 28,36 ---- " The Python program writes the port number in Xportnr. call delete("Xportnr") ! if has('job') ! let s:job = job_start("python test_channel.py") ! elseif has('win32') silent !start cmd /c start "test_channel" py test_channel.py else silent !python test_channel.py& *************** *** 62,68 **** endfunc func s:kill_server() ! if has('win32') call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') else call system("pkill -f test_channel.py") --- 65,73 ---- endfunc func s:kill_server() ! if has('job') ! call job_stop(s:job) ! elseif has('win32') call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') else call system("pkill -f test_channel.py") *** ../vim-7.4.1274/src/version.c 2016-02-07 14:23:21.744830821 +0100 --- src/version.c 2016-02-07 14:15:33.405713009 +0100 *************** *** 744,745 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1274, /**/ -- hundred-and-one symptoms of being an internet addict: 167. You have more than 200 websites bookmarked. /// Bram Moolenaar -- Bram@Moolenaar.net -- 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 /// |
| Re: Patch 7.4.1274 | James McCoy | 07/02/16 06:42 | On Sun, Feb 07, 2016 at 02:27:53PM +0100, Bram Moolenaar wrote:Have you had a chance to look at the job API that neovim implemented? https://neovim.io/doc/user/eval.html#jobclose%28%29 It would be nice to not have unnecessary divergence. Cheers, -- James GPG Key: 4096R/331BA3DB 2011-12-05 James McCoy <jame...@jamessan.com> |
| Re: Patch 7.4.1274 | Takuya Fujiwara | 07/02/16 07:13 | Hi Bram!
> -- > -- > 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 vim_dev+u...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. This is a really big news :) Do you have a plan to add more channel functions, like * Output to a spawned process's stdin * Input from a spawned process's stdout / stderr I think it is useful if Vim has more communication APIs. |
| Re: Patch 7.4.1274 | Bram Moolenaar | 07/02/16 07:18 | Now that the basic channel and job support is there, I would like to invite plugin writers to give feedback. We probably need some more features and/or change how things work. NeoVim is free to follow. It appears NeoVim's ideas about jobs and channels are quite different. And it seems to be quite complicated. -- "Hit any key to continue" is a lie. |
| Re: Patch 7.4.1274 | Takuya Fujiwara | 07/02/16 07:37 | Sorry, my post are folded in Google Groups because patch content is too big.
Send again.
> Patch 7.4.1274 This is a really big news :)This is my opinion as a Vim plugin writer. |
| Re: Patch 7.4.1274 | Björn Linse | 07/02/16 08:47 | On Sunday, February 7, 2016 at 4:18:38 PM UTC+1, Bram Moolenaar wrote: neovim implementation is indeed quite different as it uses libuv, and this difference will probably remain. But what specifically about the interface (that neovim exposes to plugin developer) do you think is too complicated? vim and neovim will likely diverge to the exact features supported, but it would be nice if the shared base features had the same interface. |
| Re: Patch 7.4.1274 | Bram Moolenaar | 07/02/16 10:22 | Please don't quote the whole patch! Using a job stdin and/or stdout to connect a channel to would be possible. It's some work to handle the difference between a socket and a file handle, but it's doable. In fact, dealing with stdin/stdout already exists for system(). Main difference is how to handle non-blocking wait and timeout. I don't have a goal of just adding more, it needs to be useful for plugin writers. It would be good to have examples of how a feature would be used. 169. You hire a housekeeper for your home page. |
| Re: Patch 7.4.1274 | Justin M. Keyes | 07/02/16 11:49 |
So just to be clear: before implementing jobs and channels, you did not seriously look at the existing work in Neovim and libuv? |
| Re: Patch 7.4.1274 | Bram Moolenaar | 07/02/16 12:19 | I didn't spend much time on it, but I failed to see what messages are
exchanged between NeoVim and a plugin. It mentions using an API and generating docs for that... Since the messages are binary, it's impossible to use them without the msgpack library. Makes debugging difficult. 175. You send yourself e-mail before you go to bed to remind you what to do when you wake up. |
| Re: Patch 7.4.1274 | Heptite | 07/02/16 12:27 | I spent some time playing with this, and one thing I noticed is that there's no way to get the status of a channel "out of band". For example, when I was writing my little Perl server for Vim to communicate with occasionally it would die on me, but my Vim script didn't know that until one of the ch_* commands threw an error, which I currently handle using either :silent or :try/:catch/:endtry. A ch_status() or something like it that returns a dictionary of info about the channel would be useful. For example, it could indicate the mode, callback, waittime, last message number sent/received, etc. and if the channel is dead it could just return an empty dictionary. The other thing I noticed is that even though it says in the documentation that you can specify a callback in ch_open, it doesn't actually call the callback--I still have to specify the callback on every call to ch_sendexpr. I'm assuming it's just not implemented yet and I should be patient.- Christian
-- Christian J. Robinson <hep...@gmail.com>
|
| Re: Patch 7.4.1274 | Björn Linse | 07/02/16 12:35 | On Sunday, February 7, 2016 at 9:19:50 PM UTC+1, Bram Moolenaar wrote:
> Björn Linse wrote:Maybe, but I was primarily referring to just the basic job functionality that this patch implements. For instance, a basic neovim example that just roundtrips some data over cat: function! s:JobHandler(job_id, data, event) call append(line('$'), a:data) endfunction let job1 = jobstart(['cat'], {'on_stdout': function('s:JobHandler')}) call jobsend(job1, ['some', 'text','']) call jobclose(job1, 'stdin') some more documentation here https://neovim.io/doc/user/job_control.html |
| Re: Patch 7.4.1274 | Bram Moolenaar | 07/02/16 13:55 | I had a look and didn't like it.
-- If Microsoft would build a car... ... You'd have to press the "Start" button to turn the engine off. |
| Re: Patch 7.4.1274 | Bram Moolenaar | 07/02/16 13:55 | That's a good point. I suppose that when Vim uses select() on the channel it will notice that it closed. I would think that in most cases a simple string would be sufficient. Like we have job_status(). We can have other functions to get the mode and timeout, although in most cases you would already know. Not sure why you would want statistics. For debugging? I plan to add a log file for that. Also, the channel callback can be invoked when the channel closes. Currently it's only called when nothing else handles the received message. Perhaps it's more useful to have a separate "close callback" or "error callback". It is implemented, but it is only invoked when the sequence number is zero. As mentioned above, a callback for when the channel closes is planned. I'm not sure how useful it is to retry opening the channel in the background. If the channel closes unexpectedly, I would think Vim needs to do something (e.g., restart the server). I was just looking at the old netbeans code to retry connecting. It would keep trying 36 times 3 seconds, and block Vim during that time. Not really useful. This is from before when we had a timeout on the connection. I believe PyClewn is the main user of netbeans these days, perhaps the developer has an opinion. Anyway, a blocking wait can be annoying. Retrying the connection (with zero timeout) in the main loop might be usefel. 176. You lie, even to user-friends, about how long you were online yesterday. |
| Re: Patch 7.4.1274 | ZyX | 07/02/16 14:22 | 2016-02-08 0:55 GMT+03:00 Bram Moolenaar <Br...@moolenaar.net>:Why? Also you did not like the API exposed to the plugins (i.e. job* functions, msgpack RPC, especially the first) or the underlying implementation?
|
| Re: Patch 7.4.1274 | Heptite | 07/02/16 14:34 | On Sun, 7 Feb 2016, Bram Moolenaar wrote: > We can have other functions to get the mode and timeout, although inIt's surprising what users might find useful, so if it's not costly in terms of code maintenance it might make sense to provide the functionality. The reason I suggested a single function returning a dictionary is that it's extendable without having to add new functions or breaking people's Vim code. Either way is fine with me, as long as it's possible to distinguish an intentionally closed channel from an unexpectedly closed channel. >> doesn't actually call the callback [...] I'm assuming it's just not >> implemented yet > > It is implemented, but it is only invoked when the sequence numberI should have realized that, since I'm using the feature while somehow missing how it works. Perhaps a note in the documentation is in order. [...] >I think you have a point. If a retry loop makes sense in either context the user can easily implement one in VimL. - Christian -- The road to success is always under construction. Christian J. Robinson <hep...@gmail.com> http://christianrobinson.name/ |
| Re: Patch 7.4.1274 | Thiago Arruda | 08/02/16 05:47 | > NeoVim is free to follow. It appears NeoVim's ideas about jobs andBram I'd love to see Neovim and Vim have the same API for shared features, in fact I have read the documentation about Vim's new channel feature and saw it can very easily be implemented on top Neovim existing async infrastructure(Basically we have a single async core that is used for implementing all async features, so adding new sources of asynchronous events is very easy at this point). I will be glad to add a compatibility layer to Vim's channel feature if plugin authors start to use it, but unfortunately I can't do the same for this job control API as it will only make things harder for users. When using a shell like bash or zsh, one can send programs to background to work on something else, and its fine that background jobs reuse the terminal stdout/stdin for communication since the consumer is sitting in the front of the terminal. But in the context of an editor like Vim, the main use case for background jobs is to allow implementation of asynchronous plugins that parse/send data to the process(like the coprocess feature in bash/zsh), so it would make more sense to simply have `job_start` receive callbacks that are invoked when the process sends data, and add a function to send data to the job's object(IMO this is simpler than going through the additional step of setting up a channel) Take the neomake plugin for example(https://github.com/benekastah/neomake), it currently parses compilation data from programs directly, but with this new API it would probably have to write a wrapper program in some scripting language to read the job's redirected data and communicate with Vim through sockets. IMHO you should consider decoupling job control from the channel feature, or it will make things more complicated for plugin authors since they will need to setup an additional layer for socket communication with the job(correct me if I'm wrong, but from what I could understand, the channel feature only works through sockets and not from the job's stdio). I'm sure plugin authors and the community that already use Neovim's job control would be very happy if you followed our API, especially since we implemented it first(Am I wrong to say that Neovim was the main driving force behind you implementing job control and the channel feature?) I know that at the moment it is impossible for Vim to use Neovim implementation since it diverged a lot by using libuv, but maybe you could base this feature on the patch I sent two years ago?( https://groups.google.com/forum/#!topic/vim_dev/QF7Bzh1YABU). The patch is old and API is different from what Neovim currently exposes, but if your are open to the idea I will be glad to assist in updating the patch to the current API and Vim's codebase. It would be much better for the community if we helped each other. During this time developing Neovim I have faced many challenges in implementing asynchronous features, I'd be happy to share my experiences to make things easier for you. Neovim could also benefit a lot from your vast knowledge of the code. BTW have you ever considered using Neovim as a "test bench" for new Vim features? We would be happy to receive PRs from you every once in a while. |
| Re: Patch 7.4.1274 | Ken Takata | 08/02/16 05:51 | Hi Bram, 2016/2/7 Sun 22:28:08 UTC+9 Bram Moolenaar wrote: > ***************
--- a/runtime/doc/eval.txt -job_start({command} [, {options}]) Job start a job -job_status({job}) String get the status of a job -job_stop({job} [, {how}]) Number stop a job +job_start( {command} [, {options}]) Job start a job +job_status( {job}) String get the status of a job +job_stop( {job} [, {how}]) Number stop a job jsdecode( {string}) any decode JS style JSON jsencode( {expr}) String encode JS style JSON Regards, |
| Re: Patch 7.4.1274 | Bram Moolenaar | 08/02/16 07:25 | Yes, thanks. 179. You wonder why your household garbage can doesn't have an "empty recycle bin" button. |
| Re: Patch 7.4.1274 | LCD 47 | 08/02/16 08:11 | On 8 February 2016, Thiago Arruda <tpadi...@gmail.com> wrote:[...] > But in the context of an editor like Vim, the main use case for[...] +1 for these particular points. IMO the main use case for jobs won't be Vim spawning a daemon and talking to it occasionally, but rather Vim running a lengthy process in background, reading results, and updating them on the fly. And callbacks for the job's stdin, stdout, and stderr is a nice way to handle that. The alternative to callbacks would be polling the job's output, and that would be less than great. This is how the existing async plugins (vimproc etc.) work, and that's a major pain point because there is no good way to do polling in Vim. Incidentally, it's also important to be able to keep the job's stderr and stdout separate from one another. Not being able to do that is a deficiency of system(). Unrelated: the default for killonexit for job_start() should be 1. I think there's a tiny minority of situations when Vim 0 would be desirable. In the vast majority of cases background processes that would survive Vim would be runaway processes that would just consume resources. /lcd |
| Re: Patch 7.4.1274 | Takuya Fujiwara | 08/02/16 08:15 | On Mon, Feb 8, 2016 at 3:22 AM, Bram Moolenaar <Br...@moolenaar.net> wrote:Hm, you mean: 1. You are going to add a feature which connect job's stdin/stdout/stderr 2. But you don't want to add more APIs(functions) Is this right? If so, by when are you going to add (1)'s feature? (Is it until the next release?) |
| Re: Patch 7.4.1274 | Diego Viola | 08/02/16 17:27 | I've just compiled vim from git and tried the new job functionality. I tried playing a song in the background first, as in: :call job_start('mpv /path/to/song.ogg') But it just slowed down the editor a lot, I got some output back in the editor though. I also tried: :call job_start('ls') And I got output but I could not move around or anything, and it slowed down vim a lot too. Any ideas what I'm doing wrong? |
| Re: Patch 7.4.1274 | Diego Viola | 08/02/16 17:58 | I've just tried doing the same with Neovim and it worked perfectly there, no hang or anything. It just worked. |
| Re: Patch 7.4.1274 | Bram Moolenaar | 09/02/16 02:03 | Please read the documentation, this is not doing what you think it's
doing. 195. Your cat has its own home page. |
| Re: Patch 7.4.1274 | Diego Viola | 10/02/16 17:28 | Hi Bram,
I read the documentation for job_start() and it's clear to me what it does, so I tried this: let job = job_start(["/bin/sh", "-c", "mpv --really-quiet /path/to/foo.ogg"]) But everything still gets too slow even though the music is playing. Am I missing something? Diego |
| Re: Patch 7.4.1274 | Ken Takata | 10/02/16 23:34 | Hi Bram, 2016/2/7 Sun 22:28:08 UTC+9 Bram Moolenaar wrote: > Patch 7.4.1274 > + /* Why only jf_pi is prefixed "jf_"? Other members have a prefix "jv_". Ken Takata |
| Re: Patch 7.4.1274 | Bram Moolenaar | 11/02/16 00:25 | Diego Viola wrote: > I read the documentation for job_start() and it's clear to me what it > does, so I tried this: > > let job = job_start(["/bin/sh", "-c", "mpv --really-quiet /path/to/foo.ogg"]) > > But everything still gets too slow even though the music is playing. > > Am I missing something? I don't know what mpv does or how it manages to make Vim slow down. You could try: let job = job_start(["/bin/sh", "-c", "mpv --really-quiet /path/to/foo.ogg </dev/null >/dev/null"]) Not sure why you would want to run a job to play music. Or what foo.ogg sounds like. -- Your fault: core dumped /// Bram Moolenaar -- Bram@Moolenaar.net -- 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 /// |
| Re: Patch 7.4.1274 | bo...@airbladesoftware.com | 11/02/16 02:16 | On Sunday, 7 February 2016 15:18:38 UTC, Bram Moolenaar wrote: > > Now that the basic channel and job support is there, I would like to > invite plugin writers to give feedback. We probably need some more > features and/or change how things work. Is it possible to read the job's stdout once it has finished? I'm thinking of vim-gitgutter which runs git-diff, parses the diff and updates signs. How would I read the resulting diff if I ran it in a vim job? Yours, |
| Re: Patch 7.4.1274 | Bram Moolenaar | 11/02/16 03:49 | Ken Takata wrote: > 2016/2/7 Sun 22:28:08 UTC+9 Bram Moolenaar wrote: > > Patch 7.4.1274 > > Problem: Cannot run a job. > > Solution: Add job_start(), job_status() and job_stop(). Currently only works > > for Unix. > > Files: eval.c, structs.h, runtime/doc/eval.txt, src/os_unix.c, > > src/proto/os_unix.pro, src/feature.h, src/version.c, > > src/testdir/test_channel.vim > > > > + /* > > + * Structure to hold info about a Job. > > + */ > > + struct jobvar_S > > + { > > + #ifdef UNIX > > + pid_t jv_pid; > > + int jv_exitval; > > + #endif > > + #ifdef WIN32 > > + PROCESS_INFORMATION jf_pi; > > + #endif > > + jobstatus_T jv_status; > > + > > + int jv_refcount; /* reference count */ > > + }; > > Why only jf_pi is prefixed "jf_"? Other members have a prefix "jv_". > Is this intended or just a mistake? That's a typo. I added the jf_pi as a placeholder and Yasuhiro implemented the stuff without correcting me. I'll fix it now. Thanks for pointing out. -- If evolution theories are correct, humans will soon grow a third hand for operating the mouse. |
| Re: Patch 7.4.1274 | Bram Moolenaar | 11/02/16 04:43 | Yes, you will be able to start the job with only an exit handler, and
when that handler is called do a ch_readall() on stdout. Something like that. I didn't have this in the spec, but we do actually already have it implemented internally, a non-blocking read on the channel. -- hundred-and-one symptoms of being an internet addict: 224. You set up your own Web page. You set up a Web page for each of your kids... and your pets. |
| Re: Patch 7.4.1274 | Diego Viola | 11/02/16 12:38 | Hi Bram,
mpv is a music player (mplayer fork), see https://mpv.io/ I don't need to play music in vim, I just wanted to try the job functionality and see how it works, and this was the first idea that came to my mind. :-) I tried the command you suggested and vim is not slow when I use </dev/null >/dev/null as part of the mpv command. Diego |
| Re: Patch 7.4.1274 | Diego Viola | 11/02/16 12:39 | Sorry, not just music player, but video/audio player.
Diego |
| Re: Patch 7.4.1274 | Christian Brabandt | 11/02/16 12:45 | And it produces lot of output when running in the terminal, correct? So redirecting stdin and stdout is the way to go. Best, Christian -- Wie man sein Kind nicht nennen sollte: Jo Seff |
| Re: Patch 7.4.1274 | Diego Viola | 14/02/16 10:15 | So is this a bug or what?
Diego |
| Re: Patch 7.4.1274 | Christian Brabandt | 14/02/16 11:28 | Hi Diego!
do you need the output? Best, Christian -- Die Ideen sind nicht verantwortlich für das, was die Menschen aus ihnen machen. -- Werner Heisenberg |
| Re: Patch 7.4.1274 | Diego Viola | 13/04/16 14:10 | Sorry for the late reply, I didn't saw it before.
No, I don't need the output. Diego |