To: vim_dev@googlegroups.com Subject: Patch 8.0.1074 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1074 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) Files: runtime/doc/eval.txt, src/channel.c, src/evalfunc.c, src/os_unix.c, src/structs.h, src/terminal.c, src/testdir/test_terminal.vim *** ../vim-8.0.1073/runtime/doc/eval.txt 2017-09-03 15:48:07.903554584 +0200 --- runtime/doc/eval.txt 2017-09-08 14:17:47.490506202 +0200 *************** *** 2401,2407 **** 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_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 --- 2401,2407 ---- 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_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,5251 **** "status" what |job_status()| returns "channel" what |job_getchannel()| returns "process" process ID ! "tty" controlling terminal name, empty when none "exitval" only valid when "status" is "dead" "exit_cb" function to be called on exit "stoponexit" |job-stoponexit| --- 5245,5252 ---- "status" what |job_status()| returns "channel" what |job_getchannel()| returns "process" process ID ! "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| *************** *** 5258,5263 **** --- 5259,5265 ---- 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. + To start a job in a terminal window see |term_start()|. {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 *************** *** 5996,6002 **** a non-empty String (|non-zero-arg|), then the full mode is returned, otherwise only the first letter is returned. ! n Normal no Operator-pending v Visual by character V Visual by line --- 5998,6004 ---- a non-empty String (|non-zero-arg|), then the full mode is returned, otherwise only the first letter is returned. ! n Normal, Terminal-Normal no Operator-pending v Visual by character V Visual by line *************** *** 6018,6023 **** --- 6020,6026 ---- rm The -- more -- prompt r? A |:confirm| query of some sort ! Shell or external command is executing + t Terminal-Job mode: keys go to the job This is useful in the 'statusline' option or when used with |remote_expr()| In most other places it always returns "c" or "n". *************** *** 8090,8099 **** string is returned. {only available when compiled with the |+terminal| feature} ! term_gettty({buf}) *term_gettty()* Get the name of the controlling terminal associated with ! terminal window {buf}. ! {buf} is used as with |term_getsize()|. {only available when compiled with the |+terminal| feature} term_list() *term_list()* --- 8094,8106 ---- string is returned. {only available when compiled with the |+terminal| feature} ! 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()|. ! ! 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()* *************** *** 8171,8180 **** 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} {only available when compiled with the |+terminal| feature} --- 8178,8186 ---- 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 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} *** ../vim-8.0.1073/src/channel.c 2017-09-02 17:18:31.226946592 +0200 --- src/channel.c 2017-09-08 14:20:45.521339375 +0200 *************** *** 969,975 **** --- 969,981 ---- 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,3092 **** if (r && nread > 0) return CW_READY; if (r == 0) ! return CW_ERROR; /* perhaps write some buffer lines */ channel_write_any_lines(); --- 3092,3111 ---- if (r && nread > 0) return CW_READY; if (r == 0) ! { ! 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,3676 **** --- 3689,3708 ---- 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,4855 **** } mch_clear_job(job); ! vim_free(job->jv_tty_name); vim_free(job->jv_stoponexit); free_callback(job->jv_exit_cb, job->jv_exit_partial); } --- 4881,4888 ---- } mch_clear_job(job); ! 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,5510 **** 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, "exitval", job->jv_exitval, NULL); dict_add_nr_str(dict, "exit_cb", 0L, job->jv_exit_cb); --- 5536,5545 ---- nr = job->jv_proc_info.dwProcessId; #endif dict_add_nr_str(dict, "process", nr, NULL); ! 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); *** ../vim-8.0.1073/src/evalfunc.c 2017-09-04 20:34:15.126492821 +0200 --- src/evalfunc.c 2017-09-08 14:11:56.264810129 +0200 *************** *** 843,849 **** {"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_list", 0, 0, f_term_list}, {"term_scrape", 2, 2, f_term_scrape}, {"term_sendkeys", 2, 2, f_term_sendkeys}, --- 843,849 ---- {"term_getsize", 1, 1, f_term_getsize}, {"term_getstatus", 1, 1, f_term_getstatus}, {"term_gettitle", 1, 1, f_term_gettitle}, ! {"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}, *** ../vim-8.0.1073/src/os_unix.c 2017-09-05 23:32:33.595883323 +0200 --- src/os_unix.c 2017-09-08 14:11:56.264810129 +0200 *************** *** 5263,5269 **** && (!(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); /* TODO: without the channel feature connect the child to /dev/null? */ /* Open pipes for stdin, stdout, stderr. */ --- 5263,5273 ---- && (!(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_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,5693 **** int pty_slave_fd = -1; channel_T *channel; ! open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name); close(pty_slave_fd); channel = add_channel(); --- 5691,5699 ---- int pty_slave_fd = -1; channel_T *channel; ! 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(); *** ../vim-8.0.1073/src/structs.h 2017-09-02 17:18:31.230946566 +0200 --- src/structs.h 2017-09-08 14:20:53.785285992 +0200 *************** *** 1487,1493 **** PROCESS_INFORMATION jv_proc_info; HANDLE jv_job_object; #endif ! char_u *jv_tty_name; /* controlling tty, allocated */ jobstatus_T jv_status; char_u *jv_stoponexit; /* allocated */ int jv_exitval; --- 1487,1494 ---- PROCESS_INFORMATION jv_proc_info; HANDLE jv_job_object; #endif ! 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,1657 **** --- 1653,1661 ---- /* 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 */ *** ../vim-8.0.1073/src/terminal.c 2017-09-05 23:30:57.764519090 +0200 --- src/terminal.c 2017-09-08 14:37:17.934926887 +0200 *************** *** 38,45 **** * in tl_scrollback are no longer used. * * TODO: ! * - ":term NONE" does not work on MS-Windows. ! * https://github.com/vim/vim/pull/2056 * - Redirecting output does not work on MS-Windows. * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. --- 38,44 ---- * in tl_scrollback are no longer used. * * TODO: ! * - 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,103 **** /* used when tl_job is NULL and only a pty was created */ int tl_tty_fd; ! char_u *tl_tty_name; int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_channel_closed; --- 96,103 ---- /* used when tl_job is NULL and only a pty was created */ int tl_tty_fd; ! char_u *tl_tty_in; ! char_u *tl_tty_out; int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_channel_closed; *************** *** 2666,2679 **** { buf_T *buf = term_get_buf(argvars); char_u *p; 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 (p != NULL) rettv->vval.v_string = vim_strsave(p); } --- 2666,2697 ---- { buf_T *buf = term_get_buf(argvars); char_u *p; + int num = 0; rettv->v_type = VAR_STRING; if (buf == NULL) return; ! 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,3061 **** HANDLE child_thread_handle; void *winpty_err; void *spawn_config = NULL; - char buf[MAX_PATH]; garray_T ga; char_u *cmd; --- 3073,3078 ---- *************** *** 3094,3100 **** 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, --- 3111,3116 ---- *************** *** 3162,3170 **** 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_refcount; term->tl_job = job; --- 3178,3187 ---- job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); job->jv_job_object = jo; job->jv_status = JOB_STARTED; ! 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,3213 **** } static int ! create_pty_only(term_T *term, jobopt_T *opt) { ! /* TODO: implement this */ return FAIL; } --- 3222,3289 ---- } static int ! create_pty_only(term_T *term, jobopt_T *options) { ! 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,3240 **** static void term_report_winsize(term_T *term, int rows, int cols) { ! winpty_set_size(term->tl_winpty, cols, rows, NULL); } int --- 3310,3317 ---- static void term_report_winsize(term_T *term, int rows, int cols) { ! if (term->tl_winpty) ! winpty_set_size(term->tl_winpty, cols, rows, NULL); } int *** ../vim-8.0.1073/src/testdir/test_terminal.vim 2017-09-05 20:29:20.664205036 +0200 --- src/testdir/test_terminal.vim 2017-09-08 14:11:56.268810103 +0200 *************** *** 36,46 **** 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('')) else ! call assert_match("^winpty://", job_info(g:job).tty) ! call assert_match("^winpty://", term_gettty('')) endif call assert_equal('t', mode()) call assert_match('%aR[^\n]*running]', execute('ls')) --- 36,46 ---- func Test_terminal_basic() let buf = Run_shell_in_terminal({}) if has("unix") ! call assert_match('^/dev/', job_info(g:job).tty_out) ! call assert_match('^/dev/', term_gettty('')) else ! 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,548 **** 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 --- 539,544 ---- *************** *** 550,560 **** let buf = term_start('NONE', {}) call assert_notequal(0, buf) ! let pty = job_info(term_getjob(buf))['tty'] call assert_notequal('', pty) ! call system('echo "look here" > ' . pty) call term_wait(buf) ! call assert_equal('look here', term_getline(buf, 1)) bwipe! endfunc --- 546,565 ---- let buf = term_start('NONE', {}) call assert_notequal(0, buf) ! let pty = job_info(term_getjob(buf))['tty_out'] call assert_notequal('', pty) ! if has('win32') ! silent exe '!cmd /c "echo look here > ' . pty . '"' ! else ! call system('echo "look here" > ' . pty) ! endif call term_wait(buf) ! ! let result = term_getline(buf, 1) ! if has('win32') ! let result = substitute(result, '\s\+$', '', '') ! endif ! call assert_equal('look here', result) bwipe! endfunc *************** *** 600,605 **** --- 605,611 ---- call WaitFor('len(readfile("Xfile")) > 0') call assert_match('123', readfile('Xfile')[0]) call delete('Xfile') + bwipe endif if has('unix') *************** *** 608,613 **** --- 614,620 ---- 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'}) *** ../vim-8.0.1073/src/version.c 2017-09-08 13:59:16.761754116 +0200 --- src/version.c 2017-09-08 14:14:33.307779650 +0200 *************** *** 771,772 **** --- 771,774 ---- { /* Add new patch number below this line */ + /**/ + 1074, /**/ -- % cat /usr/include/life.h void life(void); /// 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 ///