Patch 8.0.1000
Problem:    Cannot open a terminal without running a job in it.
Solution:   Make ":terminal NONE" open a terminal with a pty.
Files:      src/terminal.c, src/os_unix.c, src/proto/os_unix.pro,
            src/channel.c, src/proto/channel.pro, src/structs.h,
            src/testdir/test_terminal.vim, src/misc2.c, src/gui_gtk_x11.c


*** ../vim-8.0.0999/src/terminal.c      2017-08-24 21:48:22.536643534 +0200
--- src/terminal.c      2017-08-26 20:35:19.554845869 +0200
***************
*** 38,43 ****
--- 38,44 ----
   * in tl_scrollback are no longer used.
   *
   * TODO:
+  * - ":term NONE" does not work in MS-Windows.
   * - better check for blinking - reply from Thomas Dickey Aug 22
   * - test for writing lines to terminal job does not work on MS-Windows
   * - implement term_setsize()
***************
*** 47,52 ****
--- 48,54 ----
   * - do not set bufhidden to "hide"?  works like a buffer with changes.
   *   document that CTRL-W :hide can be used.
   * - GUI: when using tabs, focus in terminal, click on tab does not work.
+  * - When $HOME was set by Vim (MS-Windows), do not pass it to the job.
   * - GUI: when 'confirm' is set and trying to exit Vim, dialog offers to save
   *   changes to "!shell".
   *   (justrajdeep, 2017 Aug 22)
***************
*** 62,69 ****
   *       shell writing stderr to a file or buffer
   * - For the GUI fill termios with default values, perhaps like pangoterm:
   *   
http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
-  * - support ":term NONE" to open a terminal with a pty but not running a job
-  *   in it.  The pty can be passed to gdb to run the executable in.
   * - if the job in the terminal does not support the mouse, we can use the
   *   mouse in the Terminal window for copy/paste.
   * - when 'encoding' is not utf-8, or the job is using another encoding, setup
--- 64,69 ----
***************
*** 163,170 ****
  /*
   * Functions with separate implementation for MS-Windows and Unix-like 
systems.
   */
! static int term_and_job_init(term_T *term, int rows, int cols,
!                                             typval_T *argvar, jobopt_T *opt);
  static void term_report_winsize(term_T *term, int rows, int cols);
  static void term_free_vterm(term_T *term);
  
--- 163,170 ----
  /*
   * Functions with separate implementation for MS-Windows and Unix-like 
systems.
   */
! static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt);
! static int create_pty_only(term_T *term, jobopt_T *opt);
  static void term_report_winsize(term_T *term, int rows, int cols);
  static void term_free_vterm(term_T *term);
  
***************
*** 256,261 ****
--- 256,262 ----
      win_T     *old_curwin = curwin;
      term_T    *term;
      buf_T     *old_curbuf = NULL;
+     int               res;
  
      if (check_restricted() || check_secure())
        return;
***************
*** 355,361 ****
--- 356,368 ----
        char_u  *cmd, *p;
  
        if (argvar->v_type == VAR_STRING)
+       {
            cmd = argvar->vval.v_string;
+           if (cmd == NULL)
+               cmd = (char_u *)"";
+           else if (STRCMP(cmd, "NONE") == 0)
+               cmd = (char_u *)"pty";
+       }
        else if (argvar->v_type != VAR_LIST
                || argvar->vval.v_list == NULL
                || argvar->vval.v_list->lv_len < 1)
***************
*** 400,408 ****
      set_term_and_win_size(term);
      setup_job_options(opt, term->tl_rows, term->tl_cols);
  
!     /* System dependent: setup the vterm and start the job in it. */
!     if (term_and_job_init(term, term->tl_rows, term->tl_cols, argvar, opt)
!                                                                        == OK)
      {
        /* Get and remember the size we ended up with.  Update the pty. */
        vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
--- 407,421 ----
      set_term_and_win_size(term);
      setup_job_options(opt, term->tl_rows, term->tl_cols);
  
!     /* System dependent: setup the vterm and maybe start the job in it. */
!     if (argvar->v_type == VAR_STRING
!           && argvar->vval.v_string != NULL
!           && STRCMP(argvar->vval.v_string, "NONE") == 0)
!       res = create_pty_only(term, opt);
!     else
!       res = term_and_job_init(term, argvar, opt);
! 
!     if (res == OK)
      {
        /* Get and remember the size we ended up with.  Update the pty. */
        vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
***************
*** 553,559 ****
      if (term->tl_job != NULL)
      {
        if (term->tl_job->jv_status != JOB_ENDED
!                                     && term->tl_job->jv_status != JOB_FAILED)
            job_stop(term->tl_job, NULL, "kill");
        job_unref(term->tl_job);
      }
--- 566,573 ----
      if (term->tl_job != NULL)
      {
        if (term->tl_job->jv_status != JOB_ENDED
!               && term->tl_job->jv_status != JOB_FINISHED
!               && term->tl_job->jv_status != JOB_FAILED)
            job_stop(term->tl_job, NULL, "kill");
        job_unref(term->tl_job);
      }
***************
*** 839,846 ****
       * race condition when updating the title. */
      return term != NULL
        && term->tl_job != NULL
!       && term->tl_job->jv_status == JOB_STARTED
!       && channel_is_open(term->tl_job->jv_channel);
  }
  
  /*
--- 853,861 ----
       * race condition when updating the title. */
      return term != NULL
        && term->tl_job != NULL
!       && channel_is_open(term->tl_job->jv_channel)
!       && (term->tl_job->jv_status == JOB_STARTED
!               || term->tl_job->jv_channel->ch_keep_open);
  }
  
  /*
***************
*** 2842,2850 ****
        ch_log(NULL, "term_wait(): no job to wait for");
        return;
      }
  
      /* Get the job status, this will detect a job that finished. */
