Skip to content

Commit 13ebb03

Browse files
committed
patch 8.0.1000: cannot open a terminal without running a job in it
Problem: Cannot open a terminal without running a job in it. Solution: Make ":terminal NONE" open a terminal with a pty.
1 parent dde8131 commit 13ebb03

10 files changed

Lines changed: 140 additions & 30 deletions

File tree

src/channel.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ channel_gui_register_one(channel_T *channel, ch_part_T part)
503503
if (!CH_HAS_GUI)
504504
return;
505505

506+
/* gets stuck in handling events for a not connected channel */
507+
if (channel->ch_keep_open)
508+
return;
509+
506510
# ifdef FEAT_GUI_X11
507511
/* Tell notifier we are interested in being called
508512
* when there is input on the editor connection socket. */
@@ -548,9 +552,12 @@ channel_gui_register(channel_T *channel)
548552
{
549553
if (channel->CH_SOCK_FD != INVALID_FD)
550554
channel_gui_register_one(channel, PART_SOCK);
551-
if (channel->CH_OUT_FD != INVALID_FD)
555+
if (channel->CH_OUT_FD != INVALID_FD
556+
&& channel->CH_OUT_FD != channel->CH_SOCK_FD)
552557
channel_gui_register_one(channel, PART_OUT);
553-
if (channel->CH_ERR_FD != INVALID_FD)
558+
if (channel->CH_ERR_FD != INVALID_FD
559+
&& channel->CH_ERR_FD != channel->CH_SOCK_FD
560+
&& channel->CH_ERR_FD != channel->CH_OUT_FD)
554561
channel_gui_register_one(channel, PART_ERR);
555562
}
556563

@@ -3247,11 +3254,13 @@ channel_read(channel_T *channel, ch_part_T part, char *func)
32473254

32483255
/* Reading a disconnection (readlen == 0), or an error. */
32493256
if (readlen <= 0)
3250-
ch_close_part_on_error(channel, part, (len < 0), func);
3251-
3257+
{
3258+
if (!channel->ch_keep_open)
3259+
ch_close_part_on_error(channel, part, (len < 0), func);
3260+
}
32523261
#if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
3253-
/* signal the main loop that there is something to read */
3254-
if (CH_HAS_GUI && gtk_main_level() > 0)
3262+
else if (CH_HAS_GUI && gtk_main_level() > 0)
3263+
/* signal the main loop that there is something to read */
32553264
gtk_main_quit();
32563265
#endif
32573266
}
@@ -3509,20 +3518,24 @@ channel_fd2channel(sock_T fd, ch_part_T *partp)
35093518
}
35103519
# endif
35113520

