]> granicus.if.org Git - vim/commitdiff
patch 8.1.0870: Vim doesn't use the new ConPTY support in Windows 10 v8.1.0870
authorBram Moolenaar <Bram@vim.org>
Sun, 3 Feb 2019 13:53:10 +0000 (14:53 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 3 Feb 2019 13:53:10 +0000 (14:53 +0100)
Problem:    Vim doesn't use the new ConPTY support in Windows 10.
Solution:   Use ConPTY support, if available. (Nobuhiro Takasaki, closes #3794)

17 files changed:
runtime/doc/eval.txt
runtime/doc/options.txt
runtime/doc/terminal.txt
src/channel.c
src/evalfunc.c
src/globals.h
src/option.c
src/option.h
src/os_win32.c
src/proto/terminal.pro
src/structs.h
src/terminal.c
src/testdir/gen_opt_test.vim
src/testdir/test_autocmd.vim
src/testdir/test_mksession.vim
src/testdir/test_terminal.vim
src/version.c

index d888d9c491e5f0ce92276f2c041badfd4a27a581..cce17b9cf54d8e85eb557e8d7ed9f19a4fc2c43b 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 8.1.  Last change: 2019 Jan 29
+*eval.txt*     For Vim version 8.1.  Last change: 2019 Feb 03
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -696,7 +696,7 @@ similar to -1. >
        :let otherblob = myblob[:]      " make a copy of the Blob
 
 If the first index is beyond the last byte of the Blob or the second index is
-before the first index, the result is an empty list.  There is no error
+before the first index, the result is an empty Blob.  There is no error
 message.
 
 If the second index is equal to or greater than the length of the list the
@@ -9469,6 +9469,10 @@ term_start({cmd}, {options})                             *term_start()*
                   "ansi_colors"     A list of 16 color names or hex codes
                                     defining the ANSI palette used in GUI
                                     color modes.  See |g:terminal_ansi_colors|.
+                  "term_mode"       (MS-Windows only): Specify which pty to
+                                    use:
+                                       "winpty": Use winpty
+                                       "conpty": Use ConPTY (if available)
 
                {only available when compiled with the |+terminal| feature}
 
@@ -10186,6 +10190,7 @@ cmdline_hist            Compiled with |cmdline-history| support.
 cmdline_info           Compiled with 'showcmd' and 'ruler' support.
 comments               Compiled with |'comments'| support.
 compatible             Compiled to be very Vi compatible.
+conpty                 Platform where |ConPTY| can be used.
 cryptv                 Compiled with encryption support |encryption|.
 cscope                 Compiled with |cscope| support.
 cursorbind             Compiled with |cursorbind| (always true)
index 04c38b204174200544265ac60a2f0fd270f77deb..e73a9dae804f2089d1dae0892aa460aa52ce618c 100644 (file)
@@ -8054,6 +8054,23 @@ A jump table for the options with a short description can be found at |Q_op|.
        Note that the "cterm" attributes are still used, not the "gui" ones.
        NOTE: This option is reset when 'compatible' is set.
 
+                                               *'termmode'* *'tmod'*
+'termmode' 'tmod'      string  (default "")
+                       local to window
+                       {not in Vi, MS-Windows only}
+       Whether the window uses winpty or |ConPTY| as the virtual console.
+       When set before opening the terminal, it influences what pty is used.
+       When opening the terminal it will be set to the actually used pty.
+
+       Possible values are:
+           ""          use ConPTY if possible, winpty otherwise
+           "winpty"    use winpty, fail if not supported
+           "conpty"    use |ConPTY|, fail if not supported
+
+       |ConPTY| support depends on the platform (Windows 10 October 2018
+       edition).  winpty support needs to be installed.  If neither is
+       supported then you cannot open a terminal window.
+
                                                *'termwinscroll'* *'twsl'*
 'termwinscroll' 'twsl' number  (default 10000)
                        local to buffer
index 0db69df7d3012f21c74d8cbe711c31c236f8db91..3b78b550e2e15f7849b6ce456ad9daade7b19436 100644 (file)
@@ -228,7 +228,8 @@ Syntax ~
                                        for Python "++eof=exit()".  Special
                                        codes can be used like with `:map`,
                                        e.g. "<C-Z>" for CTRL-Z.
-
+                       ++winpty        Use winpty as the virtual console.
+                       ++conpty        Use |ConPTY| as the virtual console.
                        If you want to use more options use the |term_start()|
                        function.
                        If you want to split the window vertically, use: >
@@ -410,6 +411,13 @@ Just put the files somewhere in your PATH.  You can set the 'winptydll' option
 to point to the right file, if needed.  If you have both the 32-bit and 64-bit
 version, rename to winpty32.dll and winpty64.dll to match the way Vim was
 build.
+                                                       *ConPTY*
+On more recent versions of MS-Windows 10 (beginning with the "October 2018
+Update"), winpty is no longer required. On those versions, |:terminal| will use
+Windows' built-in support for hosting terminal applications, "ConPTY".  When
+ConPTY is in use, there may be rendering artifacts regarding ambiguous-width
+characters. If you encounter any such issues, set 'termmode' to winpty (which
+you then must have instlled).
 
 Environment variables are used to pass information to the running job:
     VIM_SERVERNAME     v:servername
index 20cf46238c0771873923f5f6d0bff4ed27bf3dbe..484d0139b16562f52516afe5d97d0ee91c9c4f69 100644 (file)
@@ -1720,11 +1720,7 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
     char_u  *res;
     char_u  *p;
 
-    /* If there is only one buffer just get that one. */
-    if (head->rq_next == NULL || head->rq_next->rq_next == NULL)
-       return channel_get(channel, part, outlen);
-
-    /* Concatenate everything into one buffer. */
+    // Concatenate everything into one buffer.
     for (node = head->rq_next; node != NULL; node = node->rq_next)
        len += node->rq_buflen;
     res = lalloc(len + 1, TRUE);
@@ -1738,7 +1734,7 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
     }
     *p = NUL;
 
-    /* Free all buffers */
+    // Free all buffers
     do
     {
        p = channel_get(channel, part, NULL);
@@ -1747,16 +1743,37 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
 
     if (outlen != NULL)
     {
+       // Returning the length, keep NUL characters.
        *outlen += len;
        return res;
     }
 
-    /* turn all NUL into NL */
-    while (len > 0)
+    // Turn all NUL into NL, so that the result can be used as a string.
+    p = res;
+    while (p < res + len)
     {
-       --len;
-       if (res[len] == NUL)
-           res[len] = NL;
+       if (*p == NUL)
+           *p = NL;
+#ifdef WIN32
+       else if (*p == 0x1b)
+       {
+           // crush the escape sequence OSC 0/1/2: ESC ]0;
+           if (p + 3 < res + len
+                   && p[1] == ']'
+                   && (p[2] == '0' || p[2] == '1' || p[2] == '2')
+                   && p[3] == ';')
+           {
+               // '\a' becomes a NL
+               while (p < res + (len - 1) && *p != '\a')
+                   ++p;
+               // BEL is zero width characters, suppress display mistake
+               // ConPTY (after 10.0.18317) requires advance checking
+               if (p[-1] == NUL)
+                   p[-1] = 0x07;
+           }
+       }
+#endif
+       ++p;
     }
 
     return res;
@@ -4330,7 +4347,7 @@ channel_parse_messages(void)
            channel = first_channel;
            continue;
        }
-       if (channel->ch_to_be_freed)
+       if (channel->ch_to_be_freed || channel->ch_killing)
        {
            channel_free(channel);
            /* channel has been freed, start over */
@@ -4930,6 +4947,28 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
                opt->jo_set2 |= JO2_TERM_KILL;
                opt->jo_term_kill = tv_get_string_chk(item);
            }
+           else if (STRCMP(hi->hi_key, "term_mode") == 0)
+           {
+               char_u *p;
+
+               if (!(supported2 & JO2_TERM_MODE))
+                   break;
+               opt->jo_set2 |= JO2_TERM_MODE;
+               p = tv_get_string_chk(item);
+               if (p == NULL)
+               {
+                   semsg(_(e_invargval), "term_mode");
+                   return FAIL;
+               }
+               // Allow empty string, "winpty", "conpty".
+               if (!(*p == NUL || STRCMP(p, "winpty") == 0
+                                                 || STRCMP(p, "conpty") == 0))
+               {
+                   semsg(_(e_invargval), "term_mode");
+                   return FAIL;
+               }
+               opt->jo_term_mode = p[0];
+           }
 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
            else if (STRCMP(hi->hi_key, "ansi_colors") == 0)
            {
@@ -5440,6 +5479,16 @@ job_cleanup(job_T *job)
        channel_need_redraw = TRUE;
     }
 
+    if (job->jv_channel != NULL
+        && job->jv_channel->ch_anonymous_pipe && !job->jv_channel->ch_killing)
+    {
+       ++safe_to_invoke_callback;
+       channel_free_contents(job->jv_channel);
+       job->jv_channel->ch_job = NULL;
+       job->jv_channel = NULL;
+       --safe_to_invoke_callback;
+    }
+
     // Do not free the job in case the close callback of the associated channel
     // isn't invoked yet and may get information by job_info().
     if (job->jv_refcount == 0 && !job_channel_still_useful(job))
index 374e70132fcdd5ce191650ca57c7fed4e1fda89b..fa7ed9bab1c4eed1f480dd8a411ded03cf42e093 100644 (file)
@@ -6737,6 +6737,10 @@ f_has(typval_T *argvars, typval_T *rettv)
 #if defined(FEAT_TERMINAL) && defined(WIN3264)
        else if (STRICMP(name, "terminal") == 0)
            n = terminal_enabled();
+#endif
+#if defined(FEAT_TERMINAL) && defined(WIN3264)
+       else if (STRICMP(name, "conpty") == 0)
+           n = use_conpty();
 #endif
     }
 
index 0562610fd0bcd25e0555c5d7f1abdd772120d8de..6cc3be21583835ba9c9daa2f300bfdb04fc77224 100644 (file)
@@ -1432,7 +1432,8 @@ EXTERN char e_fsync[]             INIT(= N_("E667: Fsync failed"));
        || defined(DYNAMIC_ICONV) \
        || defined(DYNAMIC_GETTEXT) \
        || defined(DYNAMIC_MZSCHEME) \
-       || defined(DYNAMIC_LUA)
+       || defined(DYNAMIC_LUA) \
+       || defined(FEAT_TERMINAL)
 EXTERN char e_loadlib[]        INIT(= N_("E370: Could not load library %s"));
 EXTERN char e_loadfunc[]       INIT(= N_("E448: Could not load library function %s"));
 #endif
index 6d2bab1e02dec7b03cc8a84db5b5267c981e3046..77d1024ecddb41d0dcd8b46ddd56b5d1e3f56f09 100644 (file)
 # define PV_TWK                OPT_WIN(WV_TWK)
 # define PV_TWS                OPT_WIN(WV_TWS)
 # define PV_TWSL       OPT_BUF(BV_TWSL)
+# define PV_TMOD       OPT_WIN(WV_TMOD)
 #endif
 #ifdef FEAT_SIGNS
 # define PV_SCL                OPT_WIN(WV_SCL)
@@ -2698,6 +2699,15 @@ static struct vimoption options[] =
 #else
                            (char_u*)NULL, PV_NONE,
                            {(char_u *)FALSE, (char_u *)FALSE}
+#endif
+                           SCTX_INIT},
+    {"termmode", "tmod",    P_STRING|P_ALLOCED|P_VI_DEF,
+#ifdef FEAT_TERMINAL
+                           (char_u *)VAR_WIN, PV_TMOD,
+                           {(char_u *)"", (char_u *)NULL}
+#else
+                           (char_u *)NULL, PV_NONE,
+                           {(char_u *)NULL, (char_u *)0L}
 #endif
                            SCTX_INIT},
     {"termwinkey", "twk",   P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
@@ -3208,6 +3218,9 @@ static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "noins
 #ifdef FEAT_SIGNS
 static char *(p_scl_values[]) = {"yes", "no", "auto", NULL};
 #endif
+#ifdef FEAT_TERMINAL
+static char *(p_tmod_values[]) = {"winpty", "conpty", "", NULL};
+#endif
 
 static void set_options_default(int opt_flags);
 static void set_string_default_esc(char *name, char_u *val, int escape);
@@ -3661,7 +3674,12 @@ set_init_1(int clean_arg)
            {
                char    buf[50];
 
-               sprintf(buf, "cp%ld", (long)GetConsoleCP());
+               /* Win32 console: In ConPTY, GetConsoleCP() returns zero.
+                * Use an alternative value. */
+               if (GetConsoleCP() == 0)
+                   sprintf(buf, "cp%ld", (long)GetACP());
+               else
+                   sprintf(buf, "cp%ld", (long)GetConsoleCP());
                p_tenc = vim_strsave((char_u *)buf);
                if (p_tenc != NULL)
                {
@@ -7468,14 +7486,14 @@ did_set_string_option(
 #endif
 
 #ifdef FEAT_TERMINAL
-    /* 'termwinkey' */
+    // 'termwinkey'
     else if (varp == &curwin->w_p_twk)
     {
        if (*curwin->w_p_twk != NUL
                                  && string_to_key(curwin->w_p_twk, TRUE) == 0)
            errmsg = e_invarg;
     }
-    /* 'termwinsize' */
+    // 'termwinsize'
     else if (varp == &curwin->w_p_tws)
     {
        if (*curwin->w_p_tws != NUL)
@@ -7487,6 +7505,12 @@ did_set_string_option(
                errmsg = e_invarg;
        }
     }
+    // 'termmode'
+    else if (varp == &curwin->w_p_tmod)
+    {
+       if (check_opt_strings(*varp, p_tmod_values, FALSE) != OK)
+           errmsg = e_invarg;
+    }
 #endif
 
 #ifdef FEAT_VARTABS
@@ -8838,7 +8862,7 @@ set_bool_option(
        if (!has_vtp_working())
        {
            p_tgc = 0;
-           return (char_u*)N_("E954: 24-bit colors are not supported on this environment");
+           return N_("E954: 24-bit colors are not supported on this environment");
        }
        if (is_term_win32())
            swap_tcap();
@@ -10928,6 +10952,7 @@ get_varp(struct vimoption *p)
        case PV_TWK:    return (char_u *)&(curwin->w_p_twk);
        case PV_TWS:    return (char_u *)&(curwin->w_p_tws);
        case PV_TWSL:   return (char_u *)&(curbuf->b_p_twsl);
+       case PV_TMOD:   return (char_u *)&(curwin->w_p_tmod);
 #endif
 
        case PV_AI:     return (char_u *)&(curbuf->b_p_ai);
@@ -11128,6 +11153,7 @@ copy_winopt(winopt_T *from, winopt_T *to)
 #ifdef FEAT_TERMINAL
     to->wo_twk = vim_strsave(from->wo_twk);
     to->wo_tws = vim_strsave(from->wo_tws);
+    to->wo_tmod = vim_strsave(from->wo_tmod);
 #endif
 #ifdef FEAT_FOLDING
     to->wo_fdc = from->wo_fdc;
@@ -11198,6 +11224,7 @@ check_winopt(winopt_T *wop UNUSED)
 #ifdef FEAT_TERMINAL
     check_string_option(&wop->wo_twk);
     check_string_option(&wop->wo_tws);
+    check_string_option(&wop->wo_tmod);
 #endif
 #ifdef FEAT_LINEBREAK
     check_string_option(&wop->wo_briopt);
@@ -11241,6 +11268,7 @@ clear_winopt(winopt_T *wop UNUSED)
 #ifdef FEAT_TERMINAL
     clear_string_option(&wop->wo_twk);
     clear_string_option(&wop->wo_tws);
+    clear_string_option(&wop->wo_tmod);
 #endif
 }
 
index 90c05080bd18e026a5544ed9f90ca6d4dae444da..2985781e65fc22e59f400e3ee26ae238f1669ebc 100644 (file)
@@ -1112,6 +1112,7 @@ enum
 #ifdef FEAT_TERMINAL
     , WV_TWK
     , WV_TWS
+    , WV_TMOD
 #endif
     , WV_CRBIND
 #ifdef FEAT_LINEBREAK
index 6a127a44a3815be77a06e14bcb0b97a4284f835d..10ca41881e085b596a575cf9e229ae5ce0f0d05a 100644 (file)
@@ -186,8 +186,10 @@ static int win32_getattrs(char_u *name);
 static int win32_setattrs(char_u *name, int attrs);
 static int win32_set_archive(char_u *name);
 
-#ifndef FEAT_GUI_W32
 static int vtp_working = 0;
+static void vtp_flag_init();
+
+#ifndef FEAT_GUI_W32
 static void vtp_init();
 static void vtp_exit();
 static int vtp_printf(char *format, ...);
@@ -247,6 +249,7 @@ static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx;
 typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
 static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx;
 static BOOL has_csbiex = FALSE;
+#endif
 
 /*
  * Get version number including build number
@@ -276,7 +279,7 @@ get_build_number(void)
     return ver;
 }
 
-
+#ifndef FEAT_GUI_W32
 /*
  * Version of ReadConsoleInput() that works with IME.
  * Works around problems on Windows 8.
@@ -1508,9 +1511,8 @@ WaitForChar(long msec, int ignore_input)
        /* Wait forever. */
        dwEndTime = INFINITE;
 
-    /* We need to loop until the end of the time period, because
-     * we might get multiple unusable mouse events in that time.
-     */
+    // We need to loop until the end of the time period, because
+    // we might get multiple unusable mouse events in that time.
     for (;;)
     {
        // Only process messages when waiting.
@@ -2175,6 +2177,8 @@ mch_init(void)
 #ifdef FEAT_CLIPBOARD
     win_clip_init();
 #endif
+
+    vtp_flag_init();
 }
 
 
@@ -2675,6 +2679,7 @@ mch_init(void)
     win_clip_init();
 #endif
 
+    vtp_flag_init();
     vtp_init();
 }
 
@@ -5683,7 +5688,11 @@ mch_signal_job(job_T *job, char_u *how)
     {
        /* deadly signal */
        if (job->jv_job_object != NULL)
+       {
+           if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
+               job->jv_channel->ch_killing = TRUE;
            return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
+       }
        return terminate_all(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
     }
 
@@ -7621,31 +7630,53 @@ mch_setenv(char *var, char *value, int x)
     return 0;
 }
 
-#ifndef FEAT_GUI_W32
-
 /*
  * Support for 256 colors and 24-bit colors was added in Windows 10
  * version 1703 (Creators update).
  */
-# define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
+#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
+
+/*
+ * Support for pseudo-console (ConPTY) was added in windows 10
+ * version 1809 (October 2018 update).
+ */
+#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
+
+    static void
+vtp_flag_init(void)
+{
+    DWORD   ver = get_build_number();
+#ifndef FEAT_GUI_W32
+    DWORD   mode;
+    HANDLE  out;
+
+    out = GetStdHandle(STD_OUTPUT_HANDLE);
+
+    vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
+    GetConsoleMode(out, &mode);
+    mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+    if (SetConsoleMode(out, mode) == 0)
+       vtp_working = 0;
+#endif
+
+#ifdef FEAT_GUI_W32
+    if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
+       vtp_working = 1;
+#endif
+
+}
+
+#ifndef FEAT_GUI_W32
 
     static void
 vtp_init(void)
 {
-    DWORD   ver, mode;
     HMODULE hKerneldll;
     DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
 # ifdef FEAT_TERMGUICOLORS
     COLORREF fg, bg;
 # endif
 
-    ver = get_build_number();
-    vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
-    GetConsoleMode(g_hConOut, &mode);
-    mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
-    if (SetConsoleMode(g_hConOut, mode) == 0)
-       vtp_working = 0;
-
     /* Use functions supported from Vista */
     hKerneldll = GetModuleHandle("kernel32.dll");
     if (hKerneldll != NULL)
@@ -7828,12 +7859,6 @@ control_console_color_rgb(void)
        reset_console_color_rgb();
 }
 
-    int
-has_vtp_working(void)
-{
-    return vtp_working;
-}
-
     int
 use_vtp(void)
 {
@@ -7847,3 +7872,9 @@ is_term_win32(void)
 }
 
 #endif
+
+    int
+has_vtp_working(void)
+{
+    return vtp_working;
+}
index e6914c0ba0cdfcec3ce5601f06ff127fd077e5ec..0fa62b71090d7cd0899c4ac3b094c4cd3305efbd 100644 (file)
@@ -57,4 +57,6 @@ void f_term_wait(typval_T *argvars, typval_T *rettv);
 void term_send_eof(channel_T *ch);
 job_T *term_getjob(term_T *term);
 int terminal_enabled(void);
+void term_free_conpty(term_T *term);
+int use_conpty(void);
 /* vim: set ft=c : */
index e6cc8291f18ffd775e66dc9c88ca951771431c56..5d0541bc83bb291959c2b1c67f932b67c97e4287 100644 (file)
@@ -282,6 +282,8 @@ typedef struct
 # define w_p_twk w_onebuf_opt.wo_twk   /* 'termwinkey' */
     char_u     *wo_tws;
 # define w_p_tws w_onebuf_opt.wo_tws   /* 'termwinsize' */
+    char_u     *wo_tmod;
+# define w_p_tmod w_onebuf_opt.wo_tmod /* 'termmode' */
 #endif
 
 #ifdef FEAT_EVAL
@@ -1728,13 +1730,15 @@ struct channel_S {
     int                ch_keep_open;   /* do not close on read error */
     int                ch_nonblock;
 
-    job_T      *ch_job;        /* Job that uses this channel; this does not
-                                * count as a reference to avoid a circular
-                                * reference, the job refers to the channel. */
-    int                ch_job_killed;  /* TRUE when there was a job and it was killed
-                                * or we know it died. */
+    job_T      *ch_job;        // Job that uses this channel; this does not
+                               // count as a reference to avoid a circular
+                               // reference, the job refers to the channel.
+    int                ch_job_killed;  // TRUE when there was a job and it was killed
+                               // or we know it died.
+    int                ch_anonymous_pipe;  // ConPTY
+    int                ch_killing;         // TerminateJobObject() was called
 
-    int                ch_refcount;    /* reference count */
+    int                ch_refcount;    // reference count
     int                ch_copyID;
 };
 
@@ -1787,6 +1791,7 @@ struct channel_S {
 #define JO2_NORESTORE      0x2000      /* "norestore" */
 #define JO2_TERM_KILL      0x4000      /* "term_kill" */
 #define JO2_ANSI_COLORS            0x8000      /* "ansi_colors" */
+#define JO2_TERM_MODE      0x10000     /* "term_mode" */
 
 #define JO_MODE_ALL    (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
 #define JO_CB_ALL \
@@ -1859,6 +1864,7 @@ typedef struct
 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
     long_u     jo_ansi_colors[16];
 # endif
+    int                jo_term_mode;       // first character of "term_mode"
 #endif
 } jobopt_T;
 
index 760591443842c196712643907339addc5aeb6b1a..41cd5c952b4598acefdc5d85f68283d380a5529c 100644 (file)
@@ -65,6 +65,23 @@ typedef struct sb_line_S {
     cellattr_T sb_fill_attr;   /* for short line */
 } sb_line_T;
 
+#ifdef WIN3264
+# ifndef HPCON
+#  define HPCON VOID*
+# endif
+# ifndef EXTENDED_STARTUPINFO_PRESENT
+#  define EXTENDED_STARTUPINFO_PRESENT 0x00080000
+# endif
+# ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#  define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+# endif
+typedef struct _DYN_STARTUPINFOEXW
+{
+    STARTUPINFOW StartupInfo;
+    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
+} DYN_STARTUPINFOEXW, *PDYN_STARTUPINFOEXW;
+#endif
+
 /* typedef term_T in structs.h */
 struct terminal_S {
     term_T     *tl_next;
@@ -92,10 +109,15 @@ struct terminal_S {
     char_u     *tl_opencmd;
     char_u     *tl_eof_chars;
 
+    char_u     *tl_arg0_cmd;   // To format the status bar
+
 #ifdef WIN3264
     void       *tl_winpty_config;
     void       *tl_winpty;
 
+    HPCON      tl_conpty;
+    DYN_STARTUPINFOEXW tl_siex;        // Structure that always needs to be hold
+
     FILE       *tl_out_fd;
 #endif
 #if defined(FEAT_SESSION)
@@ -147,6 +169,11 @@ static term_T *first_term = NULL;
 /* Terminal active in terminal_loop(). */
 static term_T *in_terminal_loop = NULL;
 
+#ifdef WIN3264
+static BOOL has_winpty = FALSE;
+static BOOL has_conpty = FALSE;
+#endif
+
 #define MAX_ROW 999999     /* used for tl_dirty_row_end to update all rows */
 #define KEY_BUF_LEN 200
 
@@ -715,6 +742,16 @@ ex_terminal(exarg_T *eap)
            vim_free(buf);
            *p = ' ';
        }
+       else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "winpty", 6) == 0)
+       {
+           opt.jo_set2 |= JO2_TERM_MODE;
+           opt.jo_term_mode = 'w';
+       }
+       else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "conpty", 6) == 0)
+       {
+           opt.jo_set2 |= JO2_TERM_MODE;
+           opt.jo_term_mode = 'c';
+       }
        else
        {
            if (*p)
@@ -771,6 +808,11 @@ term_write_session(FILE *fd, win_T *wp)
     if (fprintf(fd, "terminal ++curwin ++cols=%d ++rows=%d ",
                term->tl_cols, term->tl_rows) < 0)
        return FAIL;
+#ifdef WIN3264
+    if (*wp->w_p_tmod != NUL)
+       if (fprintf(fd, "++%s ", wp->w_p_tmod) < 0)
+           return FAIL;
+#endif
     if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0)
        return FAIL;
 