!     if (STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
      {
        /* The job is dead, keep reading channel I/O until the channel is
         * closed. */
--- 2857,2870 ----
        ch_log(NULL, "term_wait(): no job to wait for");
        return;
      }
+     if (buf->b_term->tl_job->jv_channel == NULL)
+       /* channel is closed, nothing to do */
+       return;
  
      /* Get the job status, this will detect a job that finished. */
!     if ((buf->b_term->tl_job->jv_channel == NULL
!                            || !buf->b_term->tl_job->jv_channel->ch_keep_open)
!           && STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
      {
        /* The job is dead, keep reading channel I/O until the channel is
         * closed. */
***************
*** 2976,2983 ****
      static int
  term_and_job_init(
        term_T      *term,
-       int         rows,
-       int         cols,
        typval_T    *argvar,
        jobopt_T    *opt)
  {
--- 2996,3001 ----
***************
*** 3023,3029 ****
      if (term->tl_winpty_config == NULL)
        goto failed;
  
!     winpty_config_set_initial_size(term->tl_winpty_config, cols, rows);
      term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
      if (term->tl_winpty == NULL)
        goto failed;
--- 3041,3048 ----
      if (term->tl_winpty_config == NULL)
        goto failed;
  
!     winpty_config_set_initial_size(term->tl_winpty_config,
!                                                term->tl_cols, term->tl_rows);
      term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
      if (term->tl_winpty == NULL)
        goto failed;
***************
*** 3085,3091 ****
      winpty_spawn_config_free(spawn_config);
      vim_free(cmd_wchar);
  
!     create_vterm(term, rows, cols);
  
      channel_set_job(channel, job, opt);
      job_set_options(job, opt);
--- 3104,3110 ----
      winpty_spawn_config_free(spawn_config);
      vim_free(cmd_wchar);
  
!     create_vterm(term, term->tl_rows, term->tl_cols);
  
      channel_set_job(channel, job, opt);
      job_set_options(job, opt);
***************
*** 3137,3142 ****
--- 3156,3168 ----
      return FAIL;
  }
  
+     static int
+ create_pty_only(term_T *term, jobopt_T *opt)
+ {
+     /* TODO: implement this */
+     return FAIL;
+ }
+ 
  /*
   * Free the terminal emulator part of "term".
   */
***************
*** 3185,3196 ****
      static int
  term_and_job_init(
        term_T      *term,
-       int         rows,
-       int         cols,
        typval_T    *argvar,
        jobopt_T    *opt)
  {
!     create_vterm(term, rows, cols);
  
      /* TODO: if the command is "NONE" only create a pty. */
      term->tl_job = job_start(argvar, opt);
--- 3211,3220 ----
      static int
  term_and_job_init(
        term_T      *term,
        typval_T    *argvar,
        jobopt_T    *opt)
  {
!     create_vterm(term, term->tl_rows, term->tl_cols);
  
      /* TODO: if the command is "NONE" only create a pty. */
      term->tl_job = job_start(argvar, opt);
***************
*** 3202,3207 ****
--- 3226,3251 ----
        && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
  }
  
+     static int
+ create_pty_only(term_T *term, jobopt_T *opt)
+ {
+     int ret;
+ 
+     create_vterm(term, term->tl_rows, term->tl_cols);
+ 
+     term->tl_job = job_alloc();
+     if (term->tl_job == NULL)
+       return FAIL;
+     ++term->tl_job->jv_refcount;
+ 
+     /* behave like the job is already finished */
+     term->tl_job->jv_status = JOB_FINISHED;
+ 
+     ret = mch_create_pty_channel(term->tl_job, opt);
+ 
+     return ret;
+ }
+ 
  /*
   * Free the terminal emulator part of "term".
   */
*** ../vim-8.0.0999/src/os_unix.c       2017-08-21 21:07:24.478671491 +0200
--- src/os_unix.c       2017-08-26 19:57:33.001144732 +0200
***************
*** 5466,5472 ****
      job->jv_channel = channel;  /* ch_refcount was set above */
  
      if (pty_master_fd >= 0)
!       close(pty_slave_fd); /* duped above */
      /* close child stdin, stdout and stderr */
      if (!use_file_for_in && fd_in[0] >= 0)
        close(fd_in[0]);
--- 5466,5472 ----
      job->jv_channel = channel;  /* ch_refcount was set above */
  
      if (pty_master_fd >= 0)
!       close(pty_slave_fd); /* not used in the parent */
      /* close child stdin, stdout and stderr */
      if (!use_file_for_in && fd_in[0] >= 0)
        close(fd_in[0]);
***************
*** 5669,5674 ****
--- 5669,5697 ----
  }
  #endif
  
+ #if defined(FEAT_TERMINAL) || defined(PROTO)
+     int
+ mch_create_pty_channel(job_T *job, jobopt_T *options)
+ {
+     int               pty_master_fd = -1;
+     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();
+     if (channel == NULL)
+       return FAIL;
+     job->jv_channel = channel;  /* ch_refcount was set by add_channel() */
+     channel->ch_keep_open = TRUE;
+ 
+     channel_set_pipes(channel, pty_master_fd, pty_master_fd, pty_master_fd);
+     channel_set_job(channel, job, options);
+     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-8.0.0999/src/proto/os_unix.pro       2017-08-13 20:06:14.963846989 
+0200
--- src/proto/os_unix.pro       2017-08-26 18:08:40.619342952 +0200
***************
*** 66,71 ****
--- 66,72 ----
  job_T *mch_detect_ended_job(job_T *job_list);
  int mch_signal_job(job_T *job, char_u *how);
  void mch_clear_job(job_T *job);
+ int mch_create_pty_channel(job_T *job, jobopt_T *options);
  void mch_breakcheck(int force);
  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-8.0.0999/src/channel.c       2017-08-19 21:26:40.081756056 +0200
--- src/channel.c       2017-08-26 22:00:19.042412608 +0200
***************
*** 503,508 ****
--- 503,512 ----
      if (!CH_HAS_GUI)
        return;
  
+     /* gets stuck in handling events for a not connected channel */
+     if (channel->ch_keep_open)
+       return;
+ 
  # ifdef FEAT_GUI_X11
      /* Tell notifier we are interested in being called
       * when there is input on the editor connection socket. */
***************
*** 548,556 ****
  {
      if (channel->CH_SOCK_FD != INVALID_FD)
        channel_gui_register_one(channel, PART_SOCK);
!     if (channel->CH_OUT_FD != INVALID_FD)
        channel_gui_register_one(channel, PART_OUT);
!     if (channel->CH_ERR_FD != INVALID_FD)
        channel_gui_register_one(channel, PART_ERR);
  }
  
--- 552,563 ----
  {
      if (channel->CH_SOCK_FD != INVALID_FD)
        channel_gui_register_one(channel, PART_SOCK);
!     if (channel->CH_OUT_FD != INVALID_FD
!           && channel->CH_OUT_FD != channel->CH_SOCK_FD)
        channel_gui_register_one(channel, PART_OUT);
!     if (channel->CH_ERR_FD != INVALID_FD
!           && channel->CH_ERR_FD != channel->CH_SOCK_FD
!           && channel->CH_ERR_FD != channel->CH_OUT_FD)
        channel_gui_register_one(channel, PART_ERR);
  }
  
***************
*** 3247,3257 ****
  
      /* Reading a disconnection (readlen == 0), or an error. */
      if (readlen <= 0)
!       ch_close_part_on_error(channel, part, (len < 0), func);
! 
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
!     /* signal the main loop that there is something to read */
!     if (CH_HAS_GUI && gtk_main_level() > 0)
        gtk_main_quit();
  #endif
  }
--- 3254,3266 ----
  
      /* Reading a disconnection (readlen == 0), or an error. */
      if (readlen <= 0)
!     {
!       if (!channel->ch_keep_open)
!           ch_close_part_on_error(channel, part, (len < 0), func);
!     }
  #if defined(CH_HAS_GUI) && defined(FEAT_GUI_GTK)
!     else if (CH_HAS_GUI && gtk_main_level() > 0)
!       /* signal the main loop that there is something to read */
        gtk_main_quit();
  #endif
  }
***************
*** 3509,3521 ****
  }
  # endif
  
! # if defined(WIN32) || defined(PROTO)
  /*
   * Check the channels for anything that is ready to be read.
   * The data is put in the read queue.
   */
      void
! channel_handle_events(void)
  {
      channel_T *channel;
      ch_part_T part;
--- 3518,3531 ----
  }
  # endif
  
! # if defined(WIN32) || defined(FEAT_GUI) || defined(PROTO)
  /*
   * Check the channels for anything that is ready to be read.
   * The data is put in the read queue.
+  * if "only_keep_open" is TRUE only check channels where ch_keep_open is set.
   */
      void
! channel_handle_events(int only_keep_open)
  {
      channel_T *channel;
      ch_part_T part;
***************
*** 3523,3528 ****
--- 3533,3541 ----
  
      for (channel = first_channel; channel != NULL; channel = channel->ch_next)
      {
+       if (only_keep_open && !channel->ch_keep_open)
+           continue;
+ 
        /* check the socket and pipes */
        for (part = PART_SOCK; part < PART_IN; ++part)
        {
*** ../vim-8.0.0999/src/proto/channel.pro       2017-08-18 20:50:26.441516959 
+0200
--- src/proto/channel.pro       2017-08-26 20:29:32.725031621 +0200
***************
*** 34,42 ****
  char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout);
  void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
  channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp);
! void channel_handle_events(void);
  void channel_set_nonblock(channel_T *channel, ch_part_T part);
! int channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, 
char *fun);
  void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
  void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
  int channel_poll_setup(int nfd_in, void *fds_in);
--- 34,42 ----
  char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout);
  void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
  channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp);