3512-
# if defined(WIN32) || defined(PROTO)
3521+
# if defined(WIN32) || defined(FEAT_GUI) || defined(PROTO)
35133522
/*
35143523
* Check the channels for anything that is ready to be read.
35153524
* The data is put in the read queue.
3525+
* if "only_keep_open" is TRUE only check channels where ch_keep_open is set.
35163526
*/
35173527
void
3518-
channel_handle_events(void)
3528+
channel_handle_events(int only_keep_open)
35193529
{
35203530
channel_T *channel;
35213531
ch_part_T part;
35223532
sock_T fd;
35233533

35243534
for (channel = first_channel; channel != NULL; channel = channel->ch_next)
35253535
{
3536+
if (only_keep_open && !channel->ch_keep_open)
3537+
continue;
3538+
35263539
/* check the socket and pipes */
35273540
for (part = PART_SOCK; part < PART_IN; ++part)
35283541
{

src/gui_gtk_x11.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6643,6 +6643,12 @@ gui_mch_wait_for_chars(long wtime)
66436643
focus = gui.in_focus;
66446644
}
66456645

6646+
# if defined(FEAT_JOB_CHANNEL)
6647+
/* Using an event handler for a channel that may be disconnected does
6648+
* not work, it hangs. Instead poll for messages. */
6649+
channel_handle_events(TRUE);
6650+
# endif
6651+
66466652
#ifdef MESSAGE_QUEUE
66476653
# ifdef FEAT_TIMERS
66486654
did_add_timer = FALSE;

src/misc2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6321,7 +6321,7 @@ parse_queued_messages(void)
63216321
{
63226322
/* For Win32 mch_breakcheck() does not check for input, do it here. */
63236323
# if defined(WIN32) && defined(FEAT_JOB_CHANNEL)
6324-
channel_handle_events();
6324+
channel_handle_events(FALSE);
63256325
# endif
63266326

63276327
# ifdef FEAT_NETBEANS_INTG

src/os_unix.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5466,7 +5466,7 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
54665466
job->jv_channel = channel; /* ch_refcount was set above */
54675467

54685468
if (pty_master_fd >= 0)
5469-
close(pty_slave_fd); /* duped above */
5469+
close(pty_slave_fd); /* not used in the parent */
54705470
/* close child stdin, stdout and stderr */
54715471
if (!use_file_for_in && fd_in[0] >= 0)
54725472
close(fd_in[0]);
@@ -5669,6 +5669,29 @@ mch_clear_job(job_T *job)
56695669
}
56705670
#endif
56715671

5672+
#if defined(FEAT_TERMINAL) || defined(PROTO)
5673+
int
5674+
mch_create_pty_channel(job_T *job, jobopt_T *options)
5675+
{
5676+
int pty_master_fd = -1;
5677+
int pty_slave_fd = -1;
5678+
channel_T *channel;
5679+
5680+
open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name);
5681+
close(pty_slave_fd);
5682+
5683+
channel = add_channel();
5684+
if (channel == NULL)
5685+
return FAIL;
5686+
job->jv_channel = channel; /* ch_refcount was set by add_channel() */
5687+
channel->ch_keep_open = TRUE;
5688+
5689+
channel_set_pipes(channel, pty_master_fd, pty_master_fd, pty_master_fd);
5690+
channel_set_job(channel, job, options);
5691+
return OK;
5692+
}
5693+
#endif
5694+
56725695
/*
56735696
* Check for CTRL-C typed by reading all available characters.
56745697
* In cooked mode we should get SIGINT, no need to check.

src/proto/channel.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ void channel_free_all(void);
3434
char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout);
3535
void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
3636
channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp);
37-
void channel_handle_events(void);
37+
void channel_handle_events(int only_keep_open);
3838
void channel_set_nonblock(channel_T *channel, ch_part_T part);
39-
int channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, char *fun);
39+
int channel_send(channel_T *channel, ch_part_T part, char_u *buf_arg, int len_arg, char *fun);
4040
void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
4141
void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
4242
int channel_poll_setup(int nfd_in, void *fds_in);

src/proto/os_unix.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ char *mch_job_status(job_T *job);
6666
job_T *mch_detect_ended_job(job_T *job_list);
6767
int mch_signal_job(job_T *job, char_u *how);
6868
void mch_clear_job(job_T *job);
69+
int mch_create_pty_channel(job_T *job, jobopt_T *options);
6970
void mch_breakcheck(int force);
7071
int mch_expandpath(garray_T *gap, char_u *path, int flags);
7172
int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);

src/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,7 @@ struct channel_S {
16561656
char_u *ch_close_cb; /* call when channel is closed */
16571657
partial_T *ch_close_partial;
16581658
int ch_drop_never;
1659+
int ch_keep_open; /* do not close on read error */
16591660

16601661
job_T *ch_job; /* Job that uses this channel; this does not
16611662
* count as a reference to avoid a circular

src/terminal.c

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* in tl_scrollback are no longer used.
3939
*
4040
* TODO:
41+
* - ":term NONE" does not work in MS-Windows.
4142
* - better check for blinking - reply from Thomas Dickey Aug 22
4243
* - test for writing lines to terminal job does not work on MS-Windows
4344
* - implement term_setsize()
@@ -47,6 +48,7 @@
4748
* - do not set bufhidden to "hide"? works like a buffer with changes.
4849
* document that CTRL-W :hide can be used.
4950
* - GUI: when using tabs, focus in terminal, click on tab does not work.
51+
* - When $HOME was set by Vim (MS-Windows), do not pass it to the job.
5052
* - GUI: when 'confirm' is set and trying to exit Vim, dialog offers to save
5153
* changes to "!shell".
5254
* (justrajdeep, 2017 Aug 22)
@@ -62,8 +64,6 @@
6264
* shell writing stderr to a file or buffer
6365
* - For the GUI fill termios with default values, perhaps like pangoterm:
6466
* http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
65-
* - support ":term NONE" to open a terminal with a pty but not running a job
66-
* in it. The pty can be passed to gdb to run the executable in.
6767
* - if the job in the terminal does not support the mouse, we can use the
6868
* mouse in the Terminal window for copy/paste.
6969
* - when 'encoding' is not utf-8, or the job is using another encoding, setup
@@ -163,8 +163,8 @@ static term_T *in_terminal_loop = NULL;
163163
/*
164164
* Functions with separate implementation for MS-Windows and Unix-like systems.
165165
*/
166-
static int term_and_job_init(term_T *term, int rows, int cols,
167-
typval_T *argvar, jobopt_T *opt);
166+
static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt);
167+
static int create_pty_only(term_T *term, jobopt_T *opt);
168168
static void term_report_winsize(term_T *term, int rows, int cols);
169169
static void term_free_vterm(term_T *term);
170170