@@ -871,6 +913,7 @@ free_unused_terminals()
        vim_free(term->tl_status_text);
        vim_free(term->tl_opencmd);
        vim_free(term->tl_eof_chars);
+       vim_free(term->tl_arg0_cmd);
 #ifdef WIN3264
        if (term->tl_out_fd != NULL)
            fclose(term->tl_out_fd);
@@ -2639,10 +2682,18 @@ handle_settermprop(
     {
        case VTERM_PROP_TITLE:
            vim_free(term->tl_title);
-           /* a blank title isn't useful, make it empty, so that "running" is
-            * displayed */
+           // a blank title isn't useful, make it empty, so that "running" is
+           // displayed
            if (*skipwhite((char_u *)value->string) == NUL)
                term->tl_title = NULL;
+           // Same as blank
+           else if (term->tl_arg0_cmd != NULL
+                   && STRNCMP(term->tl_arg0_cmd, (char_u *)value->string,
+                                         (int)STRLEN(term->tl_arg0_cmd)) == 0)
+               term->tl_title = NULL;
+           // Empty corrupted data of winpty
+           else if (STRNCMP("  - ", (char_u *)value->string, 4) == 0)
+               term->tl_title = NULL;
 #ifdef WIN3264
            else if (!enc_utf8 && enc_codepage > 0)
            {
@@ -5318,7 +5369,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
                    + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
                    + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
                    + JO2_NORESTORE + JO2_TERM_KILL
-                   + JO2_ANSI_COLORS) == FAIL)
+                   + JO2_ANSI_COLORS + JO2_TERM_MODE) == FAIL)
        return;
 
     buf = term_start(&argvars[0], NULL, &opt, 0);
