diff --git a/nsis/gvim.nsi b/nsis/gvim.nsi index dba9021b0d..b424392728 100644 --- a/nsis/gvim.nsi +++ b/nsis/gvim.nsi @@ -191,13 +191,16 @@ Section "Vim executables and runtime files" File ${VIMSRC}\vimrun.exe File /oname=tee.exe ${VIMSRC}\teew32.exe File /oname=xxd.exe ${VIMSRC}\xxdw32.exe - File ${VIMTOOLS}\diff.exe File ${VIMRT}\vimtutor.bat File ${VIMRT}\README.txt File ..\uninstal.txt File ${VIMRT}\*.vim File ${VIMRT}\rgb.txt + File ${VIMTOOLS}\diff.exe + File ${VIMTOOLS}\winpty32.dll + File ${VIMTOOLS}\winpty-agent.exe + SetOutPath $0\colors File ${VIMRT}\colors\*.* diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 9d8a7a8727..f9eb575803 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: Bram Moolenaar -" Last Change: 2017 Aug 11 +" Last Change: 2017 Aug 19 " If there already is an option window, jump to that one. let buf = bufnr('option-window') @@ -510,6 +510,10 @@ if has("terminal") call append("$", "termkey\tkey that precedes Vim commands in a terminal window") call append("$", "\t(local to window)") call OptionL("tk") + if exists("&winptydll") + call append("$", "winptydll\tname of the winpty dynamic library") + call OptionG("winptydll", &winptydll) + endif endif diff --git a/src/Makefile b/src/Makefile index 06019606ba..6dd70feffe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2291,6 +2291,7 @@ test_arglist \ test_utf8_comparisons \ test_viminfo \ test_vimscript \ + test_virtualedit \ test_visual \ test_window_cmd \ test_window_id \ diff --git a/src/channel.c b/src/channel.c index e1e043bcac..29a757fe36 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3039,7 +3039,9 @@ channel_fill_poll_write(int nfd_in, struct pollfd *fds) { chanpart_T *in_part = &ch->ch_part[PART_IN]; - if (in_part->ch_fd != INVALID_FD && in_part->ch_bufref.br_buf != NULL) + if (in_part->ch_fd != INVALID_FD + && (in_part->ch_bufref.br_buf != NULL + || in_part->ch_writeque.wq_next != NULL)) { in_part->ch_poll_idx = nfd; fds[nfd].fd = in_part->ch_fd; @@ -3582,7 +3584,7 @@ channel_set_nonblock(channel_T *channel, ch_part_T part) ioctlsocket(fd, FIONBIO, &val); #else - fcntl(fd, F_SETFL, O_NONBLOCK); + (void)fcntl(fd, F_SETFL, O_NONBLOCK); #endif ch_part->ch_nonblocking = TRUE; } @@ -3971,13 +3973,7 @@ channel_poll_check(int ret_in, void *fds_in) idx = in_part->ch_poll_idx; if (ret > 0 && idx != -1 && (fds[idx].revents & POLLOUT)) { - if (in_part->ch_buf_append) - { - if (in_part->ch_bufref.br_buf != NULL) - channel_write_new_lines(in_part->ch_bufref.br_buf); - } - else - channel_write_in(channel); + channel_write_input(channel); --ret; } } diff --git a/src/misc2.c b/src/misc2.c index 3ddadd01e6..e00e83e8c9 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -605,7 +605,18 @@ check_cursor_col_win(win_T *win) else if (ve_flags == VE_ALL) { if (oldcoladd > win->w_cursor.col) + { win->w_cursor.coladd = oldcoladd - win->w_cursor.col; + if (win->w_cursor.col < len && win->w_cursor.coladd > 0) + { + int cs, ce; + + /* check that coladd is not more than the char width */ + getvcol(win, &win->w_cursor, &cs, NULL, &ce); + if (win->w_cursor.coladd > ce - cs) + win->w_cursor.coladd = ce - cs; + } + } else /* avoid weird number when there is a miscalculation or overflow */ win->w_cursor.coladd = 0; diff --git a/src/normal.c b/src/normal.c index 4ca46b0bb8..a02d60614d 100644 --- a/src/normal.c +++ b/src/normal.c @@ -1571,7 +1571,12 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank) oap->start = VIsual; if (VIsual_mode == 'V') + { oap->start.col = 0; +# ifdef FEAT_VIRTUALEDIT + oap->start.coladd = 0; +# endif + } } /* @@ -7614,6 +7619,7 @@ nv_gomark(cmdarg_T *cap) if (!virtual_active()) curwin->w_cursor.coladd = 0; #endif + check_cursor_col(); #ifdef FEAT_FOLDING if (cap->oap->op_type == OP_NOP && pos != NULL diff --git a/src/option.c b/src/option.c index 785fb23640..d94183dc42 100644 --- a/src/option.c +++ b/src/option.c @@ -3276,6 +3276,7 @@ static struct vimoption options[] = 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) diff --git a/src/syntax.c b/src/syntax.c index ea47defee8..f6741b7b95 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -10000,7 +10000,7 @@ highlight_changed(void) if (ga_grow(&highlight_ga, 28) == FAIL) return FAIL; hlcnt = highlight_ga.ga_len; - if (id_S == 0) + if (id_S == -1) { /* Make sure id_S is always valid to simplify code below. Use the last * entry. */ diff --git a/src/term.c b/src/term.c index 8181b7704b..e6aadc6d0c 100644 --- a/src/term.c +++ b/src/term.c @@ -114,21 +114,22 @@ char *tgetstr(char *, char **); # else # define LOG_TR(msg) # endif + +# define STATUS_GET 1 /* send request when switching to RAW mode */ +# define STATUS_SENT 2 /* did send request, waiting for response */ +# define STATUS_GOT 3 /* received response */ + /* Request Terminal Version status: */ -# define CRV_GET 1 /* send T_CRV when switched to RAW mode */ -# define CRV_SENT 2 /* did send T_CRV, waiting for answer */ -# define CRV_GOT 3 /* received T_CRV response */ -static int crv_status = CRV_GET; +static int crv_status = STATUS_GET; + /* Request Cursor position report: */ -# define U7_GET 1 /* send T_U7 when switched to RAW mode */ -# define U7_SENT 2 /* did send T_U7, waiting for answer */ -# define U7_GOT 3 /* received T_U7 response */ -static int u7_status = U7_GET; +static int u7_status = STATUS_GET; + /* Request background color report: */ -# define RBG_GET 1 /* send T_RBG when switched to RAW mode */ -# define RBG_SENT 2 /* did send T_RBG, waiting for answer */ -# define RBG_GOT 3 /* received T_RBG response */ -static int rbg_status = RBG_GET; +static int rbg_status = STATUS_GET; + +/* Request cursor mode report: */ +static int rcm_status = STATUS_GET; # endif /* @@ -160,6 +161,14 @@ static char_u *vim_tgetstr(char *s, char_u **pp); static int detected_8bit = FALSE; /* detected 8-bit terminal */ +#ifdef FEAT_TERMRESPONSE +/* When the cursor shape was detected these values are used: + * 1: block, 2: underline, 3: vertical bar + * initial_cursor_blink is only valid when initial_cursor_shape is not zero. */ +static int initial_cursor_shape = 0; +static int initial_cursor_blink = FALSE; +#endif + static struct builtin_term builtin_termcaps[] = { @@ -819,12 +828,18 @@ 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")}, # 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", ESC_STR "[%i%p1%d;%p2%dH")}, @@ -1809,9 +1824,9 @@ set_termname(char_u *term) * is being used. * Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly. */ -#ifdef FEAT_GUI +# ifdef FEAT_GUI if (!gui.in_use) -#endif +# endif get_stty(); #endif @@ -1912,8 +1927,8 @@ set_termname(char_u *term) full_screen = TRUE; /* we can use termcap codes from now on */ set_term_defaults(); /* use current values as defaults */ #ifdef FEAT_TERMRESPONSE - LOG_TR("setting crv_status to CRV_GET"); - crv_status = CRV_GET; /* Get terminal version later */ + LOG_TR("setting crv_status to STATUS_GET"); + crv_status = STATUS_GET; /* Get terminal version later */ #endif /* @@ -3304,9 +3319,10 @@ settmode(int tmode) /* May need to check for T_CRV response and termcodes, it * doesn't work in Cooked mode, an external program may get * them. */ - if (tmode != TMODE_RAW && (crv_status == CRV_SENT - || u7_status == U7_SENT - || rbg_status == RBG_SENT)) + if (tmode != TMODE_RAW && (crv_status == STATUS_SENT + || u7_status == STATUS_SENT + || rbg_status == STATUS_SENT + || rcm_status == STATUS_SENT)) (void)vpeekc_nomap(); check_for_codes_from_term(); } @@ -3353,7 +3369,7 @@ starttermcap(void) may_req_termresponse(); /* Immediately check for a response. If t_Co changes, we don't * want to redraw with wrong colors first. */ - if (crv_status == CRV_SENT) + if (crv_status == STATUS_SENT) check_for_codes_from_term(); } #endif @@ -3373,8 +3389,10 @@ stoptermcap(void) # endif { /* May need to discard T_CRV, T_U7 or T_RBG response. */ - if (crv_status == CRV_SENT || u7_status == U7_SENT - || rbg_status == RBG_SENT) + if (crv_status == STATUS_SENT + || u7_status == STATUS_SENT + || rbg_status == STATUS_SENT + || rcm_status == STATUS_SENT) { # ifdef UNIX /* Give the terminal a chance to respond. */ @@ -3420,14 +3438,14 @@ stoptermcap(void) void may_req_termresponse(void) { - if (crv_status == CRV_GET + if (crv_status == STATUS_GET && can_get_termresponse() && starting == 0 && *T_CRV != NUL) { - LOG_TR("Sending CRV"); + LOG_TR("Sending CRV request"); out_str(T_CRV); - crv_status = CRV_SENT; + crv_status = STATUS_SENT; /* check for the characters now, otherwise they might be eaten by * get_keystroke() */ out_flush(); @@ -3448,7 +3466,7 @@ may_req_termresponse(void) void may_req_ambiguous_char_width(void) { - if (u7_status == U7_GET + if (u7_status == STATUS_GET && can_get_termresponse() && starting == 0 && *T_U7 != NUL @@ -3463,7 +3481,7 @@ may_req_ambiguous_char_width(void) buf[mb_char2bytes(0x25bd, buf)] = 0; out_str(buf); out_str(T_U7); - u7_status = U7_SENT; + u7_status = STATUS_SENT; out_flush(); /* This overwrites a few characters on the screen, a redraw is needed @@ -3483,19 +3501,40 @@ may_req_ambiguous_char_width(void) /* * Similar to requesting the version string: Request the terminal background * color when it is the right moment. + * Also request the cursor shape, if possible. */ void may_req_bg_color(void) { - if (rbg_status == RBG_GET - && can_get_termresponse() - && starting == 0 - && *T_RBG != NUL - && !option_was_set((char_u *)"bg")) + int done = FALSE; + + if (can_get_termresponse() && starting == 0) + { + /* Only request background if t_RB is set and 'background' wasn't + * changed. */ + if (rbg_status == STATUS_GET + && *T_RBG != NUL + && !option_was_set((char_u *)"bg")) + { + LOG_TR("Sending BG request"); + out_str(T_RBG); + rbg_status = STATUS_SENT; + done = TRUE; + } + + /* Only request the cursor shape if t_SH and t_RS are set. */ + if (rcm_status == STATUS_GET + && *T_CSH != NUL + && *T_CRS != NUL) + { + LOG_TR("Sending cursor shape request"); + out_str(T_CRS); + rcm_status = STATUS_SENT; + done = TRUE; + } + } + if (done) { - LOG_TR("Sending BG request"); - out_str(T_RBG); - rbg_status = RBG_SENT; /* check for the characters now, otherwise they might be eaten by * get_keystroke() */ out_flush(); @@ -3682,7 +3721,14 @@ term_cursor_mode(int forced) /* Only do something when redrawing the screen and we can restore the * mode. */ if (!full_screen || *T_CEI == NUL) + { +# ifdef FEAT_TERMRESPONSE + if (forced && initial_cursor_shape > 0) + /* Restore to initial values. */ + term_cursor_shape(initial_cursor_shape, initial_cursor_blink); +# endif return; + } if ((State & REPLACE) == REPLACE) { @@ -3736,6 +3782,7 @@ term_cursor_blink(int blink) out_str(T_VE); out_flush(); } +# endif /* * "shape" == 1: block, "shape" == 2: underline, "shape" == 3: vertical bar @@ -3749,7 +3796,6 @@ term_cursor_shape(int shape, int blink) out_flush(); } } -# endif #endif /* @@ -4296,7 +4342,8 @@ check_termcode( { /* Skip over the digits, the final char must * follow. */ - for (j = slen - 2; j < len && (isdigit(tp[j]) || tp[j] == ';'); ++j) + for (j = slen - 2; j < len && (isdigit(tp[j]) + || tp[j] == ';'); ++j) ; ++j; if (len < j) /* got a partial sequence */ @@ -4400,7 +4447,7 @@ check_termcode( char *aw = NULL; LOG_TR("Received U7 status"); - u7_status = U7_GOT; + u7_status = STATUS_GOT; # ifdef FEAT_AUTOCMD did_cursorhold = TRUE; # endif @@ -4439,8 +4486,8 @@ check_termcode( /* eat it when at least one digit and ending in 'c' */ if (*T_CRV != NUL && i > 2 + (tp[0] != CSI) && tp[i] == 'c') { - LOG_TR("Received CRV"); - crv_status = CRV_GOT; + LOG_TR("Received CRV response"); + crv_status = STATUS_GOT; # ifdef FEAT_AUTOCMD did_cursorhold = TRUE; # endif @@ -4572,8 +4619,8 @@ check_termcode( char *newval = (3 * '6' < tp[j+7] + tp[j+12] + tp[j+17]) ? "light" : "dark"; - LOG_TR("Received RBG"); - rbg_status = RBG_GOT; + LOG_TR("Received RBG response"); + rbg_status = STATUS_GOT; if (STRCMP(p_bg, newval) != 0) { /* value differs, apply it */ @@ -4598,24 +4645,33 @@ check_termcode( } /* Check for key code response from xterm: - * * {lead}{flag}+r<{tail} * * {lead} can be P or DCS * {flag} can be '0' or '1' * {tail} can be Esc>\ or STERM * - * Consume any code that starts with "{lead}.+r". + * Check for cursor shape response from xterm: + * {lead}1$r q{tail} + * + * {lead} can be P or DCS + * {tail} can be Esc>\ or STERM + * + * Consume any code that starts with "{lead}.+r" or "{lead}.$r". */ - else if (check_for_codes + else if ((check_for_codes || rcm_status == STATUS_SENT) && ((tp[0] == ESC && len >= 2 && tp[1] == 'P') || tp[0] == DCS)) { j = 1 + (tp[0] == ESC); - if (len >= j + 3 && (argp[1] != '+' || argp[2] != 'r')) + if (len < j + 3) + i = len; /* need more chars */ + else if ((argp[1] != '+' && argp[1] != '$') || argp[2] != 'r') i = 0; /* no match */ - else + else if (argp[1] == '+') + /* key code response */ for (i = j; i < len; ++i) + { if ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\') || tp[i] == STERM) { @@ -4626,6 +4682,32 @@ check_termcode( slen = i + 1 + (tp[i] == ESC); break; } + } + else if ((len >= j + 6 && isdigit(argp[3])) + && argp[4] == ' ' + && argp[5] == 'q') + { + /* cursor shape response */ + i = j + 6; + if ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\') + || tp[i] == STERM) + { + int number = argp[3] - '0'; + + /* 0, 1 = block blink, 2 = block + * 3 = underline blink, 4 = underline + * 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; + rcm_status = STATUS_GOT; + LOG_TR("Received cursor shape response"); + + key_name[0] = (int)KS_EXTRA; + key_name[1] = (int)KE_IGNORE; + slen = i + 1 + (tp[i] == ESC); + } + } if (i == len) { @@ -5843,7 +5925,7 @@ gather_termleader(void) termleader[len++] = CSI; /* the GUI codes are not in termcodes[] */ #endif #ifdef FEAT_TERMRESPONSE - if (check_for_codes) + if (check_for_codes || *T_CRS != NUL) termleader[len++] = DCS; /* the termcode response starts with DCS in 8-bit mode */ #endif diff --git a/src/term.h b/src/term.h index 66ad4c66c5..00e3c96aa2 100644 --- a/src/term.h +++ b/src/term.h @@ -41,6 +41,7 @@ enum SpecialKey KS_VE, /* cursor visible */ KS_VS, /* cursor very visible */ KS_CSH, /* cursor shape */ + KS_CRS, /* request cursor shape */ KS_ME, /* normal mode */ KS_MR, /* reverse mode */ KS_MD, /* bold mode */ @@ -132,6 +133,7 @@ extern char_u *(term_strings[]); /* current terminal strings */ #define T_VE (TERM_STR(KS_VE)) /* cursor visible */ #define T_VS (TERM_STR(KS_VS)) /* cursor very visible */ #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 */ #define T_MR (TERM_STR(KS_MR)) /* reverse mode */ #define T_MD (TERM_STR(KS_MD)) /* bold mode */ diff --git a/src/terminal.c b/src/terminal.c index f2ab15942b..e22dec93a9 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -38,6 +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 * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. @@ -1041,14 +1042,13 @@ term_enter_job_mode() /* Remove the terminal contents from the scrollback and the buffer. */ gap = &term->tl_scrollback; - while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled) + while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled + && gap->ga_len > 0) { ml_delete(curbuf->b_ml.ml_line_count, FALSE); line = (sb_line_T *)gap->ga_data + gap->ga_len - 1; vim_free(line->sb_cells); --gap->ga_len; - if (gap->ga_len == 0) - break; } check_cursor(); @@ -1292,9 +1292,10 @@ may_restore_cursor_props(void) if (did_change_cursor) { did_change_cursor = FALSE; - ui_cursor_shape_forced(TRUE); term_cursor_color((char_u *)""); term_cursor_blink(FALSE); + /* this will restore the initial cursor style, if possible */ + ui_cursor_shape_forced(TRUE); } } diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index 8e245ed20d..e83256a4d3 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -55,4 +55,5 @@ source test_taglist.vim source test_timers.vim source test_true_false.vim source test_unlet.vim +source test_virtualedit.vim source test_window_cmd.vim diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 64104abebe..e737aead90 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -452,14 +452,21 @@ func Test_terminal_list_args() endfunction func Test_terminal_noblock() - let buf = term_start(&shell) + let g:buf = term_start(&shell) for c in ['a','b','c','d','e','f','g','h','i','j','k'] - call term_sendkeys(buf, 'echo ' . repeat(c, 5000) . "\") + call term_sendkeys(g:buf, 'echo ' . repeat(c, 5000) . "\") endfor + call term_sendkeys(g:buf, "echo done\") + 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)) - let g:job = term_getjob(buf) - call Stop_shell_in_terminal(buf) - call term_wait(buf) + let g:job = term_getjob(g:buf) + call Stop_shell_in_terminal(g:buf) + call term_wait(g:buf) + unlet g:buf + unlet g:job + unlet g:lnum bwipe endfunc diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim new file mode 100644 index 0000000000..da143c518f --- /dev/null +++ b/src/testdir/test_virtualedit.vim @@ -0,0 +1,31 @@ +" Tests for 'virtualedit'. + +func Test_yank_move_change() + split + call setline(1, [ + \ "func foo() error {", + \ "\tif n, err := bar();", + \ "\terr != nil {", + \ "\t\treturn err", + \ "\t}", + \ "\tn = n * n", + \ ]) + set virtualedit=all + set ts=4 + function! MoveSelectionDown(count) abort + normal! m` + silent! exe "'<,'>move'>+".a:count + norm! `` + endfunction + + xmap ]e :call MoveSelectionDown(v:count1) + 2 + normal 2gg + normal J + normal jVj + normal ]e + normal ce + bwipe! + set virtualedit= + set ts=8 +endfunc diff --git a/src/version.c b/src/version.c index b90d448824..1cd3f4d9b7 100644 --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,30 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 972, +/**/ + 971, +/**/ + 970, +/**/ + 969, +/**/ + 968, +/**/ + 967, +/**/ + 966, +/**/ + 965, +/**/ + 964, +/**/ + 963, +/**/ + 962, +/**/ + 961, /**/ 960, /**/