@@ -256,6 +256,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
256256
win_T *old_curwin = curwin;
257257
term_T *term;
258258
buf_T *old_curbuf = NULL;
259+
int res;
259260

260261
if (check_restricted() || check_secure())
261262
return;
@@ -355,7 +356,13 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
355356
char_u *cmd, *p;
356357

357358
if (argvar->v_type == VAR_STRING)
359+
{
358360
cmd = argvar->vval.v_string;
361+
if (cmd == NULL)
362+
cmd = (char_u *)"";
363+
else if (STRCMP(cmd, "NONE") == 0)
364+
cmd = (char_u *)"pty";
365+
}
359366
else if (argvar->v_type != VAR_LIST
360367
|| argvar->vval.v_list == NULL
361368
|| argvar->vval.v_list->lv_len < 1)
@@ -400,9 +407,15 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
400407
set_term_and_win_size(term);
401408
setup_job_options(opt, term->tl_rows, term->tl_cols);
402409

403-
/* System dependent: setup the vterm and start the job in it. */
404-
if (term_and_job_init(term, term->tl_rows, term->tl_cols, argvar, opt)
405-
== OK)
410+
/* System dependent: setup the vterm and maybe start the job in it. */
411+
if (argvar->v_type == VAR_STRING
412+
&& argvar->vval.v_string != NULL
413+
&& STRCMP(argvar->vval.v_string, "NONE") == 0)
414+
res = create_pty_only(term, opt);
415+
else
416+
res = term_and_job_init(term, argvar, opt);
417+
418+
if (res == OK)
406419
{
407420
/* Get and remember the size we ended up with. Update the pty. */
408421
vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
@@ -553,7 +566,8 @@ free_terminal(buf_T *buf)
553566
if (term->tl_job != NULL)
554567
{
555568
if (term->tl_job->jv_status != JOB_ENDED
556-
&& term->tl_job->jv_status != JOB_FAILED)
569+
&& term->tl_job->jv_status != JOB_FINISHED
570+
&& term->tl_job->jv_status != JOB_FAILED)
557571
job_stop(term->tl_job, NULL, "kill");
558572
job_unref(term->tl_job);
559573
}
@@ -839,8 +853,9 @@ term_job_running(term_T *term)
839853
* race condition when updating the title. */
840854
return term != NULL
841855
&& term->tl_job != NULL
842-
&& term->tl_job->jv_status == JOB_STARTED
843-
&& channel_is_open(term->tl_job->jv_channel);
856+
&& channel_is_open(term->tl_job->jv_channel)
857+
&& (term->tl_job->jv_status == JOB_STARTED
858+
|| term->tl_job->jv_channel->ch_keep_open);
844859
}
845860

846861
/*
@@ -2842,9 +2857,14 @@ f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
28422857
ch_log(NULL, "term_wait(): no job to wait for");
28432858
return;
28442859
}
2860+
if (buf->b_term->tl_job->jv_channel == NULL)
2861+
/* channel is closed, nothing to do */
2862+
return;
28452863

