Merge #4813 'runtime: clipboard: start daemons in /'.

This commit is contained in:
Justin M. Keyes 2016-06-10 03:05:28 -04:00
commit a160590e40
7 changed files with 88 additions and 26 deletions

View File

@ -94,6 +94,7 @@ function! s:clipboard.set(lines, regtype, reg)
let selection.data = [a:lines, a:regtype] let selection.data = [a:lines, a:regtype]
let argv = split(s:copy[a:reg], " ") let argv = split(s:copy[a:reg], " ")
let selection.detach = s:cache_enabled let selection.detach = s:cache_enabled
let selection.cwd = "/"
let jobid = jobstart(argv, selection) let jobid = jobstart(argv, selection)
if jobid <= 0 if jobid <= 0
echohl WarningMsg echohl WarningMsg

View File

@ -4309,21 +4309,24 @@ jobstart({cmd}[, {opts}]) {Nvim} *jobstart()*
with 'shell' or 'shellcmdflag' options. The above call is with 'shell' or 'shellcmdflag' options. The above call is
only written to show the idea, one needs to perform unquoting only written to show the idea, one needs to perform unquoting
and do split taking quotes into account. and do split taking quotes into account.
If passed, {opts} must be a dictionary with any of the
following keys: {opts} is a dictionary with these keys:
- on_stdout: stdout event handler on_stdout: stdout event handler
- on_stderr: stderr event handler on_stderr: stderr event handler
- on_exit: exit event handler on_exit : exit event handler
- pty: If set, the job will be connected to a new pseudo cwd : Working directory of the job; defaults to
terminal, and the job streams are connected to the master |current-directory|.
file descriptor. pty : If set, the job will be connected to a new pseudo
- width: Width of the terminal screen(only if pty is set) terminal, and the job streams are connected to
- height: Height of the terminal screen(only if pty is set) the master file descriptor.
- TERM: $TERM environment variable(only if pty is set) width : (pty only) Width of the terminal screen
- detach: Detach the job process from the nvim process. The height : (pty only) Height of the terminal screen
process won't get killed when nvim exists. If the process TERM : (pty only) $TERM environment variable
dies before nvim exits, on_exit will still be invoked. detach : (non-pty only) Detach the job process from the
This option is only allowed for non-pty jobs. nvim process. The process will not get killed
when nvim exits. If the process dies before
nvim exits, on_exit will still be invoked.
Either funcrefs or function names can be passed as event Either funcrefs or function names can be passed as event
handlers. The {opts} object is also used as the "self" handlers. The {opts} object is also used as the "self"
argument for the callback, so the caller may pass arbitrary argument for the callback, so the caller may pass arbitrary

View File

@ -11742,8 +11742,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
dict_T *job_opts = NULL; dict_T *job_opts = NULL;
ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL;
char *cwd = NULL;
if (argvars[1].v_type == VAR_DICT) { if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict; job_opts = argvars[1].vval.v_dict;
char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false);
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
// The new cwd must be a directory.
if (!os_isdir((char_u *)cwd)) {
EMSG2(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
}
}
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
shell_free_argv(argv); shell_free_argv(argv);
return; return;
@ -11753,7 +11766,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0; bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0;
bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0; bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0;
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
job_opts, pty, detach); job_opts, pty, detach, cwd);
Process *proc = (Process *)&data->proc; Process *proc = (Process *)&data->proc;
if (pty) { if (pty) {
@ -16468,8 +16481,21 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL;
dict_T *job_opts = NULL; dict_T *job_opts = NULL;
char *cwd = ".";
if (argvars[1].v_type == VAR_DICT) { if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict; job_opts = argvars[1].vval.v_dict;
char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false);
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
// The new cwd must be a directory.
if (!os_isdir((char_u *)cwd)) {
EMSG2(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
}
}
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
shell_free_argv(argv); shell_free_argv(argv);
return; return;
@ -16477,7 +16503,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
} }
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
job_opts, true, false); job_opts, true, false, cwd);
data->proc.pty.width = curwin->w_width; data->proc.pty.width = curwin->w_width;
data->proc.pty.height = curwin->w_height; data->proc.pty.height = curwin->w_height;
data->proc.pty.term_name = xstrdup("xterm-256color"); data->proc.pty.term_name = xstrdup("xterm-256color");
@ -16492,11 +16518,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
topts.resize_cb = term_resize; topts.resize_cb = term_resize;
topts.close_cb = term_close; topts.close_cb = term_close;
char *cwd = ".";
if (argvars[1].v_type == VAR_STRING
&& os_isdir(argvars[1].vval.v_string)) {
cwd = (char *)argvars[1].vval.v_string;
}
int pid = data->proc.pty.process.pid; int pid = data->proc.pty.process.pid;
char buf[1024]; char buf[1024];
@ -21764,7 +21785,8 @@ static inline TerminalJobData *common_job_init(char **argv,
ufunc_T *on_exit, ufunc_T *on_exit,
dict_T *self, dict_T *self,
bool pty, bool pty,
bool detach) bool detach,
char *cwd)
{ {
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
data->stopped = false; data->stopped = false;
@ -21788,6 +21810,7 @@ static inline TerminalJobData *common_job_init(char **argv,
proc->cb = on_process_exit; proc->cb = on_process_exit;
proc->events = data->events; proc->events = data->events;
proc->detach = detach; proc->detach = detach;
proc->cwd = cwd;
return data; return data;
} }

View File

@ -25,7 +25,7 @@ bool libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvopts.flags |= UV_PROCESS_DETACHED; uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
} }
uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.exit_cb = exit_cb;
uvproc->uvopts.cwd = NULL; uvproc->uvopts.cwd = proc->cwd;
uvproc->uvopts.env = NULL; uvproc->uvopts.env = NULL;
uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio = uvproc->uvstdio;
uvproc->uvopts.stdio_count = 3; uvproc->uvopts.stdio_count = 3;

