diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 1f23deb785..c7c3f856a6 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 12 +*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -102,10 +102,9 @@ Syntax ~ parentheses. E.g. if "gdb" exists the second terminal buffer will use "!gdb (1)". - If [range] is given it is used for the terminal size. - One number specifies the number of rows. Unless the - "vertical" modifier is used, then it is the number of - columns. + If [range] is given the specified lines are used as + input for the job. It will not be possible to type + keys in the terminal window. Two comma separated numbers are used as "rows,cols". E.g. `:24,80gdb` opens a terminal with 24 rows and 80 @@ -125,6 +124,10 @@ Syntax ~ cannot be |abandon|ed. ++hidden Open the terminal in a hidden buffer, no window will be used. + ++rows={height} Use {height} for the terminal window + height. + ++cols={width} Use {width} for the terminal window + width. If you want to use more options use the |term_start()| function. diff --git a/src/ex_cmds.h b/src/ex_cmds.h index f1af1ad608..2041525a93 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1490,8 +1490,8 @@ EX(CMD_tearoff, "tearoff", ex_tearoff, NEEDARG|EXTRA|TRLBAR|NOTRLCOM|CMDWIN, ADDR_LINES), EX(CMD_terminal, "terminal", ex_terminal, - RANGE|NOTADR|BANG|FILES|TRLBAR|CMDWIN, - ADDR_OTHER), + RANGE|BANG|FILES|TRLBAR|CMDWIN, + ADDR_LINES), EX(CMD_tfirst, "tfirst", ex_tag, RANGE|NOTADR|BANG|TRLBAR|ZEROR, ADDR_LINES), diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 4ad8fcfb8e..f9d8282a05 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -2051,9 +2051,7 @@ dialog_changed( else { #endif - dialog_msg(buff, _("Save changes to \"%s\"?"), - (buf->b_fname != NULL) ? - buf->b_fname : (char_u *)_("Untitled")); + dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname); if (checkall) ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1); else diff --git a/src/option.c b/src/option.c index d94183dc42..134a8ce2e5 100644 --- a/src/option.c +++ b/src/option.c @@ -3254,6 +3254,8 @@ static struct vimoption options[] = p_term("t_AL", T_CAL) p_term("t_al", T_AL) p_term("t_bc", T_BC) + p_term("t_BE", T_BE) + p_term("t_BD", T_BD) p_term("t_cd", T_CD) p_term("t_ce", T_CE) p_term("t_cl", T_CL) @@ -3270,13 +3272,11 @@ static struct vimoption options[] = p_term("t_db", T_DB) p_term("t_DL", T_CDL) p_term("t_dl", T_DL) + p_term("t_EC", T_CEC) p_term("t_EI", T_CEI) p_term("t_fs", T_FS) + p_term("t_GP", T_CGP) p_term("t_IE", T_CIE) - p_term("t_SC", T_CSC) - p_term("t_EC", T_CEC) - p_term("t_SH", T_CSH) - p_term("t_RS", T_CRS) p_term("t_IS", T_CIS) p_term("t_ke", T_KE) p_term("t_ks", T_KS) @@ -3290,10 +3290,13 @@ static struct vimoption options[] = p_term("t_op", T_OP) p_term("t_RB", T_RBG) p_term("t_RI", T_CRI) + p_term("t_RS", T_CRS) p_term("t_RV", T_CRV) p_term("t_Sb", T_CSB) + p_term("t_SC", T_CSC) p_term("t_se", T_SE) p_term("t_Sf", T_CSF) + p_term("t_SH", T_CSH) p_term("t_SI", T_CSI) p_term("t_so", T_SO) p_term("t_SR", T_CSR) @@ -3308,9 +3311,9 @@ static struct vimoption options[] = p_term("t_vb", T_VB) p_term("t_ve", T_VE) p_term("t_vi", T_VI) + p_term("t_VS", T_CVS) p_term("t_vs", T_VS) p_term("t_WP", T_CWP) - p_term("t_GP", T_CGP) p_term("t_WS", T_CWS) p_term("t_xn", T_XN) p_term("t_xs", T_XS) @@ -3318,8 +3321,6 @@ static struct vimoption options[] = p_term("t_ZR", T_CZR) p_term("t_8f", T_8F) p_term("t_8b", T_8B) - p_term("t_BE", T_BE) - p_term("t_BD", T_BD) /* terminal key codes are not in here */ @@ -4468,8 +4469,6 @@ trigger_optionsset_string( (char_u *)options[opt_idx].fullname, NULL, FALSE, NULL); reset_v_option_vars(); } - vim_free(oldval); - vim_free(newval); } #endif @@ -4935,19 +4934,19 @@ do_set( } else if (opt_idx >= 0) /* string */ { - char_u *save_arg = NULL; - char_u *s = NULL; - char_u *oldval = NULL; /* previous value if *varp */ - char_u *newval; - char_u *origval = NULL; + char_u *save_arg = NULL; + char_u *s = NULL; + char_u *oldval = NULL; /* previous value if *varp */ + char_u *newval; + char_u *origval = NULL; #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) - char_u *saved_origval = NULL; - char_u *saved_newval = NULL; + char_u *saved_origval = NULL; + char_u *saved_newval = NULL; #endif - unsigned newlen; - int comma; - int bs; - int new_value_alloced; /* new string option + unsigned newlen; + int comma; + int bs; + int new_value_alloced; /* new string option was allocated */ /* When using ":set opt=val" for a global option @@ -4960,6 +4959,16 @@ do_set( /* The old value is kept until we are sure that the * new value is valid. */ oldval = *(char_u **)varp; + + /* When setting the local value of a global + * option, the old value may be the global value. */ + if (((int)options[opt_idx].indir & PV_BOTH) + && (opt_flags & OPT_LOCAL)) + origval = *(char_u **)get_varp( + &options[opt_idx]); + else + origval = oldval; + if (nextchar == '&') /* set to default val */ { newval = options[opt_idx].def_val[ @@ -5036,6 +5045,8 @@ do_set( break; } vim_free(oldval); + if (origval == oldval) + origval = *(char_u **)varp; oldval = *(char_u **)varp; } /* @@ -5074,15 +5085,6 @@ do_set( ++arg; } - /* When setting the local value of a global - * option, the old value may be the global value. */ - if (((int)options[opt_idx].indir & PV_BOTH) - && (opt_flags & OPT_LOCAL)) - origval = *(char_u **)get_varp( - &options[opt_idx]); - else - origval = oldval; - /* * Copy the new string into allocated memory. * Can't use set_string_option_direct(), because @@ -5286,7 +5288,9 @@ do_set( new_value_alloced = TRUE; } - /* Set the new value. */ + /* + * Set the new value. + */ *(char_u **)(varp) = newval; #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) @@ -5312,19 +5316,16 @@ do_set( errmsg = did_set_string_option(opt_idx, (char_u **)varp, new_value_alloced, oldval, errbuf, opt_flags); +#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) + if (errmsg == NULL) + trigger_optionsset_string(opt_idx, opt_flags, + saved_origval, saved_newval); + vim_free(saved_origval); + vim_free(saved_newval); +#endif /* If error detected, print the error message. */ if (errmsg != NULL) - { -#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) - vim_free(saved_origval); - vim_free(saved_newval); -#endif goto skip; - } -#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) - trigger_optionsset_string(opt_idx, opt_flags, - saved_origval, saved_newval); -#endif } else /* key code option */ { @@ -6135,8 +6136,11 @@ set_string_option( #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) /* call autocommand after handling side effects */ - trigger_optionsset_string(opt_idx, opt_flags, + if (r == NULL) + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + vim_free(saved_oldval); + vim_free(saved_newval); #endif } return r; diff --git a/src/os_unix.c b/src/os_unix.c index 9d8076aefc..96e362b33e 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4352,6 +4352,7 @@ mch_call_shell( # define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use 127, some shells use that already */ +# define OPEN_NULL_FAILED 123 /* Exit code if /dev/null can't be opened */ char_u *newcmd; pid_t pid; @@ -5260,6 +5261,7 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options) int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE; int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE; int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE; + int use_buffer_for_in = options->jo_io[PART_IN] == JIO_BUFFER; int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT; SIGSET_DECL(curset) @@ -5269,7 +5271,10 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options) /* default is to fail */ job->jv_status = JOB_FAILED; - if (options->jo_pty) + if (options->jo_pty + && (!(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? */ @@ -5285,8 +5290,12 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options) goto failed; } } - else if (!use_null_for_in && pty_master_fd < 0 && pipe(fd_in) < 0) - goto failed; + else + /* When writing buffer lines to the input don't use the pty, so that + * the pipe can be closed when all lines were written. */ + if (!use_null_for_in && (pty_master_fd < 0 || use_buffer_for_in) + && pipe(fd_in) < 0) + goto failed; if (use_file_for_out) { @@ -5383,7 +5392,14 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options) } if (use_null_for_in || use_null_for_out || use_null_for_err) + { null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0); + if (null_fd < 0) + { + perror("opening /dev/null failed"); + _exit(OPEN_NULL_FAILED); + } + } if (pty_slave_fd >= 0) { diff --git a/src/proto/term.pro b/src/proto/term.pro index 63e6448905..392b067334 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -53,7 +53,6 @@ void cursor_on(void); void cursor_off(void); void term_cursor_mode(int forced); void term_cursor_color(char_u *color); -void term_cursor_blink(int blink); void term_cursor_shape(int shape, int blink); void scroll_region_set(win_T *wp, int off); void scroll_region_reset(void); diff --git a/src/term.c b/src/term.c index e6aadc6d0c..969bcea37a 100644 --- a/src/term.c +++ b/src/term.c @@ -828,17 +828,13 @@ static struct builtin_term builtin_termcaps[] = {(int)KS_LE, "\b"}, {(int)KS_VI, IF_EB("\033[?25l", ESC_STR "[?25l")}, {(int)KS_VE, IF_EB("\033[?25h", ESC_STR "[?25h")}, -#if 0 - /* This is currently disabled, because we cannot reliably restore the - * cursor style because of what appears to be an xterm bug. */ - {(int)KS_VE, IF_EB("\033[?25h\033[?12l", ESC_STR "[?25h" ESC_STR "[?12l")}, {(int)KS_VS, IF_EB("\033[?12h", ESC_STR "[?12h")}, + {(int)KS_CVS, IF_EB("\033[?12l", ESC_STR "[?12l")}, # ifdef TERMINFO {(int)KS_CSH, IF_EB("\033[%p1%d q", ESC_STR "[%p1%d q")}, # else {(int)KS_CSH, IF_EB("\033[%d q", ESC_STR "[%d q")}, # endif -#endif {(int)KS_CRS, IF_EB("\033P$q q\033\\", ESC_STR "P$q q" ESC_STR "\\")}, # ifdef TERMINFO {(int)KS_CM, IF_EB("\033[%i%p1%d;%p2%dH", @@ -1591,7 +1587,7 @@ set_termname(char_u *term) {KS_DL, "dl"}, {KS_CDL,"DL"}, {KS_CS, "cs"}, {KS_CL, "cl"}, {KS_CD, "cd"}, {KS_VI, "vi"}, {KS_VE, "ve"}, {KS_MB, "mb"}, - {KS_VS, "vs"}, {KS_ME, "me"}, {KS_MR, "mr"}, + {KS_ME, "me"}, {KS_MR, "mr"}, {KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"}, {KS_CZH,"ZH"}, {KS_CZR,"ZR"}, {KS_UE, "ue"}, {KS_US, "us"}, {KS_UCE, "Ce"}, {KS_UCS, "Cs"}, @@ -1601,6 +1597,7 @@ set_termname(char_u *term) {KS_BC, "bc"}, {KS_CSB,"Sb"}, {KS_CSF,"Sf"}, {KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_LE, "le"}, {KS_ND, "nd"}, {KS_OP, "op"}, {KS_CRV, "RV"}, + {KS_VS, "vs"}, {KS_CVS, "VS"}, {KS_CIS, "IS"}, {KS_CIE, "IE"}, {KS_CSC, "SC"}, {KS_CEC, "EC"}, {KS_TS, "ts"}, {KS_FS, "fs"}, @@ -3671,11 +3668,11 @@ mouse_model_popup(void) void scroll_start(void) { - if (*T_VS != NUL) + if (*T_VS != NUL && *T_CVS != NUL) { out_str(T_VS); - out_str(T_VE); - screen_start(); /* don't know where cursor is now */ + out_str(T_CVS); + screen_start(); /* don't know where cursor is now */ } } @@ -3700,10 +3697,9 @@ cursor_on(void) void cursor_off(void) { - if (full_screen) + if (full_screen && !cursor_is_off) { - if (!cursor_is_off) - out_str(T_VI); /* disable cursor */ + out_str(T_VI); /* disable cursor */ cursor_is_off = TRUE; } } @@ -3772,20 +3768,10 @@ term_cursor_color(char_u *color) out_flush(); } } - - void -term_cursor_blink(int blink) -{ - if (blink) - out_str(T_VS); - else - out_str(T_VE); - out_flush(); -} # endif /* - * "shape" == 1: block, "shape" == 2: underline, "shape" == 3: vertical bar + * "shape": 1 = block, 2 = underline, 3 = vertical bar */ void term_cursor_shape(int shape, int blink) @@ -3795,6 +3781,17 @@ term_cursor_shape(int shape, int blink) OUT_STR(tgoto((char *)T_CSH, 0, shape * 2 - blink)); out_flush(); } + /* When t_SH is not set try setting just the blink state. */ + else if (blink && *T_VS != NUL) + { + out_str(T_VS); + out_flush(); + } + else if (!blink && *T_CVS != NUL) + { + out_str(T_CVS); + out_flush(); + } } #endif @@ -4699,7 +4696,9 @@ check_termcode( * 5 = vertical bar blink, 6 = vertical bar */ number = number == 0 ? 1 : number; initial_cursor_shape = (number + 1) / 2; - initial_cursor_blink = (number & 1) ? TRUE : FALSE; + /* The blink flag is actually inverted, compared to + * the value set with T_SH. */ + initial_cursor_blink = (number & 1) ? FALSE : TRUE; rcm_status = STATUS_GOT; LOG_TR("Received cursor shape response"); diff --git a/src/term.h b/src/term.h index 00e3c96aa2..18c1294afa 100644 --- a/src/term.h +++ b/src/term.h @@ -39,7 +39,8 @@ enum SpecialKey KS_DB, /* text may be scrolled up from down */ KS_VI, /* cursor invisible */ KS_VE, /* cursor visible */ - KS_VS, /* cursor very visible */ + KS_VS, /* cursor very visible (blink) */ + KS_CVS, /* cursor normally visible (no blink) */ KS_CSH, /* cursor shape */ KS_CRS, /* request cursor shape */ KS_ME, /* normal mode */ @@ -131,7 +132,8 @@ extern char_u *(term_strings[]); /* current terminal strings */ #define T_DB (TERM_STR(KS_DB)) /* text may be scrolled up from down */ #define T_VI (TERM_STR(KS_VI)) /* cursor invisible */ #define T_VE (TERM_STR(KS_VE)) /* cursor visible */ -#define T_VS (TERM_STR(KS_VS)) /* cursor very visible */ +#define T_VS (TERM_STR(KS_VS)) /* cursor very visible (blink) */ +#define T_CVS (TERM_STR(KS_CVS)) /* cursor normally visible (no blink) */ #define T_CSH (TERM_STR(KS_CSH)) /* cursor shape */ #define T_CRS (TERM_STR(KS_CRS)) /* request cursor shape */ #define T_ME (TERM_STR(KS_ME)) /* normal mode */ diff --git a/src/terminal.c b/src/terminal.c index e22dec93a9..5ee38108f2 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -38,8 +38,7 @@ * in tl_scrollback are no longer used. * * TODO: - * - help index for winptydll, optwin entry for winptydll - * - make [range]terminal pipe [range] lines to the terminal + * - test for writing lines to terminal job does not work on MS-Windows * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. * - support minimal size when 'termsize' is "rows*cols". @@ -50,8 +49,8 @@ "err_io", "err_name", "err_buf", "err_modifiable", "err_msg" * Check that something is connected to the terminal. * Test: "cat" reading from a file or buffer - * "ls" writing stdout to a file or buffer - * shell writing stderr to a file or buffer + * "ls" writing stdout to a file or buffer + * 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 @@ -448,10 +447,14 @@ ex_terminal(exarg_T *eap) cmd = eap->arg; while (*cmd && *cmd == '+' && *(cmd + 1) == '+') { - char_u *p; + char_u *p, *ep; cmd += 2; p = skiptowhite(cmd); + ep = vim_strchr(cmd, '='); + if (ep != NULL && ep < p) + p = ep; + if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0) opt.jo_term_finish = 'c'; else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0) @@ -460,6 +463,20 @@ ex_terminal(exarg_T *eap) opt.jo_curwin = 1; else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0) opt.jo_hidden = 1; + else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0 + && ep != NULL && isdigit(ep[1])) + { + opt.jo_set2 |= JO2_TERM_ROWS; + opt.jo_term_rows = atoi((char *)ep + 1); + p = skiptowhite(cmd); + } + else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "cols", 4) == 0 + && ep != NULL && isdigit(ep[1])) + { + opt.jo_set2 |= JO2_TERM_COLS; + opt.jo_term_cols = atoi((char *)ep + 1); + p = skiptowhite(cmd); + } else { if (*p) @@ -473,17 +490,14 @@ ex_terminal(exarg_T *eap) /* Make a copy, an autocommand may set 'shell'. */ tofree = cmd = vim_strsave(p_sh); - if (eap->addr_count == 2) + if (eap->addr_count > 0) { - opt.jo_term_rows = eap->line1; - opt.jo_term_cols = eap->line2; - } - else if (eap->addr_count == 1) - { - if (cmdmod.split & WSP_VERT) - opt.jo_term_cols = eap->line2; - else - opt.jo_term_rows = eap->line2; + /* Write lines from current buffer to the job. */ + opt.jo_set |= JO_IN_IO | JO_IN_BUF | JO_IN_TOP | JO_IN_BOT; + opt.jo_io[PART_IN] = JIO_BUFFER; + opt.jo_io_buf[PART_IN] = curbuf->b_fnum; + opt.jo_in_top = eap->line1; + opt.jo_in_bot = eap->line2; } argvar.v_type = VAR_STRING; @@ -836,7 +850,26 @@ add_scrollback_line_to_buffer(term_T *term, char_u *text, int len) int empty = (buf->b_ml.ml_flags & ML_EMPTY); linenr_T lnum = buf->b_ml.ml_line_count; - ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE); +#ifdef _WIN32 + if (!enc_utf8 && enc_codepage > 0) + { + WCHAR *ret = NULL; + int length = 0; + + MultiByteToWideChar_alloc(CP_UTF8, 0, (char*)text, len + 1, + &ret, &length); + if (ret != NULL) + { + WideCharToMultiByte_alloc(enc_codepage, 0, + ret, length, (char **)&text, &len, 0, 0); + vim_free(ret); + ml_append_buf(term->tl_buffer, lnum, text, len, FALSE); + vim_free(text); + } + } + else +#endif + ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE); if (empty) { /* Delete the empty line that was in the empty buffer. */ @@ -927,7 +960,7 @@ move_terminal_to_buffer(term_T *term) int c; for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i) - ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c, + ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c, (char_u *)ga.ga_data + ga.ga_len); } } @@ -1083,6 +1116,29 @@ term_vgetc() return c; } +/* + * Get the part that is connected to the tty. Normally this is PART_IN, but + * when writing buffer lines to the job it can be another. This makes it + * possible to do "1,5term vim -". + */ + static ch_part_T +get_tty_part(term_T *term) +{ +#ifdef UNIX + ch_part_T parts[3] = {PART_IN, PART_OUT, PART_ERR}; + int i; + + for (i = 0; i < 3; ++i) + { + int fd = term->tl_job->jv_channel->ch_part[parts[i]].ch_fd; + + if (isatty(fd)) + return parts[i]; + } +#endif + return PART_IN; +} + /* * Send keys to terminal. * Return FAIL when the key needs to be handled in Normal mode. @@ -1154,8 +1210,8 @@ send_keys_to_term(term_T *term, int c, int typed) len = term_convert_key(term, c, msg); if (len > 0) /* TODO: if FAIL is returned, stop? */ - channel_send(term->tl_job->jv_channel, PART_IN, - (char_u *)msg, (int)len, NULL); + channel_send(term->tl_job->jv_channel, get_tty_part(term), + (char_u *)msg, (int)len, NULL); return OK; } @@ -1276,8 +1332,6 @@ may_set_cursor_props(term_T *term) term_cursor_color(term->tl_cursor_color); else term_cursor_color((char_u *)""); - /* do both blink and shape+blink, in case setting shape does not work */ - term_cursor_blink(term->tl_cursor_blink); term_cursor_shape(term->tl_cursor_shape, term->tl_cursor_blink); } } @@ -1293,7 +1347,6 @@ may_restore_cursor_props(void) { did_change_cursor = FALSE; term_cursor_color((char_u *)""); - term_cursor_blink(FALSE); /* this will restore the initial cursor style, if possible */ ui_cursor_shape_forced(TRUE); } @@ -1341,7 +1394,8 @@ terminal_loop(void) #ifdef UNIX { - int fd = curbuf->b_term->tl_job->jv_channel->ch_part[PART_IN].ch_fd; + int part = get_tty_part(curbuf->b_term); + int fd = curbuf->b_term->tl_job->jv_channel->ch_part[part].ch_fd; if (isatty(fd)) { @@ -1438,6 +1492,18 @@ terminal_loop(void) goto theend; } } +# ifdef _WIN32 + if (!enc_utf8 && has_mbyte && c >= 0x80) + { + WCHAR wc; + char_u mb[3]; + + mb[0] = (unsigned)c >> 8; + mb[1] = c; + if (MultiByteToWideChar(GetACP(), 0, (char*)mb, 2, &wc, 1) > 0) + c = wc; + } +# endif if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) { ret = OK; @@ -1597,7 +1663,7 @@ color2index(VTermColor *color, int fg, int *boldp) /* 216-color cube */ return 17 + ((red + 25) / 0x33) * 36 - + ((green + 25) / 0x33) * 6 + + ((green + 25) / 0x33) * 6 + (blue + 25) / 0x33; } return 0; @@ -2046,20 +2112,39 @@ term_update_window(win_T *wp) else { #if defined(FEAT_MBYTE) - if (enc_utf8 && c >= 0x80) + if (enc_utf8) { - ScreenLines[off] = ' '; - ScreenLinesUC[off] = c; - } - else - { - ScreenLines[off] = c; - if (enc_utf8) + if (c >= 0x80) + { + ScreenLines[off] = ' '; + ScreenLinesUC[off] = c; + } + else + { + ScreenLines[off] = c; ScreenLinesUC[off] = NUL; + } } -#else - ScreenLines[off] = c; +# ifdef _WIN32 + else if (has_mbyte && c >= 0x80) + { + char_u mb[MB_MAXBYTES+1]; + WCHAR wc = c; + + if (WideCharToMultiByte(GetACP(), 0, &wc, 1, + (char*)mb, 2, 0, 0) > 1) + { + ScreenLines[off] = mb[0]; + ScreenLines[off+1] = mb[1]; + cell.width = mb_ptr2cells(mb); + } + else + ScreenLines[off] = c; + } +# endif + else #endif + ScreenLines[off] = c; } ScreenAttrs[off] = cell2attr(cell.attrs, cell.fg, cell.bg); @@ -2067,11 +2152,12 @@ term_update_window(win_T *wp) ++off; if (cell.width == 2) { - ScreenLines[off] = NUL; #if defined(FEAT_MBYTE) if (enc_utf8) ScreenLinesUC[off] = NUL; + else if (!has_mbyte) #endif + ScreenLines[off] = NUL; ++pos.col; ++off; } @@ -2156,6 +2242,7 @@ create_vterm(term_T *term, int rows, int cols) { VTerm *vterm; VTermScreen *screen; + VTermValue value; vterm = vterm_new(rows, cols); term->tl_vterm = vterm; @@ -2180,6 +2267,11 @@ create_vterm(term_T *term, int rows, int cols) /* Allow using alternate screen. */ vterm_screen_enable_altscreen(screen, 1); + + /* We do not want a blinking cursor by default. */ + value.boolean = 0; + vterm_state_set_termprop(vterm_obtain_state(vterm), + VTERM_PROP_CURSORBLINK, &value); } /* @@ -2832,13 +2924,20 @@ dyn_winpty_init(int verbose) * Return OK or FAIL. */ static int -term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T *opt) +term_and_job_init( + term_T *term, + int rows, + int cols, + typval_T *argvar, + jobopt_T *opt) { - WCHAR *p = NULL; + WCHAR *cmd_wchar = NULL; channel_T *channel = NULL; job_T *job = NULL; DWORD error; - HANDLE jo = NULL, child_process_handle, child_thread_handle; + HANDLE jo = NULL; + HANDLE child_process_handle; + HANDLE child_thread_handle; void *winpty_err; void *spawn_config = NULL; char buf[MAX_PATH]; @@ -2858,8 +2957,8 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T * cmd = ga.ga_data; } - p = enc_to_utf16(cmd, NULL); - if (p == NULL) + cmd_wchar = enc_to_utf16(cmd, NULL); + if (cmd_wchar == NULL) return FAIL; job = job_alloc(); @@ -2884,7 +2983,7 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T * WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN, NULL, - p, + cmd_wchar, NULL, NULL, &winpty_err); @@ -2899,20 +2998,25 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T * if (job == NULL) goto failed; + /* TODO: when all lines are written and the fd is closed, the command + * doesn't get EOF and hangs. */ + if (opt->jo_set & JO_IN_BUF) + job->jv_in_buf = buflist_findnr(opt->jo_io_buf[PART_IN]); + if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle, &child_thread_handle, &error, &winpty_err)) goto failed; channel_set_pipes(channel, - (sock_T) CreateFileW( + (sock_T)CreateFileW( winpty_conin_name(term->tl_winpty), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), - (sock_T) CreateFileW( + (sock_T)CreateFileW( winpty_conout_name(term->tl_winpty), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL), - (sock_T) CreateFileW( + (sock_T)CreateFileW( winpty_conerr_name(term->tl_winpty), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)); @@ -2929,7 +3033,7 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T * } winpty_spawn_config_free(spawn_config); - vim_free(p); + vim_free(cmd_wchar); create_vterm(term, rows, cols); @@ -2952,8 +3056,8 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T * failed: if (argvar->v_type == VAR_LIST) vim_free(ga.ga_data); - if (p != NULL) - vim_free(p); + if (cmd_wchar != NULL) + vim_free(cmd_wchar); if (spawn_config != NULL) winpty_spawn_config_free(spawn_config); if (channel != NULL) @@ -3029,7 +3133,12 @@ terminal_enabled(void) * Return OK or FAIL. */ static int -term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T *opt) +term_and_job_init( + term_T *term, + int rows, + int cols, + typval_T *argvar, + jobopt_T *opt) { create_vterm(term, rows, cols); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 2b7127bffc..8cfcf0da9e 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -548,6 +548,19 @@ func Test_OptionSet() call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) + " 18: Setting string option" + let oldval = &tags + let g:options=[['tags', oldval, 'tagpath', 'global']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 1l: Resetting string option" + let g:options=[['tags', 'tagpath', oldval, 'global']] + set tags& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + " Cleanup au! OptionSet for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp'] diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index e737aead90..bdc5655d54 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -251,7 +251,7 @@ endfunc func Test_terminal_size() let cmd = Get_cat_123_cmd() - exe '5terminal ' . cmd + exe 'terminal ++rows=5 ' . cmd let size = term_getsize('') bwipe! call assert_equal(5, size[0]) @@ -262,7 +262,7 @@ func Test_terminal_size() call assert_equal(6, size[0]) vsplit - exe '5,33terminal ' . cmd + exe 'terminal ++rows=5 ++cols=33 ' . cmd let size = term_getsize('') bwipe! call assert_equal([5, 33], size) @@ -272,7 +272,7 @@ func Test_terminal_size() bwipe! call assert_equal([6, 36], size) - exe 'vertical 20terminal ' . cmd + exe 'vertical terminal ++cols=20 ' . cmd let size = term_getsize('') bwipe! call assert_equal(20, size[1]) @@ -283,7 +283,7 @@ func Test_terminal_size() call assert_equal(26, size[1]) split - exe 'vertical 6,20terminal ' . cmd + exe 'vertical terminal ++rows=6 ++cols=20 ' . cmd let size = term_getsize('') bwipe! call assert_equal([6, 20], size) @@ -458,9 +458,16 @@ func Test_terminal_noblock() call term_sendkeys(g:buf, 'echo ' . repeat(c, 5000) . "\") endfor call term_sendkeys(g:buf, "echo done\") + + " On MS-Windows there is an extra empty line below "done". Find "done" in + " the last-but-one or the last-but-two line. let g:lnum = term_getsize(g:buf)[0] - 1 - call WaitFor('term_getline(g:buf, g:lnum) =~ "done"', 3000) - call assert_match('done', term_getline(g:buf, g:lnum)) + call WaitFor('term_getline(g:buf, g:lnum) =~ "done" || term_getline(g:buf, g:lnum - 1) =~ "done"', 3000) + let line = term_getline(g:buf, g:lnum) + if line !~ 'done' + let line = term_getline(g:buf, g:lnum - 1) + endif + call assert_match('done', line) let g:job = term_getjob(g:buf) call Stop_shell_in_terminal(g:buf) @@ -470,3 +477,26 @@ func Test_terminal_noblock() unlet g:lnum bwipe endfunc + +func Test_terminal_write_stdin() + " Todo: make this work on all systems. + if !has('unix') + return + endif + new + call setline(1, ['one', 'two', 'three']) + %term wc + call WaitFor('getline(1) != ""') + let nrs = split(getline(1)) + call assert_equal(['3', '3', '14'], nrs) + bwipe + + call setline(1, ['one', 'two', 'three', 'four']) + 2,3term wc + call WaitFor('getline(1) != ""') + let nrs = split(getline(1)) + call assert_equal(['2', '2', '10'], nrs) + bwipe + + bwipe! +endfunc diff --git a/src/version.c b/src/version.c index 1cd3f4d9b7..ee219872ca 100644 --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,28 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 983, +/**/ + 982, +/**/ + 981, +/**/ + 980, +/**/ + 979, +/**/ + 978, +/**/ + 977, +/**/ + 976, +/**/ + 975, +/**/ + 974, +/**/ + 973, /**/ 972, /**/