@@ -5426,6 +5477,327 @@ term_getjob(term_T *term)
  * 2. MS-Windows implementation.
  */
 
+HRESULT (WINAPI *pCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON*);
+HRESULT (WINAPI *pResizePseudoConsole)(HPCON, COORD);
+HRESULT (WINAPI *pClosePseudoConsole)(HPCON);
+BOOL (*pInitializeProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T);
+BOOL (*pUpdateProcThreadAttribute)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T);
+void (*pDeleteProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST);
+
+    static int
+dyn_conpty_init(int verbose)
+{
+    static BOOL        handled = FALSE;
+    static int result;
+    HMODULE    hKerneldll;
+    int                i;
+    static struct
+    {
+       char    *name;
+       FARPROC *ptr;
+    } conpty_entry[] =
+    {
+       {"CreatePseudoConsole", (FARPROC*)&pCreatePseudoConsole},
+       {"ResizePseudoConsole", (FARPROC*)&pResizePseudoConsole},
+       {"ClosePseudoConsole", (FARPROC*)&pClosePseudoConsole},
+       {"InitializeProcThreadAttributeList",
+                               (FARPROC*)&pInitializeProcThreadAttributeList},
+       {"UpdateProcThreadAttribute",
+                               (FARPROC*)&pUpdateProcThreadAttribute},
+       {"DeleteProcThreadAttributeList",
+                               (FARPROC*)&pDeleteProcThreadAttributeList},
+       {NULL, NULL}
+    };
+
+    if (handled)
+       return result;
+
+    if (!has_vtp_working())
+    {
+       handled = TRUE;
+       result = FAIL;
+       return FAIL;
+    }
+
+    hKerneldll = vimLoadLib("kernel32.dll");
+    for (i = 0; conpty_entry[i].name != NULL
+                                       && conpty_entry[i].ptr != NULL; ++i)
+    {
+       if ((*conpty_entry[i].ptr = (FARPROC)GetProcAddress(hKerneldll,
+                                               conpty_entry[i].name)) == NULL)
+       {
+           if (verbose)
+               semsg(_(e_loadfunc), conpty_entry[i].name);
+           return FAIL;
+       }
+    }
+
+    handled = TRUE;
+    result = OK;
+    return OK;
+}
+
+    static int
+conpty_term_and_job_init(
+       term_T      *term,
+       typval_T    *argvar,
+       char        **argv,
+       jobopt_T    *opt,
+       jobopt_T    *orig_opt)
+{
+    WCHAR          *cmd_wchar = NULL;
+    WCHAR          *cmd_wchar_copy = NULL;
+    WCHAR          *cwd_wchar = NULL;
+    WCHAR          *env_wchar = NULL;
+    channel_T      *channel = NULL;
+    job_T          *job = NULL;
+    HANDLE         jo = NULL;
+    garray_T       ga_cmd, ga_env;
+    char_u         *cmd = NULL;
+    HRESULT        hr;
+    COORD          consize;
+    SIZE_T         breq;
+    PROCESS_INFORMATION proc_info;
+    HANDLE         i_theirs = NULL;
+    HANDLE         o_theirs = NULL;
+    HANDLE         i_ours = NULL;
+    HANDLE         o_ours = NULL;
+
+    ga_init2(&ga_cmd, (int)sizeof(char*), 20);
+    ga_init2(&ga_env, (int)sizeof(char*), 20);
+
+    if (argvar->v_type == VAR_STRING)
+    {
+       cmd = argvar->vval.v_string;
+    }
+    else if (argvar->v_type == VAR_LIST)
+    {
+       if (win32_build_cmd(argvar->vval.v_list, &ga_cmd) == FAIL)
+           goto failed;
+       cmd = ga_cmd.ga_data;
+    }
+    if (cmd == NULL || *cmd == NUL)
+    {
+       emsg(_(e_invarg));
+       goto failed;
+    }
+
+    term->tl_arg0_cmd = vim_strsave(cmd);
+
+    cmd_wchar = enc_to_utf16(cmd, NULL);
+
+    if (cmd_wchar != NULL)
+    {
+       /* Request by CreateProcessW */
+       breq = wcslen(cmd_wchar) + 1 + 1;       /* Addition of NUL by API */
+       cmd_wchar_copy = (PWSTR)alloc((int)(breq * sizeof(WCHAR)));
+       wcsncpy(cmd_wchar_copy, cmd_wchar, breq - 1);
+    }
+
+    ga_clear(&ga_cmd);
+    if (cmd_wchar == NULL)
+       goto failed;
+    if (opt->jo_cwd != NULL)
+       cwd_wchar = enc_to_utf16(opt->jo_cwd, NULL);
+
+    win32_build_env(opt->jo_env, &ga_env, TRUE);
+    env_wchar = ga_env.ga_data;
+
+    if (!CreatePipe(&i_theirs, &i_ours, NULL, 0))
+       goto failed;
+    if (!CreatePipe(&o_ours, &o_theirs, NULL, 0))
+       goto failed;
+
+    consize.X = term->tl_cols;
+    consize.Y = term->tl_rows;
+    hr = pCreatePseudoConsole(consize, i_theirs, o_theirs, 0,
+                                                            &term->tl_conpty);
+    if (FAILED(hr))
+       goto failed;
+
+    term->tl_siex.StartupInfo.cb = sizeof(term->tl_siex);
+
+    /* Set up pipe inheritance safely: Vista or later. */
+    pInitializeProcThreadAttributeList(NULL, 1, 0, &breq);
+    term->tl_siex.lpAttributeList =
+           (PPROC_THREAD_ATTRIBUTE_LIST)alloc((int)breq);
+    if (!term->tl_siex.lpAttributeList)
+       goto failed;
+    if (!pInitializeProcThreadAttributeList(term->tl_siex.lpAttributeList, 1,
+                                                                    0, &breq))
+       goto failed;
+    if (!pUpdateProcThreadAttribute(
+           term->tl_siex.lpAttributeList, 0,
+           PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, term->tl_conpty,
+           sizeof(HPCON), NULL, NULL))
+       goto failed;
+
+    channel = add_channel();
+    if (channel == NULL)
+       goto failed;
+
+    job = job_alloc();
+    if (job == NULL)
+       goto failed;
+    if (argvar->v_type == VAR_STRING)
+    {
+       int argc;
+
+       build_argv_from_string(cmd, &job->jv_argv, &argc);
+    }
+    else
+    {
+       int argc;
+
+       build_argv_from_list(argvar->vval.v_list, &job->jv_argv, &argc);
+    }
+
+    if (opt->jo_set & JO_IN_BUF)
+       job->jv_in_buf = buflist_findnr(opt->jo_io_buf[PART_IN]);
+
+    if (!CreateProcessW(NULL, cmd_wchar_copy, NULL, NULL, FALSE,
+           EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT
+           | CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP
+           | CREATE_DEFAULT_ERROR_MODE,
+           env_wchar, cwd_wchar,
+           &term->tl_siex.StartupInfo, &proc_info))
+       goto failed;
+
+    CloseHandle(i_theirs);
+    CloseHandle(o_theirs);
+
+    channel_set_pipes(channel,
+           (sock_T)i_ours,
+           (sock_T)o_ours,
+           (sock_T)o_ours);
+
+    /* Write lines with CR instead of NL. */
+    channel->ch_write_text_mode = TRUE;
+
+    /* Use to explicitly delete anonymous pipe handle. */
+    channel->ch_anonymous_pipe = TRUE;
+
+    jo = CreateJobObject(NULL, NULL);
+    if (jo == NULL)
+       goto failed;
+
+    if (!AssignProcessToJobObject(jo, proc_info.hProcess))
+    {
+       /* Failed, switch the way to terminate process with TerminateProcess. */
+       CloseHandle(jo);
+       jo = NULL;
+    }
+
+    ResumeThread(proc_info.hThread);
+    CloseHandle(proc_info.hThread);
+
+    vim_free(cmd_wchar);
+    vim_free(cmd_wchar_copy);
+    vim_free(cwd_wchar);
+    vim_free(env_wchar);
+
+    if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL)
+       goto failed;
+
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+    if (opt->jo_set2 & JO2_ANSI_COLORS)
+       set_vterm_palette(term->tl_vterm, opt->jo_ansi_colors);
+    else
+       init_vterm_ansi_colors(term->tl_vterm);
+#endif
+
+    channel_set_job(channel, job, opt);
+    job_set_options(job, opt);
+
+    job->jv_channel = channel;
+    job->jv_proc_info = proc_info;
+    job->jv_job_object = jo;
+    job->jv_status = JOB_STARTED;
+    ++job->jv_refcount;
+    term->tl_job = job;
+
+    /* Redirecting stdout and stderr doesn't work at the job level.  Instead
+     * open the file here and handle it in.  opt->jo_io was changed in
+     * setup_job_options(), use the original flags here. */
+    if (orig_opt->jo_io[PART_OUT] == JIO_FILE)
+    {
+       char_u *fname = opt->jo_io_name[PART_OUT];
+
+       ch_log(channel, "Opening output file %s", fname);
+       term->tl_out_fd = mch_fopen((char *)fname, WRITEBIN);
+       if (term->tl_out_fd == NULL)
+           semsg(_(e_notopen), fname);
+    }
+
+    return OK;
+
+failed:
+    ga_clear(&ga_cmd);
+    ga_clear(&ga_env);
+    vim_free(cmd_wchar);
+    vim_free(cmd_wchar_copy);
+    vim_free(cwd_wchar);
+    if (channel != NULL)
+       channel_clear(channel);
+    if (job != NULL)
+    {
+       job->jv_channel = NULL;
+       job_cleanup(job);
+    }
+    term->tl_job = NULL;
+    if (jo != NULL)
+       CloseHandle(jo);
+
+    if (term->tl_siex.lpAttributeList != NULL)
+    {
+       pDeleteProcThreadAttributeList(term->tl_siex.lpAttributeList);
+       vim_free(term->tl_siex.lpAttributeList);
+    }
+    term->tl_siex.lpAttributeList = NULL;
+    if (o_theirs != NULL)
+       CloseHandle(o_theirs);
+    if (o_ours != NULL)
+       CloseHandle(o_ours);
+    if (i_ours != NULL)
+       CloseHandle(i_ours);
+    if (i_theirs != NULL)
+       CloseHandle(i_theirs);
+    if (term->tl_conpty != NULL)
+       pClosePseudoConsole(term->tl_conpty);
+    term->tl_conpty = NULL;
+    return FAIL;
+}
+
+    static void
+conpty_term_report_winsize(term_T *term, int rows, int cols)
+{
+    COORD consize;
+
+    consize.X = cols;
+    consize.Y = rows;
+    pResizePseudoConsole(term->tl_conpty, consize);
+}
+
+    void
+term_free_conpty(term_T *term)
+{
+    if (term->tl_siex.lpAttributeList != NULL)
+    {
+       pDeleteProcThreadAttributeList(term->tl_siex.lpAttributeList);
+       vim_free(term->tl_siex.lpAttributeList);
+    }
+    term->tl_siex.lpAttributeList = NULL;
+    if (term->tl_conpty != NULL)
+       pClosePseudoConsole(term->tl_conpty);
+    term->tl_conpty = NULL;
+}
+
+    int
+use_conpty(void)
+{
+    return has_conpty;
+}
+
 #  ifndef PROTO
 
 #define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