View File

@ -21,6 +21,7 @@ struct process {
int pid, status, refcount; int pid, status, refcount;
// set to the hrtime of when process_stop was called for the process. // set to the hrtime of when process_stop was called for the process.
uint64_t stopped_time; uint64_t stopped_time;
char *cwd;
char **argv; char **argv;
Stream *in, *out, *err; Stream *in, *out, *err;
process_exit_cb cb; process_exit_cb cb;
@ -40,6 +41,7 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
.status = 0, .status = 0,
.refcount = 0, .refcount = 0,
.stopped_time = 0, .stopped_time = 0,
.cwd = NULL,
.argv = NULL, .argv = NULL,
.in = NULL, .in = NULL,
.out = NULL, .out = NULL,

View File

@ -28,6 +28,7 @@
#include "nvim/event/process.h" #include "nvim/event/process.h"
#include "nvim/os/pty_process_unix.h" #include "nvim/os/pty_process_unix.h"
#include "nvim/log.h" #include "nvim/log.h"
#include "nvim/os/os.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/pty_process_unix.c.generated.h" # include "os/pty_process_unix.c.generated.h"
@ -131,6 +132,12 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
signal(SIGTERM, SIG_DFL); signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL); signal(SIGALRM, SIG_DFL);
Process *proc = (Process *)ptyproc;
if (proc->cwd && os_chdir(proc->cwd) != 0) {
fprintf(stderr, "chdir failed: %s\n", strerror(errno));
return;
}
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
execvp(ptyproc->process.argv[0], ptyproc->process.argv); execvp(ptyproc->process.argv[0], ptyproc->process.argv);
fprintf(stderr, "execvp failed: %s\n", strerror(errno)); fprintf(stderr, "execvp failed: %s\n", strerror(errno));

View File

@ -1,11 +1,11 @@
local helpers = require('test.functional.helpers') local helpers = require('test.functional.helpers')
local clear, eq, eval, execute, feed, insert, neq, next_msg, nvim, local clear, eq, eval, execute, feed, insert, neq, next_msg, nvim,
nvim_dir, ok, source, write_file = helpers.clear, nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear,
helpers.eq, helpers.eval, helpers.execute, helpers.feed, helpers.eq, helpers.eval, helpers.execute, helpers.feed,
helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, helpers.insert, helpers.neq, helpers.next_message, helpers.nvim,
helpers.nvim_dir, helpers.ok, helpers.source, helpers.nvim_dir, helpers.ok, helpers.source,
helpers.write_file helpers.write_file, helpers.mkdir, helpers.rmdir
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
@ -37,6 +37,32 @@ describe('jobs', function()
eq({'notification', 'exit', {0, 0}}, next_msg()) eq({'notification', 'exit', {0, 0}}, next_msg())
end) end)
it('changes to given / directory', function()
nvim('command', "let g:job_opts.cwd = '/'")
nvim('command', "let j = jobstart('pwd', g:job_opts)")
eq({'notification', 'stdout', {0, {'/', ''}}}, next_msg())
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
it('changes to given `cwd` directory', function()
local dir = eval('resolve(tempname())')
mkdir(dir)
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
nvim('command', "let j = jobstart('pwd', g:job_opts)")
eq({'notification', 'stdout', {0, {dir, ''}}}, next_msg())
eq({'notification', 'exit', {0, 0}}, next_msg())
rmdir(dir)
end)
it('fails to change to invalid `cwd`', function()
local dir = eval('resolve(tempname())."-bogus"')
local _, err = pcall(function()
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
nvim('command', "let j = jobstart('pwd', g:job_opts)")
end)
ok(string.find(err, "E475: Invalid argument: expected valid directory$") ~= nil)
end)
it('returns 0 when it fails to start', function() it('returns 0 when it fails to start', function()
local status, rv = pcall(eval, "jobstart([])") local status, rv = pcall(eval, "jobstart([])")
eq(false, status) eq(false, status)