summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-05-11 22:01:51 +0200
committerBram Moolenaar <Bram@vim.org>2018-05-11 22:01:51 +0200
commit0cb8ac71ae42f66d525ad855db01361ca38d935a (patch)
treeb92519c8cf47222d59efc7c78bb4f47005f6e041
parenta10ae5e323f4ebe6620869f4f9b0812b78090915 (diff)
downloadvim-git-0cb8ac71ae42f66d525ad855db01361ca38d935a.tar.gz
patch 8.0.1815: crash with terminal window and with 'lazyredraw' setv8.0.1815
Problem: Still a crash with terminal window and with 'lazyredraw' set. (Antoine) Solution: Do not wipe out the buffer when updating the screen.
-rw-r--r--src/proto/screen.pro1
-rw-r--r--src/proto/terminal.pro1
-rw-r--r--src/screen.c22
-rw-r--r--src/terminal.c119
-rw-r--r--src/ui.c5
-rw-r--r--src/version.c2
6 files changed, 108 insertions, 42 deletions
diff --git a/src/proto/screen.pro b/src/proto/screen.pro
index f690f93d6..5760dae1b 100644
--- a/src/proto/screen.pro
+++ b/src/proto/screen.pro
@@ -9,6 +9,7 @@ void redraw_buf_and_status_later(buf_T *buf, int type);
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
index 93c6ab53c..25f9647f2 100644
--- 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
index cac5a3a1f..841dc1866 100644
--- 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
index 8deac9da8..1991f382f 100644
--- 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_callbacks = {
};
/*
+ * 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_callbacks = {
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
index 05e82e6c7..8e3f0deee 100644
--- 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
index 918e19274..6346e6217 100644
--- 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,