changeset 13888:81e8e6181aeb v8.0.1815

patch 8.0.1815: crash with terminal window and with 'lazyredraw' set commit https://github.com/vim/vim/commit/0cb8ac71ae42f66d525ad855db01361ca38d935a Author: Bram Moolenaar <Bram@vim.org> 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.
author Christian Brabandt <cb@256bit.org>
date Fri, 11 May 2018 22:15:06 +0200
parents f5a2d0eb9926
children c50f74ab8035
files src/proto/screen.pro src/proto/terminal.pro src/screen.c src/terminal.c src/ui.c src/version.c
diffstat 6 files changed, 108 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- 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);
--- 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);
--- 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)
--- 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.
  */
--- 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);
 }
 
 /*****************************************************************************
--- 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,