# HG changeset patch # User Christian Brabandt # Date 1526069706 -7200 # Node ID 81e8e6181aeb1334ab0367cd22e8277628f8ad59 # Parent f5a2d0eb992627d1ffde1cdd52cc04f3f3511e48 patch 8.0.1815: crash with terminal window and with 'lazyredraw' set commit https://github.com/vim/vim/commit/0cb8ac71ae42f66d525ad855db01361ca38d935a Author: Bram Moolenaar Date: Fri May 11 22:01:51 2018 +0200 patch 8.0.1815: crash with terminal window and with 'lazyredraw' set Problem: Still a crash with terminal window and with 'lazyredraw' set. (Antoine) Solution: Do not wipe out the buffer when updating the screen. diff --git a/src/proto/screen.pro b/src/proto/screen.pro --- a/src/proto/screen.pro +++ b/src/proto/screen.pro @@ -9,6 +9,7 @@ void redraw_buf_and_status_later(buf_T * int redraw_asap(int type); void redraw_after_callback(int call_update_screen); void redrawWinline(linenr_T lnum, int invalid); +void reset_updating_screen(int may_resize_shell); void update_curbuf(int type); int update_screen(int type_arg); int conceal_cursor_line(win_T *wp); diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -20,6 +20,7 @@ void term_win_entered(void); int terminal_loop(int blocking); void term_job_ended(job_T *job); void term_channel_closed(channel_T *ch); +void term_check_channel_closed_recently(void); int term_do_update_window(win_T *wp); void term_update_window(win_T *wp); int term_is_finished(buf_T *buf); diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -514,6 +514,19 @@ redrawWinline( #endif } + void +reset_updating_screen(int may_resize_shell UNUSED) +{ + updating_screen = FALSE; +#ifdef FEAT_GUI + if (may_resize_shell) + gui_may_resize_shell(); +#endif +#ifdef FEAT_TERMINAL + term_check_channel_closed_recently(); +#endif +} + /* * Update all windows that are editing the current buffer. */ @@ -778,10 +791,7 @@ update_screen(int type_arg) FOR_ALL_WINDOWS(wp) wp->w_buffer->b_mod_set = FALSE; - updating_screen = FALSE; -#ifdef FEAT_GUI - gui_may_resize_shell(); -#endif + reset_updating_screen(TRUE); /* Clear or redraw the command line. Done last, because scrolling may * mess up the command line. */ @@ -861,11 +871,9 @@ update_finish(void) end_search_hl(); # endif - updating_screen = FALSE; + reset_updating_screen(TRUE); # ifdef FEAT_GUI - gui_may_resize_shell(); - /* Redraw the cursor and update the scrollbars when all screen updating is * done. */ if (gui.in_use) diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -103,6 +103,8 @@ struct terminal_S { int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_channel_closed; + int tl_channel_recently_closed; // still need to handle tl_finish + int tl_finish; #define TL_FINISH_UNSET NUL #define TL_FINISH_CLOSE 'c' /* ++close or :terminal without argument */ @@ -2780,6 +2782,53 @@ static VTermScreenCallbacks screen_callb }; /* + * Do the work after the channel of a terminal was closed. + * Must be called only when updating_screen is FALSE. + * Returns TRUE when a buffer was closed (list of terminals may have changed). + */ + static int +term_after_channel_closed(term_T *term) +{ + /* Unless in Terminal-Normal mode: clear the vterm. */ + if (!term->tl_normal_mode) + { + int fnum = term->tl_buffer->b_fnum; + + cleanup_vterm(term); + + if (term->tl_finish == TL_FINISH_CLOSE) + { + aco_save_T aco; + + /* ++close or term_finish == "close" */ + ch_log(NULL, "terminal job finished, closing window"); + aucmd_prepbuf(&aco, term->tl_buffer); + do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE); + aucmd_restbuf(&aco); + return TRUE; + } + if (term->tl_finish == TL_FINISH_OPEN + && term->tl_buffer->b_nwindows == 0) + { + char buf[50]; + + /* TODO: use term_opencmd */ + ch_log(NULL, "terminal job finished, opening window"); + vim_snprintf(buf, sizeof(buf), + term->tl_opencmd == NULL + ? "botright sbuf %d" + : (char *)term->tl_opencmd, fnum); + do_cmdline_cmd((char_u *)buf); + } + else + ch_log(NULL, "terminal job finished"); + } + + redraw_buf_and_status_later(term->tl_buffer, NOT_VALID); + return FALSE; +} + +/* * Called when a channel has been closed. * If this was a channel for a terminal window then finish it up. */ @@ -2787,9 +2836,12 @@ static VTermScreenCallbacks screen_callb term_channel_closed(channel_T *ch) { term_T *term; + term_T *next_term; int did_one = FALSE; - for (term = first_term; term != NULL; term = term->tl_next) + for (term = first_term; term != NULL; term = next_term) + { + next_term = term->tl_next; if (term->tl_job == ch->ch_job) { term->tl_channel_closed = TRUE; @@ -2805,43 +2857,19 @@ term_channel_closed(channel_T *ch) } #endif - /* Unless in Terminal-Normal mode: clear the vterm. */ - if (!term->tl_normal_mode) + if (updating_screen) { - int fnum = term->tl_buffer->b_fnum; - - cleanup_vterm(term); - - if (term->tl_finish == TL_FINISH_CLOSE) - { - aco_save_T aco; - - /* ++close or term_finish == "close" */ - ch_log(NULL, "terminal job finished, closing window"); - aucmd_prepbuf(&aco, term->tl_buffer); - do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE); - aucmd_restbuf(&aco); - break; - } - if (term->tl_finish == TL_FINISH_OPEN - && term->tl_buffer->b_nwindows == 0) - { - char buf[50]; - - /* TODO: use term_opencmd */ - ch_log(NULL, "terminal job finished, opening window"); - vim_snprintf(buf, sizeof(buf), - term->tl_opencmd == NULL - ? "botright sbuf %d" - : (char *)term->tl_opencmd, fnum); - do_cmdline_cmd((char_u *)buf); - } - else - ch_log(NULL, "terminal job finished"); + /* Cannot open or close windows now. Can happen when + * 'lazyredraw' is set. */ + term->tl_channel_recently_closed = TRUE; + continue; } - redraw_buf_and_status_later(term->tl_buffer, NOT_VALID); + if (term_after_channel_closed(term)) + next_term = first_term; } + } + if (did_one) { redraw_statuslines(); @@ -2861,6 +2889,29 @@ term_channel_closed(channel_T *ch) } /* + * To be called after resetting updating_screen: handle any terminal where the + * channel was closed. + */ + void +term_check_channel_closed_recently() +{ + term_T *term; + term_T *next_term; + + for (term = first_term; term != NULL; term = next_term) + { + next_term = term->tl_next; + if (term->tl_channel_recently_closed) + { + term->tl_channel_recently_closed = FALSE; + if (term_after_channel_closed(term)) + // start over, the list may have changed + next_term = first_term; + } + } +} + +/* * Fill one screen line from a line of the terminal. * Advances "pos" to past the last column. */ diff --git a/src/ui.c b/src/ui.c --- a/src/ui.c +++ b/src/ui.c @@ -415,7 +415,10 @@ ui_breakcheck_force(int force) #endif mch_breakcheck(force); - updating_screen = save_us; + if (save_us) + updating_screen = save_us; + else + reset_updating_screen(FALSE); } /***************************************************************************** diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -762,6 +762,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1815, +/**/ 1814, /**/ 1813,