28462864
/* Get the job status, this will detect a job that finished. */
2847-
if (STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
2865+
if ((buf->b_term->tl_job->jv_channel == NULL
2866+
|| !buf->b_term->tl_job->jv_channel->ch_keep_open)
2867+
&& STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
28482868
{
28492869
/* The job is dead, keep reading channel I/O until the channel is
28502870
* closed. */
@@ -2976,8 +2996,6 @@ dyn_winpty_init(int verbose)
29762996
static int
29772997
term_and_job_init(
29782998
term_T *term,
2979-
int rows,
2980-
int cols,
29812999
typval_T *argvar,
29823000
jobopt_T *opt)
29833001
{
@@ -3023,7 +3041,8 @@ term_and_job_init(
30233041
if (term->tl_winpty_config == NULL)
30243042
goto failed;
30253043

3026-
winpty_config_set_initial_size(term->tl_winpty_config, cols, rows);
3044+
winpty_config_set_initial_size(term->tl_winpty_config,
3045+
term->tl_cols, term->tl_rows);
30273046
term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
30283047
if (term->tl_winpty == NULL)
30293048
goto failed;
@@ -3085,7 +3104,7 @@ term_and_job_init(
30853104
winpty_spawn_config_free(spawn_config);
30863105
vim_free(cmd_wchar);
30873106

3088-
create_vterm(term, rows, cols);
3107+
create_vterm(term, term->tl_rows, term->tl_cols);
30893108

30903109
channel_set_job(channel, job, opt);
30913110
job_set_options(job, opt);
@@ -3137,6 +3156,13 @@ term_and_job_init(
31373156
return FAIL;
31383157
}
31393158

3159+
static int
3160+
create_pty_only(term_T *term, jobopt_T *opt)
3161+
{
3162+
/* TODO: implement this */
3163+
return FAIL;
3164+
}
3165+
31403166
/*
31413167
* Free the terminal emulator part of "term".
31423168
*/
@@ -3185,12 +3211,10 @@ terminal_enabled(void)
31853211
static int
31863212
term_and_job_init(
31873213
term_T *term,
3188-
int rows,
3189-
int cols,
31903214
typval_T *argvar,
31913215
jobopt_T *opt)
31923216
{
3193-
create_vterm(term, rows, cols);
3217+
create_vterm(term, term->tl_rows, term->tl_cols);
31943218

31953219
/* TODO: if the command is "NONE" only create a pty. */
31963220
term->tl_job = job_start(argvar, opt);
@@ -3202,6 +3226,26 @@ term_and_job_init(
32023226
&& term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
32033227
}
32043228

3229+
static int
3230+
create_pty_only(term_T *term, jobopt_T *opt)
3231+
{
3232+
int ret;
3233+
3234+
create_vterm(term, term->tl_rows, term->tl_cols);
3235+
3236+
term->tl_job = job_alloc();
3237+
if (term->tl_job == NULL)
3238+
return FAIL;
3239+
++term->tl_job->jv_refcount;
3240+
3241+
/* behave like the job is already finished */
3242+
term->tl_job->jv_status = JOB_FINISHED;
3243+
3244+
ret = mch_create_pty_channel(term->tl_job, opt);
3245+
3246+
return ret;
3247+
}
3248+
32053249
/*
32063250
* Free the terminal emulator part of "term".
32073251
*/

src/testdir/test_terminal.vim

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,3 +505,23 @@ func Test_terminal_write_stdin()
505505

506506
bwipe!
507507
endfunc
508+
509+
func Test_terminal_no_cmd()
510+
" Todo: make this work on all systems.
511+
if !has('unix')
512+
return
513+
endif
514+
" Todo: make this work in the GUI
515+
if !has('gui_running')
516+
return
517+
endif
518+
let buf = term_start('NONE', {})
519+
call assert_notequal(0, buf)
520+
521+
let pty = job_info(term_getjob(buf))['tty']
522+
call assert_notequal('', pty)
523+
call system('echo "look here" > ' . pty)
524+
call term_wait(buf)
525+
call assert_equal('look here', term_getline(buf, 1))
526+
bwipe!
527+
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,8 @@ static char *(features[]) =
769769

770770
static int included_patches[] =
771771
{ /* Add new patch number below this line */
772+
/**/
773+
1000,
772774
/**/
773775
999,
774776
/**/

0 commit comments

Comments
 (0)