From 99433291b135094d9592c41f96d3ccd60073e2c1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 12:37:47 +0200 Subject: [PATCH 1/6] patch 8.0.1072: :highlight command causes a redraw even when nothing changed Problem: The :highlight command causes a redraw even when nothing changed. Solution: Only set "need_highlight_changed" when an attribute changed. --- src/syntax.c | 193 ++++++++++++++++++++++++++++---------------------- src/version.c | 2 + 2 files changed, 109 insertions(+), 86 deletions(-) diff --git a/src/syntax.c b/src/syntax.c index 00e292932d..ded6cd33d9 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -7364,6 +7364,8 @@ do_highlight( int attr; int id; int idx; + struct hl_group *item; + struct hl_group item_before; int dodefault = FALSE; int doclear = FALSE; int dolink = FALSE; @@ -7459,12 +7461,13 @@ do_highlight( } from_id = syn_check_group(from_start, (int)(from_end - from_start)); + item = &HL_TABLE()[from_id - 1]; if (STRNCMP(to_start, "NONE", 4) == 0) to_id = 0; else to_id = syn_check_group(to_start, (int)(to_end - to_start)); - if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) + if (from_id > 0 && (!init || item->sg_set == 0)) { /* * Don't allow a link when there already is some highlighting @@ -7476,22 +7479,26 @@ do_highlight( if (sourcing_name == NULL && !dodefault) EMSG(_("E414: group has settings, highlight link ignored")); } - else + else if (item->sg_link != to_id +#ifdef FEAT_EVAL + || item->sg_scriptID != current_SID +#endif + || item->sg_cleared) { if (!init) - HL_TABLE()[from_id - 1].sg_set |= SG_LINK; - HL_TABLE()[from_id - 1].sg_link = to_id; + item->sg_set |= SG_LINK; + item->sg_link = to_id; #ifdef FEAT_EVAL - HL_TABLE()[from_id - 1].sg_scriptID = current_SID; + item->sg_scriptID = current_SID; #endif - HL_TABLE()[from_id - 1].sg_cleared = FALSE; + item->sg_cleared = FALSE; redraw_all_later(SOME_VALID); + + /* Only call highlight_changed() once after multiple changes. */ + need_highlight_changed = TRUE; } } - /* Only call highlight_changed() once, after sourcing a syntax file */ - need_highlight_changed = TRUE; - return; } @@ -7577,19 +7584,23 @@ do_highlight( if (id == 0) /* failed (out of memory) */ return; idx = id - 1; /* index is ID minus one */ + item = &HL_TABLE()[idx]; /* Return if "default" was used and the group already has settings. */ if (dodefault && hl_has_settings(idx, TRUE)) return; - if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) + /* Make a copy so we can check if any attribute actually changed. */ + item_before = *item; + + if (STRCMP(item->sg_name_u, "NORMAL") == 0) is_normal_group = TRUE; #ifdef FEAT_GUI_X11 - else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0) + else if (STRCMP(item->sg_name_u, "MENU") == 0) is_menu_group = TRUE; - else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0) + else if (STRCMP(item->sg_name_u, "SCROLLBAR") == 0) is_scrollbar_group = TRUE; - else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0) + else if (STRCMP(item->sg_name_u, "TOOLTIP") == 0) is_tooltip_group = TRUE; #endif @@ -7598,7 +7609,7 @@ do_highlight( { highlight_clear(idx); if (!doclear) - HL_TABLE()[idx].sg_set = 0; + item->sg_set = 0; } if (!doclear) @@ -7629,10 +7640,10 @@ do_highlight( if (STRCMP(key, "NONE") == 0) { - if (!init || HL_TABLE()[idx].sg_set == 0) + if (!init || item->sg_set == 0) { if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; + item->sg_set |= SG_TERM+SG_CTERM+SG_GUI; highlight_clear(idx); } continue; @@ -7719,31 +7730,31 @@ do_highlight( break; if (*key == 'T') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) + if (!init || !(item->sg_set & SG_TERM)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM; - HL_TABLE()[idx].sg_term = attr; + item->sg_set |= SG_TERM; + item->sg_term = attr; } } else if (*key == 'C') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) + if (!init || !(item->sg_set & SG_CTERM)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_CTERM; - HL_TABLE()[idx].sg_cterm = attr; - HL_TABLE()[idx].sg_cterm_bold = FALSE; + item->sg_set |= SG_CTERM; + item->sg_cterm = attr; + item->sg_cterm_bold = FALSE; } } #if defined(FEAT_GUI) || defined(FEAT_EVAL) else { - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) + if (!init || !(item->sg_set & SG_GUI)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_GUI; - HL_TABLE()[idx].sg_gui = attr; + item->sg_set |= SG_GUI; + item->sg_gui = attr; } } #endif @@ -7752,69 +7763,74 @@ do_highlight( { /* in non-GUI fonts are simply ignored */ #ifdef FEAT_GUI - if (!gui.shell_created) + if (item->sg_font_name != NULL + && STRCMP(item->sg_font_name, arg) == 0) + { + /* Font name didn't change, ignore. */ + } + else if (!gui.shell_created) { /* GUI not started yet, always accept the name. */ - vim_free(HL_TABLE()[idx].sg_font_name); - HL_TABLE()[idx].sg_font_name = vim_strsave(arg); + vim_free(item->sg_font_name); + item->sg_font_name = vim_strsave(arg); } else { - GuiFont temp_sg_font = HL_TABLE()[idx].sg_font; + GuiFont temp_sg_font = item->sg_font; # ifdef FEAT_XFONTSET - GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset; + GuiFontset temp_sg_fontset = item->sg_fontset; # endif /* First, save the current font/fontset. * Then try to allocate the font/fontset. - * If the allocation fails, HL_TABLE()[idx].sg_font OR + * If the allocation fails, item->sg_font OR * sg_fontset will be set to NOFONT or NOFONTSET respectively. */ - HL_TABLE()[idx].sg_font = NOFONT; + item->sg_font = NOFONT; # ifdef FEAT_XFONTSET - HL_TABLE()[idx].sg_fontset = NOFONTSET; + item->sg_fontset = NOFONTSET; # endif hl_do_font(idx, arg, is_normal_group, is_menu_group, is_tooltip_group, FALSE); # ifdef FEAT_XFONTSET - if (HL_TABLE()[idx].sg_fontset != NOFONTSET) + if (item->sg_fontset != NOFONTSET) { /* New fontset was accepted. Free the old one, if there * was one. */ gui_mch_free_fontset(temp_sg_fontset); - vim_free(HL_TABLE()[idx].sg_font_name); - HL_TABLE()[idx].sg_font_name = vim_strsave(arg); + vim_free(item->sg_font_name); + item->sg_font_name = vim_strsave(arg); } else - HL_TABLE()[idx].sg_fontset = temp_sg_fontset; + item->sg_fontset = temp_sg_fontset; # endif - if (HL_TABLE()[idx].sg_font != NOFONT) + if (item->sg_font != NOFONT) { /* New font was accepted. Free the old one, if there was * one. */ gui_mch_free_font(temp_sg_font); - vim_free(HL_TABLE()[idx].sg_font_name); - HL_TABLE()[idx].sg_font_name = vim_strsave(arg); + vim_free(item->sg_font_name); + item->sg_font_name = vim_strsave(arg); } else - HL_TABLE()[idx].sg_font = temp_sg_font; + item->sg_font = temp_sg_font; } #endif } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) + if (!init || !(item->sg_set & SG_CTERM)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_CTERM; + item->sg_set |= SG_CTERM; /* When setting the foreground color, and previously the "bold" * flag was set for a light color, reset it now */ - if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) + if (key[5] == 'F' && item->sg_cterm_bold) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = FALSE; + item->sg_cterm &= ~HL_BOLD; + item->sg_cterm_bold = FALSE; } if (VIM_ISDIGIT(*arg)) @@ -7871,22 +7887,22 @@ do_highlight( * colors (on some terminals, e.g. "linux") */ if (bold == TRUE) { - HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = TRUE; + item->sg_cterm |= HL_BOLD; + item->sg_cterm_bold = TRUE; } else if (bold == FALSE) - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + item->sg_cterm &= ~HL_BOLD; } /* Add one to the argument, to avoid zero. Zero is used for * "NONE", then "color" is -1. */ if (key[5] == 'F') { - HL_TABLE()[idx].sg_cterm_fg = color + 1; + item->sg_cterm_fg = color + 1; if (is_normal_group) { cterm_normal_fg_color = color + 1; - cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); + cterm_normal_fg_bold = (item->sg_cterm & HL_BOLD); #ifdef FEAT_GUI /* Don't do this if the GUI is used. */ if (!gui.in_use && !gui.starting) @@ -7900,7 +7916,7 @@ do_highlight( } else { - HL_TABLE()[idx].sg_cterm_bg = color + 1; + item->sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; @@ -7940,23 +7956,23 @@ do_highlight( else if (STRCMP(key, "GUIFG") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) + if (!init || !(item->sg_set & SG_GUI)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_GUI; + item->sg_set |= SG_GUI; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* In GUI guifg colors are only used when recognized */ i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) { - HL_TABLE()[idx].sg_gui_fg = i; + item->sg_gui_fg = i; # endif - vim_free(HL_TABLE()[idx].sg_gui_fg_name); + vim_free(item->sg_gui_fg_name); if (STRCMP(arg, "NONE") != 0) - HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg); + item->sg_gui_fg_name = vim_strsave(arg); else - HL_TABLE()[idx].sg_gui_fg_name = NULL; + item->sg_gui_fg_name = NULL; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) # ifdef FEAT_GUI_X11 if (is_menu_group) @@ -7977,23 +7993,23 @@ do_highlight( else if (STRCMP(key, "GUIBG") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) + if (!init || !(item->sg_set & SG_GUI)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_GUI; + item->sg_set |= SG_GUI; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* In GUI guifg colors are only used when recognized */ i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) { - HL_TABLE()[idx].sg_gui_bg = i; + item->sg_gui_bg = i; # endif - vim_free(HL_TABLE()[idx].sg_gui_bg_name); + vim_free(item->sg_gui_bg_name); if (STRCMP(arg, "NONE") != 0) - HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg); + item->sg_gui_bg_name = vim_strsave(arg); else - HL_TABLE()[idx].sg_gui_bg_name = NULL; + item->sg_gui_bg_name = NULL; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) # ifdef FEAT_GUI_X11 if (is_menu_group) @@ -8014,22 +8030,22 @@ do_highlight( else if (STRCMP(key, "GUISP") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) + if (!init || !(item->sg_set & SG_GUI)) { if (!init) - HL_TABLE()[idx].sg_set |= SG_GUI; + item->sg_set |= SG_GUI; # ifdef FEAT_GUI i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) { - HL_TABLE()[idx].sg_gui_sp = i; + item->sg_gui_sp = i; # endif - vim_free(HL_TABLE()[idx].sg_gui_sp_name); + vim_free(item->sg_gui_sp_name); if (STRCMP(arg, "NONE") != 0) - HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg); + item->sg_gui_sp_name = vim_strsave(arg); else - HL_TABLE()[idx].sg_gui_sp_name = NULL; + item->sg_gui_sp_name = NULL; # ifdef FEAT_GUI } # endif @@ -8042,7 +8058,7 @@ do_highlight( char_u *tname; if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM; + item->sg_set |= SG_TERM; /* * The "start" and "stop" arguments can be a literal escape @@ -8109,13 +8125,13 @@ do_highlight( p = vim_strsave(buf); if (key[2] == 'A') { - vim_free(HL_TABLE()[idx].sg_start); - HL_TABLE()[idx].sg_start = p; + vim_free(item->sg_start); + item->sg_start = p; } else { - vim_free(HL_TABLE()[idx].sg_stop); - HL_TABLE()[idx].sg_stop = p; + vim_free(item->sg_stop); + item->sg_stop = p; } } else @@ -8124,13 +8140,13 @@ do_highlight( error = TRUE; break; } - HL_TABLE()[idx].sg_cleared = FALSE; + item->sg_cleared = FALSE; /* * When highlighting has been given for a group, don't link it. */ - if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) - HL_TABLE()[idx].sg_link = 0; + if (!init || !(item->sg_set & SG_LINK)) + item->sg_link = 0; /* * Continue with next argument. @@ -8147,10 +8163,10 @@ do_highlight( { if (is_normal_group) { - HL_TABLE()[idx].sg_term_attr = 0; - HL_TABLE()[idx].sg_cterm_attr = 0; + item->sg_term_attr = 0; + item->sg_cterm_attr = 0; #ifdef FEAT_GUI - HL_TABLE()[idx].sg_gui_attr = 0; + item->sg_gui_attr = 0; /* * Need to update all groups, because they might be using "bg" * and/or "fg", which have been changed now. @@ -8185,15 +8201,20 @@ do_highlight( else set_hl_attr(idx); #ifdef FEAT_EVAL - HL_TABLE()[idx].sg_scriptID = current_SID; + item->sg_scriptID = current_SID; #endif - redraw_all_later(NOT_VALID); } + vim_free(key); vim_free(arg); - /* Only call highlight_changed() once, after sourcing a syntax file */ - need_highlight_changed = TRUE; + /* Only call highlight_changed() once, after a sequence of highlight + * commands, and only if an attribute actually changed. */ + if (memcmp(item, &item_before, sizeof(item_before)) != 0) + { + redraw_all_later(NOT_VALID); + need_highlight_changed = TRUE; + } } #if defined(EXITFREE) || defined(PROTO) diff --git a/src/version.c b/src/version.c index 4d040fb0db..2ffd91a509 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1072, /**/ 1071, /**/ From ba2929b6afd2fc20479912a8dec789be26a38244 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 13:59:21 +0200 Subject: [PATCH 2/6] patch 8.0.1073: may get an endless loop if 'statusline' changes a highlight Problem: May get an endless loop if 'statusline' changes a highlight. Solution: Do not let evaluating 'statusline' trigger a redraw. --- src/buffer.c | 22 ++++++++++++++++------ src/version.c | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index e95abfa5af..57330bfa14 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3915,8 +3915,8 @@ build_stl_str_hl( char_u *t; int byteval; #ifdef FEAT_EVAL - win_T *o_curwin; - buf_T *o_curbuf; + win_T *save_curwin; + buf_T *save_curbuf; #endif int empty_line; colnr_T virtcol; @@ -3958,6 +3958,9 @@ build_stl_str_hl( char_u tmp[TMPLEN]; char_u *usefmt = fmt; struct stl_hlrec *sp; + int save_must_redraw = must_redraw; + int save_redr_type = curwin->w_redr_type; + int save_highlight_shcnaged = need_highlight_changed; #ifdef FEAT_EVAL /* @@ -4267,15 +4270,15 @@ build_stl_str_hl( vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); set_internal_string_var((char_u *)"actual_curbuf", tmp); - o_curbuf = curbuf; - o_curwin = curwin; + save_curbuf = curbuf; + save_curwin = curwin; curwin = wp; curbuf = wp->w_buffer; str = eval_to_string_safe(p, &t, use_sandbox); - curwin = o_curwin; - curbuf = o_curbuf; + curwin = save_curwin; + curbuf = save_curbuf; do_unlet((char_u *)"g:actual_curbuf", TRUE); if (str != NULL && *str != 0) @@ -4730,6 +4733,13 @@ build_stl_str_hl( sp->userhl = 0; } + /* We do not want redrawing a stausline, ruler, title, etc. to trigger + * another redraw, it may cause an endless loop. This happens when a + * statusline changes a highlight group. */ + must_redraw = save_must_redraw; + curwin->w_redr_type = save_redr_type; + need_highlight_changed = save_highlight_shcnaged; + return width; } #endif /* FEAT_STL_OPT */ diff --git a/src/version.c b/src/version.c index 2ffd91a509..9ec37eb667 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1073, /**/ 1072, /**/ From 2dc9d26c14e410c09e538cccfa90da19ae344ba4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 14:39:30 +0200 Subject: [PATCH 3/6] patch 8.0.1074: ":term NONE" does not work on MS-Windows Problem: ":term NONE" does not work on MS-Windows. Solution: Make it work. Split "pty" into "pty_in" and "pty_out". (Yasuhiro Matsumoto, closes #2058, closes #2045) --- runtime/doc/eval.txt | 21 ++++--- src/channel.c | 43 ++++++++++++-- src/evalfunc.c | 2 +- src/os_unix.c | 10 +++- src/structs.h | 6 +- src/terminal.c | 107 +++++++++++++++++++++++++++++----- src/testdir/test_terminal.vim | 29 +++++---- src/version.c | 2 + 8 files changed, 177 insertions(+), 43 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index f9c6ae3b46..af43bf28fd 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2401,7 +2401,7 @@ term_getscrolled({buf}) Number get the scroll count of a terminal term_getsize({buf}) List get the size of a terminal term_getstatus({buf}) String get the status of a terminal term_gettitle({buf}) String get the title of a terminal -term_gettty({buf}) String get the tty name of a terminal +term_getttty({buf}, [{input}]) String get the tty name of a terminal term_list() List get the list of terminal buffers term_scrape({buf}, {row}) List get row of a terminal screen term_sendkeys({buf}, {keys}) none send keystrokes to a terminal @@ -5245,7 +5245,8 @@ job_info({job}) *job_info()* "status" what |job_status()| returns "channel" what |job_getchannel()| returns "process" process ID - "tty" controlling terminal name, empty when none + "tty_in" terminal input name, empty when none + "tty_out" terminal output name, empty when none "exitval" only valid when "status" is "dead" "exit_cb" function to be called on exit "stoponexit" |job-stoponexit| @@ -8092,10 +8093,13 @@ term_gettitle({buf}) *term_gettitle()* string is returned. {only available when compiled with the |+terminal| feature} -term_gettty({buf}) *term_gettty()* +term_gettty({buf} [, {input}]) *term_gettty()* Get the name of the controlling terminal associated with - terminal window {buf}. - {buf} is used as with |term_getsize()|. + terminal window {buf}. {buf} is used as with |term_getsize()|. + + When {input} is omitted or 0, return the name for writing + (stdout). When {input} is 1 return the name for reading + (stdin). On UNIX, both return same name. {only available when compiled with the |+terminal| feature} term_list() *term_list()* @@ -8173,10 +8177,9 @@ term_start({cmd}, {options}) *term_start()* specified "botright sbuf %d" is used "eof_chars" Text to send after all buffer lines were written to the terminal. When not set - CTRL-D is used. For Python use CTRL-Z or - "exit()". For a shell use "exit". A CR - is always added. - {only on MS-Windows} + CTRL-D is used on MS-Windows. For Python + use CTRL-Z or "exit()". For a shell use + "exit". A CR is always added. {only available when compiled with the |+terminal| feature} diff --git a/src/channel.c b/src/channel.c index 30a4304d8a..c401df2907 100644 --- a/src/channel.c +++ b/src/channel.c @@ -969,7 +969,13 @@ ch_close_part(channel_T *channel, ch_part_T part) if ((part == PART_IN || channel->CH_IN_FD != *fd) && (part == PART_OUT || channel->CH_OUT_FD != *fd) && (part == PART_ERR || channel->CH_ERR_FD != *fd)) + { +#ifdef WIN32 + if (channel->ch_named_pipe) + DisconnectNamedPipe((HANDLE)fd); +#endif fd_close(*fd); + } } *fd = INVALID_FD; @@ -3086,7 +3092,20 @@ channel_wait(channel_T *channel, sock_T fd, int timeout) if (r && nread > 0) return CW_READY; if (r == 0) - return CW_ERROR; + { + DWORD err = GetLastError(); + + if (err != ERROR_BAD_PIPE && err != ERROR_BROKEN_PIPE) + return CW_ERROR; + + if (channel->ch_named_pipe) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } + else + return CW_ERROR; + } /* perhaps write some buffer lines */ channel_write_any_lines(); @@ -3670,7 +3689,20 @@ channel_send( if (part == PART_SOCK) res = sock_write(fd, (char *)buf, len); else + { res = fd_write(fd, (char *)buf, len); +#ifdef WIN32 + if (channel->ch_named_pipe) + { + if (res < 0) + { + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); + } + } +#endif + + } if (res < 0 && (errno == EWOULDBLOCK #ifdef EAGAIN || errno == EAGAIN @@ -4849,7 +4881,8 @@ job_free_contents(job_T *job) } mch_clear_job(job); - vim_free(job->jv_tty_name); + vim_free(job->jv_tty_in); + vim_free(job->jv_tty_out); vim_free(job->jv_stoponexit); free_callback(job->jv_exit_cb, job->jv_exit_partial); } @@ -5503,8 +5536,10 @@ job_info(job_T *job, dict_T *dict) nr = job->jv_proc_info.dwProcessId; #endif dict_add_nr_str(dict, "process", nr, NULL); - dict_add_nr_str(dict, "tty", 0L, - job->jv_tty_name != NULL ? job->jv_tty_name : (char_u *)""); + dict_add_nr_str(dict, "tty_in", 0L, + job->jv_tty_in != NULL ? job->jv_tty_in : (char_u *)""); + dict_add_nr_str(dict, "tty_out", 0L, + job->jv_tty_out != NULL ? job->jv_tty_out : (char_u *)""); dict_add_nr_str(dict, "exitval", job->jv_exitval, NULL); dict_add_nr_str(dict, "exit_cb", 0L, job->jv_exit_cb); diff --git a/src/evalfunc.c b/src/evalfunc.c index cf9c8d8ec3..a2542e2c5e 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -843,7 +843,7 @@ static struct fst {"term_getsize", 1, 1, f_term_getsize}, {"term_getstatus", 1, 1, f_term_getstatus}, {"term_gettitle", 1, 1, f_term_gettitle}, - {"term_gettty", 1, 1, f_term_gettty}, + {"term_gettty", 1, 2, f_term_gettty}, {"term_list", 0, 0, f_term_list}, {"term_scrape", 2, 2, f_term_scrape}, {"term_sendkeys", 2, 2, f_term_sendkeys}, diff --git a/src/os_unix.c b/src/os_unix.c index 1ec59fcdcf..3366efd1da 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5263,7 +5263,11 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options) && (!(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); + { + open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out); + if (job->jv_tty_out != NULL) + job->jv_tty_in = vim_strsave(job->jv_tty_out); + } /* TODO: without the channel feature connect the child to /dev/null? */ /* Open pipes for stdin, stdout, stderr. */ @@ -5687,7 +5691,9 @@ mch_create_pty_channel(job_T *job, jobopt_T *options) int pty_slave_fd = -1; channel_T *channel; - open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name); + open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out); + if (job->jv_tty_out != NULL) + job->jv_tty_in = vim_strsave(job->jv_tty_out); close(pty_slave_fd); channel = add_channel(); diff --git a/src/structs.h b/src/structs.h index f19377e36a..772b4e7064 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1487,7 +1487,8 @@ struct jobvar_S PROCESS_INFORMATION jv_proc_info; HANDLE jv_job_object; #endif - char_u *jv_tty_name; /* controlling tty, allocated */ + char_u *jv_tty_in; /* controlling tty input, allocated */ + char_u *jv_tty_out; /* controlling tty output, allocated */ jobstatus_T jv_status; char_u *jv_stoponexit; /* allocated */ int jv_exitval; @@ -1652,6 +1653,9 @@ struct channel_S { /* callback for Netbeans when channel is * closed */ +#ifdef WIN32 + int ch_named_pipe; /* using named pipe instead of pty */ +#endif char_u *ch_callback; /* call when any msg is not handled */ partial_T *ch_partial; char_u *ch_close_cb; /* call when channel is closed */ diff --git a/src/terminal.c b/src/terminal.c index 3ceb360fbc..9c4e75948c 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -38,8 +38,7 @@ * in tl_scrollback are no longer used. * * TODO: - * - ":term NONE" does not work on MS-Windows. - * https://github.com/vim/vim/pull/2056 + * - patch to use GUI or cterm colors for vterm. Yasuhiro, #2067 * - Redirecting output does not work on MS-Windows. * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. @@ -97,7 +96,8 @@ struct terminal_S { /* used when tl_job is NULL and only a pty was created */ int tl_tty_fd; - char_u *tl_tty_name; + char_u *tl_tty_in; + char_u *tl_tty_out; int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_channel_closed; @@ -2666,14 +2666,32 @@ f_term_gettty(typval_T *argvars, typval_T *rettv) { buf_T *buf = term_get_buf(argvars); char_u *p; + int num = 0; rettv->v_type = VAR_STRING; if (buf == NULL) return; - if (buf->b_term->tl_job != NULL) - p = buf->b_term->tl_job->jv_tty_name; - else - p = buf->b_term->tl_tty_name; + if (argvars[1].v_type != VAR_UNKNOWN) + num = get_tv_number(&argvars[1]); + + switch (num) + { + case 0: + if (buf->b_term->tl_job != NULL) + p = buf->b_term->tl_job->jv_tty_out; + else + p = buf->b_term->tl_tty_out; + break; + case 1: + if (buf->b_term->tl_job != NULL) + p = buf->b_term->tl_job->jv_tty_in; + else + p = buf->b_term->tl_tty_in; + break; + default: + EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + return; + } if (p != NULL) rettv->vval.v_string = vim_strsave(p); } @@ -3055,7 +3073,6 @@ term_and_job_init( HANDLE child_thread_handle; void *winpty_err; void *spawn_config = NULL; - char buf[MAX_PATH]; garray_T ga; char_u *cmd; @@ -3094,7 +3111,6 @@ term_and_job_init( if (term->tl_winpty == NULL) goto failed; - /* TODO: if the command is "NONE" only create a pty. */ spawn_config = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN, @@ -3162,9 +3178,10 @@ term_and_job_init( job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); job->jv_job_object = jo; job->jv_status = JOB_STARTED; - sprintf(buf, "winpty://%lu", - GetProcessId(winpty_agent_process(term->tl_winpty))); - job->jv_tty_name = vim_strsave((char_u*)buf); + job->jv_tty_in = utf16_to_enc( + (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); ++job->jv_refcount; term->tl_job = job; @@ -3205,9 +3222,68 @@ failed: } static int -create_pty_only(term_T *term, jobopt_T *opt) +create_pty_only(term_T *term, jobopt_T *options) { - /* TODO: implement this */ + HANDLE hPipeIn = INVALID_HANDLE_VALUE; + HANDLE hPipeOut = INVALID_HANDLE_VALUE; + char in_name[80], out_name[80]; + channel_T *channel = NULL; + + create_vterm(term, term->tl_rows, term->tl_cols); + + vim_snprintf(in_name, sizeof(in_name), "\\\\.\\pipe\\vim-%d-in-%d", + GetCurrentProcessId(), + curbuf->b_fnum); + hPipeIn = CreateNamedPipe(in_name, PIPE_ACCESS_OUTBOUND, + PIPE_TYPE_MESSAGE | PIPE_NOWAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, NMPWAIT_NOWAIT, NULL); + if (hPipeIn == INVALID_HANDLE_VALUE) + goto failed; + + vim_snprintf(out_name, sizeof(out_name), "\\\\.\\pipe\\vim-%d-out-%d", + GetCurrentProcessId(), + curbuf->b_fnum); + hPipeOut = CreateNamedPipe(out_name, PIPE_ACCESS_INBOUND, + PIPE_TYPE_MESSAGE | PIPE_NOWAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, 0, NULL); + if (hPipeOut == INVALID_HANDLE_VALUE) + goto failed; + + ConnectNamedPipe(hPipeIn, NULL); + ConnectNamedPipe(hPipeOut, NULL); + + term->tl_job = job_alloc(); + if (term->tl_job == NULL) + goto failed; + ++term->tl_job->jv_refcount; + + /* behave like the job is already finished */ + term->tl_job->jv_status = JOB_FINISHED; + + channel = add_channel(); + if (channel == NULL) + goto failed; + term->tl_job->jv_channel = channel; + channel->ch_keep_open = TRUE; + channel->ch_named_pipe = TRUE; + + channel_set_pipes(channel, + (sock_T)hPipeIn, + (sock_T)hPipeOut, + (sock_T)hPipeOut); + channel_set_job(channel, term->tl_job, options); + term->tl_job->jv_tty_in = vim_strsave((char_u*)in_name); + term->tl_job->jv_tty_out = vim_strsave((char_u*)out_name); + + return OK; + +failed: + if (hPipeIn != NULL) + CloseHandle(hPipeIn); + if (hPipeOut != NULL) + CloseHandle(hPipeOut); return FAIL; } @@ -3234,7 +3310,8 @@ term_free_vterm(term_T *term) static void term_report_winsize(term_T *term, int rows, int cols) { - winpty_set_size(term->tl_winpty, cols, rows, NULL); + if (term->tl_winpty) + winpty_set_size(term->tl_winpty, cols, rows, NULL); } int diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 800567920d..efc491d92b 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -36,11 +36,11 @@ endfunc func Test_terminal_basic() let buf = Run_shell_in_terminal({}) if has("unix") - call assert_match("^/dev/", job_info(g:job).tty) - call assert_match("^/dev/", term_gettty('')) + call assert_match('^/dev/', job_info(g:job).tty_out) + call assert_match('^/dev/', term_gettty('')) else - call assert_match("^winpty://", job_info(g:job).tty) - call assert_match("^winpty://", term_gettty('')) + call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out) + call assert_match('^\\\\.\\pipe\\', term_gettty('')) endif call assert_equal('t', mode()) call assert_match('%aR[^\n]*running]', execute('ls')) @@ -539,10 +539,6 @@ func Test_terminal_write_stdin() endfunc func Test_terminal_no_cmd() - " Todo: make this work on all systems. - if !has('unix') - return - endif " Todo: make this work in the GUI if !has('gui_running') return @@ -550,11 +546,20 @@ func Test_terminal_no_cmd() let buf = term_start('NONE', {}) call assert_notequal(0, buf) - let pty = job_info(term_getjob(buf))['tty'] + let pty = job_info(term_getjob(buf))['tty_out'] call assert_notequal('', pty) - call system('echo "look here" > ' . pty) + if has('win32') + silent exe '!cmd /c "echo look here > ' . pty . '"' + else + call system('echo "look here" > ' . pty) + endif call term_wait(buf) - call assert_equal('look here', term_getline(buf, 1)) + + let result = term_getline(buf, 1) + if has('win32') + let result = substitute(result, '\s\+$', '', '') + endif + call assert_equal('look here', result) bwipe! endfunc @@ -600,6 +605,7 @@ func Test_terminal_redir_file() call WaitFor('len(readfile("Xfile")) > 0') call assert_match('123', readfile('Xfile')[0]) call delete('Xfile') + bwipe endif if has('unix') @@ -608,6 +614,7 @@ func Test_terminal_redir_file() call WaitFor('len(readfile("Xfile")) > 0') call assert_match('executing job failed', readfile('Xfile')[0]) call delete('Xfile') + bwipe call writefile(['one line'], 'Xfile') let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xfile'}) diff --git a/src/version.c b/src/version.c index 9ec37eb667..2ab1b17d3b 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1074, /**/ 1073, /**/ From 1a735d6c9bf4204492b9f9aeba280737fab46ed3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 16:25:54 +0200 Subject: [PATCH 4/6] patch 8.0.1075: MS-Windows: mouse does not work in terminal Problem: MS-Windows: mouse does not work in terminal. Solution: Force the winpty mouse on. (Yasuhiro Matsumoto, closes #2072) --- src/terminal.c | 9 ++++++++- src/version.c | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/terminal.c b/src/terminal.c index 9c4e75948c..eddca8ee70 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -2972,11 +2972,13 @@ term_send_eof(channel_T *ch) #define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul #define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull +#define WINPTY_MOUSE_MODE_FORCE 2 void* (*winpty_config_new)(UINT64, void*); void* (*winpty_open)(void*, void*); void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*); BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*); +void (*winpty_config_set_mouse_mode)(void*, int); void (*winpty_config_set_initial_size)(void*, int, int); LPCWSTR (*winpty_conin_name)(void*); LPCWSTR (*winpty_conout_name)(void*); @@ -3007,7 +3009,10 @@ dyn_winpty_init(int verbose) {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name}, {"winpty_config_free", (FARPROC*)&winpty_config_free}, {"winpty_config_new", (FARPROC*)&winpty_config_new}, - {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size}, + {"winpty_config_set_mouse_mode", + (FARPROC*)&winpty_config_set_mouse_mode}, + {"winpty_config_set_initial_size", + (FARPROC*)&winpty_config_set_initial_size}, {"winpty_conin_name", (FARPROC*)&winpty_conin_name}, {"winpty_conout_name", (FARPROC*)&winpty_conout_name}, {"winpty_error_free", (FARPROC*)&winpty_error_free}, @@ -3105,6 +3110,8 @@ term_and_job_init( if (term->tl_winpty_config == NULL) goto failed; + winpty_config_set_mouse_mode(term->tl_winpty_config, + WINPTY_MOUSE_MODE_FORCE); winpty_config_set_initial_size(term->tl_winpty_config, term->tl_cols, term->tl_rows); term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err); diff --git a/src/version.c b/src/version.c index 2ab1b17d3b..f8a10e3f94 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1075, /**/ 1074, /**/ From 3c518400d1a51929572dd9fcf77dba94d78d7545 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 20:47:00 +0200 Subject: [PATCH 5/6] patch 8.0.1076: term_start() does not take callbacks Problem: term_start() does not take callbacks. When using two terminals without a job only one is read from. A terminal without a window returns the wrong pty. Solution: Support "callback", "out_cb" and "err_cb". Fix terminal without a window. Fix reading from multiple channels. --- src/channel.c | 11 ++++----- src/proto/terminal.pro | 3 ++- src/terminal.c | 56 +++++++++++++++++++++++++++++------------- src/version.c | 2 ++ 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/channel.c b/src/channel.c index c401df2907..dfb8ac989f 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3692,13 +3692,10 @@ channel_send( { res = fd_write(fd, (char *)buf, len); #ifdef WIN32 - if (channel->ch_named_pipe) + if (channel->ch_named_pipe && res < 0) { - if (res < 0) - { - DisconnectNamedPipe((HANDLE)fd); - ConnectNamedPipe((HANDLE)fd, NULL); - } + DisconnectNamedPipe((HANDLE)fd); + ConnectNamedPipe((HANDLE)fd, NULL); } #endif @@ -4084,6 +4081,7 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in) if (ret > 0 && fd != INVALID_FD && FD_ISSET(fd, rfds)) { channel_read(channel, part, "channel_select_check"); + FD_CLR(fd, rfds); --ret; } } @@ -4093,6 +4091,7 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in) && FD_ISSET(in_part->ch_fd, wfds)) { channel_write_input(channel); + FD_CLR(in_part->ch_fd, wfds); --ret; } } diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index 3811ba7a51..639318d518 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -3,6 +3,7 @@ void ex_terminal(exarg_T *eap); void free_terminal(buf_T *buf); void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel); int term_job_running(term_T *term); +int term_none_open(term_T *term); int term_in_normal_mode(void); void term_enter_job_mode(void); int send_keys_to_term(term_T *term, int c, int typed); @@ -16,7 +17,6 @@ int term_update_window(win_T *wp); int term_is_finished(buf_T *buf); int term_show_buffer(buf_T *buf); void term_change_in_curbuf(void); -void term_send_eof(channel_T *ch); int term_get_attr(buf_T *buf, linenr_T lnum, int col); char_u *term_get_status_text(term_T *term); int set_ref_in_term(int copyID); @@ -35,5 +35,6 @@ void f_term_scrape(typval_T *argvars, typval_T *rettv); void f_term_sendkeys(typval_T *argvars, typval_T *rettv); void f_term_start(typval_T *argvars, typval_T *rettv); void f_term_wait(typval_T *argvars, typval_T *rettv); +void term_send_eof(channel_T *ch); int terminal_enabled(void); /* vim: set ft=c : */ diff --git a/src/terminal.c b/src/terminal.c index eddca8ee70..c4313eeabc 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -245,7 +245,11 @@ setup_job_options(jobopt_T *opt, int rows, int cols) opt->jo_term_cols = cols; } - static void +/* + * Start a terminal window and return its buffer. + * Returns NULL when failed. + */ + static buf_T * term_start(typval_T *argvar, jobopt_T *opt, int forceit) { exarg_T split_ea; @@ -253,9 +257,10 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) term_T *term; buf_T *old_curbuf = NULL; int res; + buf_T *newbuf; if (check_restricted() || check_secure()) - return; + return NULL; if ((opt->jo_set & (JO_IN_IO + JO_OUT_IO + JO_ERR_IO)) == (JO_IN_IO + JO_OUT_IO + JO_ERR_IO) @@ -263,12 +268,12 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) || (!(opt->jo_set & JO_ERR_IO) && (opt->jo_set & JO_ERR_BUF))) { EMSG(_(e_invarg)); - return; + return NULL; } term = (term_T *)alloc_clear(sizeof(term_T)); if (term == NULL) - return; + return NULL; term->tl_dirty_row_end = MAX_ROW; term->tl_cursor_visible = TRUE; term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; @@ -283,13 +288,13 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) { no_write_message(); vim_free(term); - return; + return NULL; } if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE, ECMD_HIDE + (forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) { vim_free(term); - return; + return NULL; } } else if (opt->jo_hidden) @@ -303,7 +308,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) if (buf == NULL || ml_open(buf) == FAIL) { vim_free(term); - return; + return NULL; } old_curbuf = curbuf; --curbuf->b_nwindows; @@ -333,7 +338,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) { /* split failed */ vim_free(term); - return; + return NULL; } } term->tl_buffer = curbuf; @@ -419,6 +424,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) else res = term_and_job_init(term, argvar, opt); + newbuf = curbuf; if (res == OK) { /* Get and remember the size we ended up with. Update the pty. */ @@ -453,7 +459,9 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) /* Wiping out the buffer will also close the window and call * free_terminal(). */ do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE); + return NULL; } + return newbuf; } /* @@ -688,7 +696,7 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) update_screen(0); update_cursor(term, TRUE); } - else + else if (buffer->b_nwindows > 0) redraw_after_callback(TRUE); } } @@ -878,6 +886,20 @@ term_job_running(term_T *term) || term->tl_job->jv_channel->ch_keep_open); } +/* + * Return TRUE if "term" has an active channel and used ":term NONE". + */ + int +term_none_open(term_T *term) +{ + /* Also consider the job finished when the channel is closed, to avoid a + * race condition when updating the title. */ + return term != NULL + && term->tl_job != NULL + && channel_is_open(term->tl_job->jv_channel) + && term->tl_job->jv_channel->ch_keep_open; +} + /* * Add the last line of the scrollback buffer to the buffer in the window. */ @@ -2379,6 +2401,8 @@ term_get_status_text(term_T *term) } else if (term->tl_title != NULL) txt = term->tl_title; + else if (term_none_open(term)) + txt = (char_u *)_("active"); else if (term_job_running(term)) txt = (char_u *)_("running"); else @@ -2858,11 +2882,13 @@ f_term_sendkeys(typval_T *argvars, typval_T *rettv) f_term_start(typval_T *argvars, typval_T *rettv) { jobopt_T opt; + buf_T *buf; init_job_options(&opt); if (argvars[1].v_type != VAR_UNKNOWN && get_job_options(&argvars[1], &opt, JO_TIMEOUT_ALL + JO_STOPONEXIT + + JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK + JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_OUT_IO, JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN @@ -2871,10 +2897,10 @@ f_term_start(typval_T *argvars, typval_T *rettv) if (opt.jo_vertical) cmdmod.split = WSP_VERT; - term_start(&argvars[0], &opt, FALSE); + buf = term_start(&argvars[0], &opt, FALSE); - if (curbuf->b_term != NULL) - rettv->vval.v_number = curbuf->b_fnum; + if (buf != NULL && buf->b_term != NULL) + rettv->vval.v_number = buf->b_fnum; } /* @@ -3359,8 +3385,6 @@ term_and_job_init( static int create_pty_only(term_T *term, jobopt_T *opt) { - int ret; - create_vterm(term, term->tl_rows, term->tl_cols); term->tl_job = job_alloc(); @@ -3371,9 +3395,7 @@ create_pty_only(term_T *term, jobopt_T *opt) /* behave like the job is already finished */ term->tl_job->jv_status = JOB_FINISHED; - ret = mch_create_pty_channel(term->tl_job, opt); - - return ret; + return mch_create_pty_channel(term->tl_job, opt); } /* diff --git a/src/version.c b/src/version.c index f8a10e3f94..ad59f5e630 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1076, /**/ 1075, /**/ From fe386641b0c56c5de2bca8e1f4cd5e2a1f1aea7e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 8 Sep 2017 21:10:04 +0200 Subject: [PATCH 6/6] patch 8.0.1077: no debugger making use of the terminal window Problem: No debugger making use of the terminal window. Solution: Add the term debugger plugin. So far only displays the current line when stopped. --- Filelist | 1 + .../dist/opt/termdebug/plugin/termdebug.vim | 109 ++++++++++++++++-- src/version.c | 2 + 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/Filelist b/Filelist index 594b33552a..0587917c36 100644 --- a/Filelist +++ b/Filelist @@ -638,6 +638,7 @@ RT_ALL = \ runtime/pack/dist/opt/matchit/doc/tags \ runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim \ runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim \ + runtime/pack/dist/opt/termdebug/plugin/termdebug.vim \ # runtime files for all distributions without CR-NL translation RT_ALL_BIN = \ diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 97fadf695a..f69990b52b 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -1,35 +1,128 @@ -" Debugger commands. +" Debugger plugin using gdb. " " WORK IN PROGRESS - much doesn't work yet " -" Open two terminal windows: +" Open two visible terminal windows: " 1. run a pty, as with ":term NONE" " 2. run gdb, passing the pty -" The current window is used to edit source code and follows gdb. +" The current window is used to view source code and follows gdb. +" +" A third terminal window is hidden, it is used for communication with gdb. +" +" The communication with gdb uses GDB/MI. See: +" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html " " Author: Bram Moolenaar -" Copyright: Vim license applies +" Copyright: Vim license applies, see ":help license" +" The command that starts debugging, e.g. ":Termdebug vim". +" To end type "quit" in the gdb window. command -nargs=* -complete=file Termdebug call s:StartDebug() +" Name of the gdb command, defaults to "gdb". if !exists('debugger') let debugger = 'gdb' endif +" Sign used to highlight the line where the program has stopped. +sign define debugPC linehl=debugPC +if &background == 'light' + hi debugPC term=reverse ctermbg=lightblue guibg=lightblue +else + hi debugPC term=reverse ctermbg=darkblue guibg=darkblue +endif +let s:pc_id = 12 + func s:StartDebug(cmd) + let s:startwin = win_getid(winnr()) + let s:startsigncolumn = &signcolumn + " Open a terminal window without a job, to run the debugged program - let s:ptybuf = term_start('NONE', {}) - let pty = job_info(term_getjob(s:ptybuf))['tty'] + let s:ptybuf = term_start('NONE', { + \ 'term_name': 'gdb program', + \ }) + if s:ptybuf == 0 + echoerr 'Failed to open the program terminal window' + return + endif + let pty = job_info(term_getjob(s:ptybuf))['tty_out'] + + " Create a hidden terminal window to communicate with gdb + let s:commbuf = term_start('NONE', { + \ 'term_name': 'gdb communication', + \ 'out_cb': function('s:CommOutput'), + \ 'hidden': 1, + \ }) + if s:commbuf == 0 + echoerr 'Failed to open the communication terminal window' + exe 'bwipe! ' . s:ptybuf + return + endif + let commpty = job_info(term_getjob(s:commbuf))['tty_out'] " Open a terminal window to run the debugger. let cmd = [g:debugger, '-tty', pty, a:cmd] echomsg 'executing "' . join(cmd) . '"' let gdbbuf = term_start(cmd, { \ 'exit_cb': function('s:EndDebug'), - \ 'term_finish': 'close' + \ 'term_finish': 'close', \ }) + if gdbbuf == 0 + echoerr 'Failed to open the gdb terminal window' + exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:commbuf + return + endif + + " Connect gdb to the communication pty, using the GDB/MI interface + call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") endfunc func s:EndDebug(job, status) - exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:commbuf + call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn) +endfunc + +" Handle a message received from gdb on the GDB/MI interface. +func s:CommOutput(chan, msg) + let msgs = split(a:msg, "\r") + + for msg in msgs + " remove prefixed NL + if msg[0] == "\n" + let msg = msg[1:] + endif + if msg != '' + if msg =~ '^\*\(stopped\|running\)' + let wid = win_getid(winnr()) + + if win_gotoid(s:startwin) + if msg =~ '^\*stopped' + " TODO: proper parsing + let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '') + let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') + if lnum =~ '^[0-9]*$' + if expand('%:h') != fname + if &modified + " TODO: find existing window + exe 'split ' . fnameescape(fname) + let s:startwin = win_getid(winnr()) + else + exe 'edit ' . fnameescape(fname) + endif + endif + exe lnum + exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) + setlocal signcolumn=yes + endif + else + exe 'sign unplace ' . s:pc_id + endif + + call win_gotoid(wid) + endif + endif + endif + endfor endfunc diff --git a/src/version.c b/src/version.c index ad59f5e630..a54e153b28 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1077, /**/ 1076, /**/