! void channel_handle_events(int only_keep_open);
  void channel_set_nonblock(channel_T *channel, ch_part_T part);
! int channel_send(channel_T *channel, ch_part_T part, char_u *buf_arg, int 
len_arg, char *fun);
  void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
  void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
  int channel_poll_setup(int nfd_in, void *fds_in);
*** ../vim-8.0.0999/src/structs.h       2017-08-18 20:50:26.441516959 +0200
--- src/structs.h       2017-08-26 18:27:54.471843502 +0200
***************
*** 1656,1661 ****
--- 1656,1662 ----
      char_u    *ch_close_cb;   /* call when channel is closed */
      partial_T *ch_close_partial;
      int               ch_drop_never;
+     int               ch_keep_open;   /* do not close on read error */
  
      job_T     *ch_job;        /* Job that uses this channel; this does not
                                 * count as a reference to avoid a circular
*** ../vim-8.0.0999/src/testdir/test_terminal.vim       2017-08-25 
23:22:01.689645666 +0200
--- src/testdir/test_terminal.vim       2017-08-26 20:03:16.958970560 +0200
***************
*** 505,507 ****
--- 505,527 ----
  
    bwipe!
  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
+   endif
+   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
*** ../vim-8.0.0999/src/misc2.c 2017-08-19 15:05:16.048003367 +0200
--- src/misc2.c 2017-08-26 20:30:31.316662527 +0200
***************
*** 6321,6327 ****
  {
      /* For Win32 mch_breakcheck() does not check for input, do it here. */
  # if defined(WIN32) && defined(FEAT_JOB_CHANNEL)