@@ -5516,16 +5888,11 @@ dyn_winpty_init(int verbose)
     return OK;
 }
 
-/*
- * Create a new terminal of "rows" by "cols" cells.
- * Store a reference in "term".
- * Return OK or FAIL.
- */
     static int
-term_and_job_init(
+winpty_term_and_job_init(
        term_T      *term,
        typval_T    *argvar,
-       char        **argv UNUSED,
+       char        **argv,
        jobopt_T    *opt,
        jobopt_T    *orig_opt)
 {
@@ -5543,8 +5910,6 @@ term_and_job_init(
     garray_T       ga_cmd, ga_env;
     char_u         *cmd = NULL;
 
-    if (dyn_winpty_init(TRUE) == FAIL)
-       return FAIL;
     ga_init2(&ga_cmd, (int)sizeof(char*), 20);
     ga_init2(&ga_env, (int)sizeof(char*), 20);
 
@@ -5564,6 +5929,8 @@ term_and_job_init(
        goto failed;
     }
 
+    term->tl_arg0_cmd = vim_strsave(cmd);
+
     cmd_wchar = enc_to_utf16(cmd, NULL);
     ga_clear(&ga_cmd);
     if (cmd_wchar == NULL)
@@ -5676,9 +6043,9 @@ term_and_job_init(
     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);
+           (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);
+           (short_u *)winpty_conout_name(term->tl_winpty), NULL);
     ++job->jv_refcount;
     term->tl_job = job;
 
@@ -5722,7 +6089,7 @@ failed:
     term->tl_winpty_config = NULL;
     if (winpty_err != NULL)
     {
-       char_u *msg = utf16_to_enc(
+       char *msg = (char *)utf16_to_enc(
                                (short_u *)winpty_error_msg(winpty_err), NULL);
 
        emsg(msg);
@@ -5731,6 +6098,76 @@ failed:
     return FAIL;
 }
 
+/*
+ * Create a new terminal of "rows" by "cols" cells.
+ * Store a reference in "term".
+ * Return OK or FAIL.
+ */
+    static int
+term_and_job_init(
+       term_T      *term,
+       typval_T    *argvar,
+       char        **argv UNUSED,
+       jobopt_T    *opt,
+       jobopt_T    *orig_opt)
+{
+    int                    use_winpty = FALSE;
+    int                    use_conpty = FALSE;
+
+    has_winpty = dyn_winpty_init(FALSE) != FAIL ? TRUE : FALSE;
+    has_conpty = dyn_conpty_init(FALSE) != FAIL ? TRUE : FALSE;
+
+    if (!has_winpty && !has_conpty)
+       // If neither is available give the errors for winpty, since when
+       // conpty is not available it can't be installed either.
+       return dyn_winpty_init(TRUE);
+
+    if (opt->jo_term_mode == 'w')
+       set_string_option_direct((char_u *)"tmod", -1, (char_u *)"winpty",
+                                                       OPT_FREE|OPT_LOCAL, 0);
+    if (opt->jo_term_mode == 'c')
+       set_string_option_direct((char_u *)"tmod", -1, (char_u *)"conpty",
+                                                       OPT_FREE|OPT_LOCAL, 0);
+
+    if (curwin->w_p_tmod == NULL || *curwin->w_p_tmod == NUL)
+    {
+       if (has_conpty)
+           use_conpty = TRUE;
+       else if (has_winpty)
+           use_winpty = TRUE;
+       // else: error
+    }
+    else if (STRICMP(curwin->w_p_tmod, "winpty") == 0)
+    {
+       if (has_winpty)
+           use_winpty = TRUE;
+    }
+    else if (STRICMP(curwin->w_p_tmod, "conpty") == 0)
+    {
+       if (has_conpty)
+           use_conpty = TRUE;
+       else
+           return dyn_conpty_init(TRUE);
+    }
+
+    if (use_conpty)
+    {
+       set_string_option_direct((char_u *)"tmod", -1, (char_u *)"conpty",
+                                                       OPT_FREE|OPT_LOCAL, 0);
+       return conpty_term_and_job_init(term, argvar, argv, opt, orig_opt);
+    }
+
+    if (use_winpty)
+    {
+       set_string_option_direct((char_u *)"tmod", -1, (char_u *)"winpty",
+                                                       OPT_FREE|OPT_LOCAL, 0);
+       return winpty_term_and_job_init(term, argvar, argv, opt, orig_opt);
+    }
+
+    // error
+    return dyn_winpty_init(TRUE);
+}
+
     static int
 create_pty_only(term_T *term, jobopt_T *options)
 {
@@ -5804,6 +6241,7 @@ failed:
     static void
 term_free_vterm(term_T *term)
 {
+    term_free_conpty(term);
     if (term->tl_winpty != NULL)
        winpty_free(term->tl_winpty);
     term->tl_winpty = NULL;
@@ -5821,6 +6259,8 @@ term_free_vterm(term_T *term)
     static void
 term_report_winsize(term_T *term, int rows, int cols)
 {
+    if (term->tl_conpty)
+       conpty_term_report_winsize(term, rows, cols);
     if (term->tl_winpty)
        winpty_set_size(term->tl_winpty, cols, rows, NULL);
 }
@@ -5828,7 +6268,7 @@ term_report_winsize(term_T *term, int rows, int cols)
     int
 terminal_enabled(void)
 {
-    return dyn_winpty_init(FALSE) == OK;
+    return dyn_winpty_init(FALSE) == OK || dyn_conpty_init(FALSE) == OK;
 }
 
 # else
@@ -5852,6 +6292,8 @@ term_and_job_init(
        jobopt_T    *opt,
        jobopt_T    *orig_opt UNUSED)
 {
+    term->tl_arg0_cmd = NULL;
+
     if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL)
        return FAIL;
 
index c0e89cfb0f8aee4f1145b257db67e05197bcd36a..8c75d377eef722727e6a5a553b1cbbb9d0ac6849 100644 (file)
@@ -131,6 +131,7 @@ let test_values = {
       \ 'term': [[], []],
       \ 'termguicolors': [[], []],
       \ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']],
+      \ 'termmode': [['', 'winpty', 'conpty'], ['xxx']],
       \ 'termwinsize': [['', '24x80', '0x80', '32x0', '0x0'], ['xxx', '80', '8ax9', '24x80b']],
       \ 'toolbar': [['', 'icons', 'text'], ['xxx']],
       \ 'toolbariconsize': [['', 'tiny', 'huge'], ['xxx']],
index 9e8a02cb49115fee89f4dd4513f7b3e7bf87cfb7..71df515294e3ef2d0d3fd96259f8f9f5e8097325 100644 (file)
@@ -1397,7 +1397,13 @@ func Test_Changed_FirstTime()
   let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
   call assert_equal('running', term_getstatus(buf))
   " Wait for the ruler (in the status line) to be shown.
-  call WaitForAssert({-> assert_match('\<All$', term_getline(buf, 3))})
+  " In ConPTY, there is additional character which is drawn up to the width of
+  " the screen.
+  if has('conpty')
+    call WaitForAssert({-> assert_match('\<All.*$', term_getline(buf, 3))})
+  else
+    call WaitForAssert({-> assert_match('\<All$', term_getline(buf, 3))})
+  endif
   " It's only adding autocmd, so that no event occurs.
   call term_sendkeys(buf, ":au! TextChanged <buffer> call writefile(['No'], 'Xchanged.txt')\<cr>")
   call term_sendkeys(buf, "\<C-\\>\<C-N>:qa!\<cr>")
index 303842201069cf857f0b22e7796ce47f36d8acff..456cd1e4238ef1a25e27363dd3aeb3c4113c60ab 100644 (file)
@@ -295,7 +295,7 @@ func Test_mksession_terminal_shell()
       call assert_report('unexpected shell line: ' . line)
     endif
   endfor
-  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+\s*$', term_cmd)
+  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+\s*.*$', term_cmd)
 
   call Stop_shell_in_terminal(bufnr('%'))
   call delete('Xtest_mks.out')
@@ -375,7 +375,7 @@ func Test_mksession_terminal_restore_other()
       let term_cmd = line
     endif
   endfor
-  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+ other', term_cmd)
+  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+.*other', term_cmd)
 
   call Stop_shell_in_terminal(bufnr('%'))
   call delete('Xtest_mks.out')
index 698da04982ae068558cebc651de9b078eebf5e5a..8eb43a0cc75c849ac04cc6c4e7a7595c70926b47 100644 (file)
@@ -39,8 +39,11 @@ func Test_terminal_basic()
     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(''))
+    " ConPTY works on anonymous pipe.
+    if !has('conpty')
+      call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
+      call assert_match('^\\\\.\\pipe\\', term_gettty(''))
+    endif
   endif
   call assert_equal('t', mode())
   call assert_equal('yes', b:done)
@@ -129,7 +132,12 @@ endfunc
 
 func Get_cat_123_cmd()
   if has('win32')
-    return 'cmd /c "cls && color 2 && echo 123"'
+    if !has('conpty')
+      return 'cmd /c "cls && color 2 && echo 123"'
+    else
+      " When clearing twice, extra sequence is not output.
+      return 'cmd /c "cls && cls && color 2 && echo 123"'
+    endif
   else
     call writefile(["\<Esc>[32m123"], 'Xtext')
     return "cat Xtext"
@@ -143,8 +151,8 @@ func Test_terminal_nasty_cb()
 
   call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
   call WaitForAssert({-> assert_equal(0, g:buf)})
-  unlet g:buf
   unlet g:job
+  unlet g:buf
   call delete('Xtext')
 endfunc
 
@@ -563,6 +571,9 @@ func Test_terminal_noblock()
     " The shell or something else has a problem dealing with more than 1000
     " characters at the same time.
     let len = 1000
+  " NPFS is used in Windows, nonblocking mode does not work properly.
+  elseif has('win32')
+    let len = 1
   else
     let len = 5000
   endif
@@ -693,8 +704,11 @@ func Test_terminal_redir_file()
   let cmd = Get_cat_123_cmd()
   let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xfile'})
   call term_wait(buf)
-  call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
-  call assert_match('123', readfile('Xfile')[0])
+  " ConPTY may precede escape sequence. There are things that are not so.
+  if !has('conpty')
+    call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
+    call assert_match('123', readfile('Xfile')[0])
+  endif
   let g:job = term_getjob(buf)
   call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
   call delete('Xfile')
@@ -1661,6 +1675,10 @@ func Test_terminal_hidden_and_close()
 endfunc
 
 func Test_terminal_does_not_truncate_last_newlines()
+  " This test does not pass through ConPTY.
+  if has('conpty')
+    return
+  endif
   let contents = [
   \   [ 'One', '', 'X' ],
   \   [ 'Two', '', '' ],
index 6da6bea94430ea7d8a11042430200a6747e16393..3e0dc7e9f3ec1fdbd54084e1e2d74f02c4c27cbb 100644 (file)
@@ -783,6 +783,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    870,
 /**/
     869,
 /**/