mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.0.1074: ":term NONE" does not work on MS-Windows
Problem: ":term NONE" does not work on MS-Windows. Solution: Make it work. Split "pty" into "pty_in" and "pty_out". (Yasuhiro Matsumoto, closes #2058, closes #2045)
This commit is contained in:
parent
ba2929b6af
commit
2dc9d26c14
@ -2401,7 +2401,7 @@ term_getscrolled({buf}) Number get the scroll count of a terminal
|
||||
term_getsize({buf}) List get the size of a terminal
|
||||
term_getstatus({buf}) String get the status of a terminal
|
||||
term_gettitle({buf}) String get the title of a terminal
|
||||
term_gettty({buf}) String get the tty name of a terminal
|
||||
term_getttty({buf}, [{input}]) String get the tty name of a terminal
|
||||
term_list() List get the list of terminal buffers
|
||||
term_scrape({buf}, {row}) List get row of a terminal screen
|
||||
term_sendkeys({buf}, {keys}) none send keystrokes to a terminal
|
||||
@ -5245,7 +5245,8 @@ job_info({job}) *job_info()*
|
||||
"status" what |job_status()| returns
|
||||
"channel" what |job_getchannel()| returns
|
||||
"process" process ID
|
||||
"tty" controlling terminal name, empty when none
|
||||
"tty_in" terminal input name, empty when none
|
||||
"tty_out" terminal output name, empty when none
|
||||
"exitval" only valid when "status" is "dead"
|
||||
"exit_cb" function to be called on exit
|
||||
"stoponexit" |job-stoponexit|
|
||||
@ -8092,10 +8093,13 @@ term_gettitle({buf}) *term_gettitle()*
|
||||
string is returned.
|
||||
{only available when compiled with the |+terminal| feature}
|
||||
|
||||
term_gettty({buf}) *term_gettty()*
|
||||
term_gettty({buf} [, {input}]) *term_gettty()*
|
||||
Get the name of the controlling terminal associated with
|
||||
terminal window {buf}.
|
||||
{buf} is used as with |term_getsize()|.
|
||||
terminal window {buf}. {buf} is used as with |term_getsize()|.
|
||||
|
||||
When {input} is omitted or 0, return the name for writing
|
||||
(stdout). When {input} is 1 return the name for reading
|
||||
(stdin). On UNIX, both return same name.
|
||||
{only available when compiled with the |+terminal| feature}
|
||||
|
||||
term_list() *term_list()*
|
||||
@ -8173,10 +8177,9 @@ term_start({cmd}, {options}) *term_start()*
|
||||
specified "botright sbuf %d" is used
|
||||
"eof_chars" Text to send after all buffer lines were
|
||||
written to the terminal. When not set
|
||||
CTRL-D is used. For Python use CTRL-Z or
|
||||
"exit()". For a shell use "exit". A CR
|
||||
is always added.
|
||||
{only on MS-Windows}
|
||||
CTRL-D is used on MS-Windows. For Python
|
||||
use CTRL-Z or "exit()". For a shell use
|
||||
"exit". A CR is always added.
|
||||
|
||||
{only available when compiled with the |+terminal| feature}
|
||||
|
||||
|
@ -969,7 +969,13 @@ ch_close_part(channel_T *channel, ch_part_T part)
|
||||
if ((part == PART_IN || channel->CH_IN_FD != *fd)
|
||||
&& (part == PART_OUT || channel->CH_OUT_FD != *fd)
|
||||
&& (part == PART_ERR || channel->CH_ERR_FD != *fd))
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (channel->ch_named_pipe)
|
||||
DisconnectNamedPipe((HANDLE)fd);
|
||||
#endif
|
||||
fd_close(*fd);
|
||||
}
|
||||
}
|
||||
*fd = INVALID_FD;
|
||||
|
||||
@ -3086,7 +3092,20 @@ channel_wait(channel_T *channel, sock_T fd, int timeout)
|
||||
if (r && nread > 0)
|
||||
return CW_READY;
|
||||
if (r == 0)
|
||||
return CW_ERROR;
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
|
||||
if (err != ERROR_BAD_PIPE && err != ERROR_BROKEN_PIPE)
|
||||
return CW_ERROR;
|
||||
|
||||
if (channel->ch_named_pipe)
|
||||
{
|
||||
DisconnectNamedPipe((HANDLE)fd);
|
||||
ConnectNamedPipe((HANDLE)fd, NULL);
|
||||
}
|
||||
else
|
||||
return CW_ERROR;
|
||||
}
|
||||
|
||||
/* perhaps write some buffer lines */
|
||||
channel_write_any_lines();
|
||||
@ -3670,7 +3689,20 @@ channel_send(
|
||||
if (part == PART_SOCK)
|
||||
res = sock_write(fd, (char *)buf, len);
|
||||
else
|
||||
{
|
||||
res = fd_write(fd, (char *)buf, len);
|
||||
#ifdef WIN32
|
||||
if (channel->ch_named_pipe)
|
||||
{
|
||||
if (res < 0)
|
||||
{
|
||||
DisconnectNamedPipe((HANDLE)fd);
|
||||
ConnectNamedPipe((HANDLE)fd, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
if (res < 0 && (errno == EWOULDBLOCK
|
||||
#ifdef EAGAIN
|
||||
|| errno == EAGAIN
|
||||
@ -4849,7 +4881,8 @@ job_free_contents(job_T *job)
|
||||
}
|
||||
mch_clear_job(job);
|
||||
|
||||
vim_free(job->jv_tty_name);
|
||||
vim_free(job->jv_tty_in);
|
||||
vim_free(job->jv_tty_out);
|
||||
vim_free(job->jv_stoponexit);
|
||||
free_callback(job->jv_exit_cb, job->jv_exit_partial);
|
||||
}
|
||||
@ -5503,8 +5536,10 @@ job_info(job_T *job, dict_T *dict)
|
||||
nr = job->jv_proc_info.dwProcessId;
|
||||
#endif
|
||||
dict_add_nr_str(dict, "process", nr, NULL);
|
||||
dict_add_nr_str(dict, "tty", 0L,
|
||||
job->jv_tty_name != NULL ? job->jv_tty_name : (char_u *)"");
|
||||
dict_add_nr_str(dict, "tty_in", 0L,
|
||||
job->jv_tty_in != NULL ? job->jv_tty_in : (char_u *)"");
|
||||
dict_add_nr_str(dict, "tty_out", 0L,
|
||||
job->jv_tty_out != NULL ? job->jv_tty_out : (char_u *)"");
|
||||
|
||||
dict_add_nr_str(dict, "exitval", job->jv_exitval, NULL);
|
||||
dict_add_nr_str(dict, "exit_cb", 0L, job->jv_exit_cb);
|
||||
|
@ -843,7 +843,7 @@ static struct fst
|
||||
{"term_getsize", 1, 1, f_term_getsize},
|
||||
{"term_getstatus", 1, 1, f_term_getstatus},
|
||||
{"term_gettitle", 1, 1, f_term_gettitle},
|
||||
{"term_gettty", 1, 1, f_term_gettty},
|
||||
{"term_gettty", 1, 2, f_term_gettty},
|
||||
{"term_list", 0, 0, f_term_list},
|
||||
{"term_scrape", 2, 2, f_term_scrape},
|
||||
{"term_sendkeys", 2, 2, f_term_sendkeys},
|
||||
|
@ -5263,7 +5263,11 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
|
||||
&& (!(use_file_for_in || use_null_for_in)
|
||||
|| !(use_file_for_in || use_null_for_out)
|
||||
|| !(use_out_for_err || use_file_for_err || use_null_for_err)))
|
||||
open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name);
|
||||
{
|
||||
open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out);
|
||||
if (job->jv_tty_out != NULL)
|
||||
job->jv_tty_in = vim_strsave(job->jv_tty_out);
|
||||
}
|
||||
|
||||
/* TODO: without the channel feature connect the child to /dev/null? */
|
||||
/* Open pipes for stdin, stdout, stderr. */
|
||||
@ -5687,7 +5691,9 @@ mch_create_pty_channel(job_T *job, jobopt_T *options)
|
||||
int pty_slave_fd = -1;
|
||||
channel_T *channel;
|
||||
|
||||
open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name);
|
||||
open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out);
|
||||
if (job->jv_tty_out != NULL)
|
||||
job->jv_tty_in = vim_strsave(job->jv_tty_out);
|
||||
close(pty_slave_fd);
|
||||
|
||||
channel = add_channel();
|
||||
|
@ -1487,7 +1487,8 @@ struct jobvar_S
|
||||
PROCESS_INFORMATION jv_proc_info;
|
||||
HANDLE jv_job_object;
|
||||
#endif
|
||||
char_u *jv_tty_name; /* controlling tty, allocated */
|
||||
char_u *jv_tty_in; /* controlling tty input, allocated */
|
||||
char_u *jv_tty_out; /* controlling tty output, allocated */
|
||||
jobstatus_T jv_status;
|
||||
char_u *jv_stoponexit; /* allocated */
|
||||
int jv_exitval;
|
||||
@ -1652,6 +1653,9 @@ struct channel_S {
|
||||
/* callback for Netbeans when channel is
|
||||
* closed */
|
||||
|
||||
#ifdef WIN32
|
||||
int ch_named_pipe; /* using named pipe instead of pty */
|
||||
#endif
|
||||
char_u *ch_callback; /* call when any msg is not handled */
|
||||
partial_T *ch_partial;
|
||||
char_u *ch_close_cb; /* call when channel is closed */
|
||||
|
107
src/terminal.c
107
src/terminal.c
@ -38,8 +38,7 @@
|
||||
* in tl_scrollback are no longer used.
|
||||
*
|
||||
* TODO:
|
||||
* - ":term NONE" does not work on MS-Windows.
|
||||
* https://github.com/vim/vim/pull/2056
|
||||
* - patch to use GUI or cterm colors for vterm. Yasuhiro, #2067
|
||||
* - Redirecting output does not work on MS-Windows.
|
||||
* - implement term_setsize()
|
||||
* - add test for giving error for invalid 'termsize' value.
|
||||
@ -97,7 +96,8 @@ struct terminal_S {
|
||||
|
||||
/* used when tl_job is NULL and only a pty was created */
|
||||
int tl_tty_fd;
|
||||
char_u *tl_tty_name;
|
||||
char_u *tl_tty_in;
|
||||
char_u *tl_tty_out;
|
||||
|
||||
int tl_normal_mode; /* TRUE: Terminal-Normal mode */
|
||||
int tl_channel_closed;
|
||||
@ -2666,14 +2666,32 @@ f_term_gettty(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
buf_T *buf = term_get_buf(argvars);
|
||||
char_u *p;
|
||||
int num = 0;
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
if (buf == NULL)
|
||||
return;
|
||||
if (buf->b_term->tl_job != NULL)
|
||||
p = buf->b_term->tl_job->jv_tty_name;
|
||||
else
|
||||
p = buf->b_term->tl_tty_name;
|
||||
if (argvars[1].v_type != VAR_UNKNOWN)
|
||||
num = get_tv_number(&argvars[1]);
|
||||
|
||||
switch (num)
|
||||
{
|
||||
case 0:
|
||||
if (buf->b_term->tl_job != NULL)
|
||||
p = buf->b_term->tl_job->jv_tty_out;
|
||||
else
|
||||
p = buf->b_term->tl_tty_out;
|
||||
break;
|
||||
case 1:
|
||||
if (buf->b_term->tl_job != NULL)
|
||||
p = buf->b_term->tl_job->jv_tty_in;
|
||||
else
|
||||
p = buf->b_term->tl_tty_in;
|
||||
break;
|
||||
default:
|
||||
EMSG2(_(e_invarg2), get_tv_string(&argvars[1]));
|
||||
return;
|
||||
}
|
||||
if (p != NULL)
|
||||
rettv->vval.v_string = vim_strsave(p);
|
||||
}
|
||||
@ -3055,7 +3073,6 @@ term_and_job_init(
|
||||
HANDLE child_thread_handle;
|
||||
void *winpty_err;
|
||||
void *spawn_config = NULL;
|
||||
char buf[MAX_PATH];
|
||||
garray_T ga;
|
||||
char_u *cmd;
|
||||
|
||||
@ -3094,7 +3111,6 @@ term_and_job_init(
|
||||
if (term->tl_winpty == NULL)
|
||||
goto failed;
|
||||
|
||||
/* TODO: if the command is "NONE" only create a pty. */
|
||||
spawn_config = winpty_spawn_config_new(
|
||||
WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
|
||||
WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
|
||||
@ -3162,9 +3178,10 @@ term_and_job_init(
|
||||
job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle);
|
||||
job->jv_job_object = jo;
|
||||
job->jv_status = JOB_STARTED;
|
||||
sprintf(buf, "winpty://%lu",
|
||||
GetProcessId(winpty_agent_process(term->tl_winpty)));
|
||||
job->jv_tty_name = vim_strsave((char_u*)buf);
|
||||
job->jv_tty_in = utf16_to_enc(
|
||||
(short_u*)winpty_conin_name(term->tl_winpty), NULL);
|
||||
job->jv_tty_out = utf16_to_enc(
|
||||
(short_u*)winpty_conout_name(term->tl_winpty), NULL);
|
||||
++job->jv_refcount;
|
||||
term->tl_job = job;
|
||||
|
||||
@ -3205,9 +3222,68 @@ failed:
|
||||
}
|
||||
|
||||
static int
|
||||
create_pty_only(term_T *term, jobopt_T *opt)
|
||||
create_pty_only(term_T *term, jobopt_T *options)
|
||||
{
|
||||
/* TODO: implement this */
|
||||
HANDLE hPipeIn = INVALID_HANDLE_VALUE;
|
||||
HANDLE hPipeOut = INVALID_HANDLE_VALUE;
|
||||
char in_name[80], out_name[80];
|
||||
channel_T *channel = NULL;
|
||||
|
||||
create_vterm(term, term->tl_rows, term->tl_cols);
|
||||
|
||||
vim_snprintf(in_name, sizeof(in_name), "\\\\.\\pipe\\vim-%d-in-%d",
|
||||
GetCurrentProcessId(),
|
||||
curbuf->b_fnum);
|
||||
hPipeIn = CreateNamedPipe(in_name, PIPE_ACCESS_OUTBOUND,
|
||||
PIPE_TYPE_MESSAGE | PIPE_NOWAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
0, 0, NMPWAIT_NOWAIT, NULL);
|
||||
if (hPipeIn == INVALID_HANDLE_VALUE)
|
||||
goto failed;
|
||||
|
||||
vim_snprintf(out_name, sizeof(out_name), "\\\\.\\pipe\\vim-%d-out-%d",
|
||||
GetCurrentProcessId(),
|
||||
curbuf->b_fnum);
|
||||
hPipeOut = CreateNamedPipe(out_name, PIPE_ACCESS_INBOUND,
|
||||
PIPE_TYPE_MESSAGE | PIPE_NOWAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
0, 0, 0, NULL);
|
||||
if (hPipeOut == INVALID_HANDLE_VALUE)
|
||||
goto failed;
|
||||
|
||||
ConnectNamedPipe(hPipeIn, NULL);
|
||||
ConnectNamedPipe(hPipeOut, NULL);
|
||||
|
||||
term->tl_job = job_alloc();
|
||||
if (term->tl_job == NULL)
|
||||
goto failed;
|
||||
++term->tl_job->jv_refcount;
|
||||
|
||||
/* behave like the job is already finished */
|
||||
term->tl_job->jv_status = JOB_FINISHED;
|
||||
|
||||
channel = add_channel();
|
||||
if (channel == NULL)
|
||||
goto failed;
|
||||
term->tl_job->jv_channel = channel;
|
||||
channel->ch_keep_open = TRUE;
|
||||
channel->ch_named_pipe = TRUE;
|
||||
|
||||
channel_set_pipes(channel,
|
||||
(sock_T)hPipeIn,
|
||||
(sock_T)hPipeOut,
|
||||
(sock_T)hPipeOut);
|
||||
channel_set_job(channel, term->tl_job, options);
|
||||
term->tl_job->jv_tty_in = vim_strsave((char_u*)in_name);
|
||||
term->tl_job->jv_tty_out = vim_strsave((char_u*)out_name);
|
||||
|
||||
return OK;
|
||||
|
||||
failed:
|
||||
if (hPipeIn != NULL)
|
||||
CloseHandle(hPipeIn);
|
||||
if (hPipeOut != NULL)
|
||||
CloseHandle(hPipeOut);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@ -3234,7 +3310,8 @@ term_free_vterm(term_T *term)
|
||||
static void
|
||||
term_report_winsize(term_T *term, int rows, int cols)
|
||||
{
|
||||
winpty_set_size(term->tl_winpty, cols, rows, NULL);
|
||||
if (term->tl_winpty)
|
||||
winpty_set_size(term->tl_winpty, cols, rows, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -36,11 +36,11 @@ endfunc
|
||||
func Test_terminal_basic()
|
||||
let buf = Run_shell_in_terminal({})
|
||||
if has("unix")
|
||||
call assert_match("^/dev/", job_info(g:job).tty)
|
||||
call assert_match("^/dev/", term_gettty(''))
|
||||
call assert_match('^/dev/', job_info(g:job).tty_out)
|
||||
call assert_match('^/dev/', term_gettty(''))
|
||||
else
|
||||
call assert_match("^winpty://", job_info(g:job).tty)
|
||||
call assert_match("^winpty://", term_gettty(''))
|
||||
call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
|
||||
call assert_match('^\\\\.\\pipe\\', term_gettty(''))
|
||||
endif
|
||||
call assert_equal('t', mode())
|
||||
call assert_match('%aR[^\n]*running]', execute('ls'))
|
||||
@ -539,10 +539,6 @@ func Test_terminal_write_stdin()
|
||||
endfunc
|
||||
|
||||
func Test_terminal_no_cmd()
|
||||
" Todo: make this work on all systems.
|
||||
if !has('unix')
|
||||
return
|
||||
endif
|
||||
" Todo: make this work in the GUI
|
||||
if !has('gui_running')
|
||||
return
|
||||
@ -550,11 +546,20 @@ func Test_terminal_no_cmd()
|
||||
let buf = term_start('NONE', {})
|
||||
call assert_notequal(0, buf)
|
||||
|
||||
let pty = job_info(term_getjob(buf))['tty']
|
||||
let pty = job_info(term_getjob(buf))['tty_out']
|
||||
call assert_notequal('', pty)
|
||||
call system('echo "look here" > ' . pty)
|
||||
if has('win32')
|
||||
silent exe '!cmd /c "echo look here > ' . pty . '"'
|
||||
else
|
||||
call system('echo "look here" > ' . pty)
|
||||
endif
|
||||
call term_wait(buf)
|
||||
call assert_equal('look here', term_getline(buf, 1))
|
||||
|
||||
let result = term_getline(buf, 1)
|
||||
if has('win32')
|
||||
let result = substitute(result, '\s\+$', '', '')
|
||||
endif
|
||||
call assert_equal('look here', result)
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
@ -600,6 +605,7 @@ func Test_terminal_redir_file()
|
||||
call WaitFor('len(readfile("Xfile")) > 0')
|
||||
call assert_match('123', readfile('Xfile')[0])
|
||||
call delete('Xfile')
|
||||
bwipe
|
||||
endif
|
||||
|
||||
if has('unix')
|
||||
@ -608,6 +614,7 @@ func Test_terminal_redir_file()
|
||||
call WaitFor('len(readfile("Xfile")) > 0')
|
||||
call assert_match('executing job failed', readfile('Xfile')[0])
|
||||
call delete('Xfile')
|
||||
bwipe
|
||||
|
||||
call writefile(['one line'], 'Xfile')
|
||||
let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xfile'})
|
||||
|
@ -769,6 +769,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1074,
|
||||
/**/
|
||||
1073,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user