!     channel_handle_events();
  # endif
  
  # ifdef FEAT_NETBEANS_INTG
--- 6321,6327 ----
  {
      /* For Win32 mch_breakcheck() does not check for input, do it here. */
  # if defined(WIN32) && defined(FEAT_JOB_CHANNEL)
!     channel_handle_events(FALSE);
  # endif
  
  # ifdef FEAT_NETBEANS_INTG
*** ../vim-8.0.0999/src/gui_gtk_x11.c   2017-07-23 16:45:05.669761183 +0200
--- src/gui_gtk_x11.c   2017-08-26 20:32:13.764017011 +0200
***************
*** 6643,6648 ****
--- 6643,6654 ----
            focus = gui.in_focus;
        }
  
+ # if defined(FEAT_JOB_CHANNEL)
+       /* Using an event handler for a channel that may be disconnected does
+        * not work, it hangs.  Instead poll for messages. */
+       channel_handle_events(TRUE);
+ # endif
+ 
  #ifdef MESSAGE_QUEUE
  # ifdef FEAT_TIMERS
        did_add_timer = FALSE;
*** ../vim-8.0.0999/src/version.c       2017-08-26 17:48:57.582995164 +0200
--- src/version.c       2017-08-26 18:43:27.801784838 +0200
***************
*** 771,772 ****
--- 771,774 ----
  {   /* Add new patch number below this line */
+ /**/
+     1000,
  /**/

-- 
I have a drinking problem -- I don't have a drink!

 /// Bram Moolenaar -- [email protected] -- 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    ///

-- 
-- 
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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui