aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Prager <splinterofchaos@gmail.com>2015-04-03 18:44:26 -0400
committerScott Prager <splinterofchaos@gmail.com>2015-04-05 13:39:55 -0400
commitb6296f4e84217adaa3326c715d4e2c82a105bc39 (patch)
treedf9775942fd195a36fdd1c309dbaf5b1b89a89e4
parenta69e464f70f1f26c352b713f70b627ea07232e79 (diff)
downloadrneovim-b6296f4e84217adaa3326c715d4e2c82a105bc39.tar.gz
rneovim-b6296f4e84217adaa3326c715d4e2c82a105bc39.tar.bz2
rneovim-b6296f4e84217adaa3326c715d4e2c82a105bc39.zip
terminal: Handle loss of focus in event loop.
While in a terminal and insert mode, if an event caused loss of focus, nvim would stay in the terminal event loop causing an inconsistent view of internal state and/or segfault. Remove the "term" argument from terminal_enter() as it only makes sense to call it with curbuf->terminal. Terminate the loop when switched to a different buffer. fixes #2301
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/terminal.c15
-rw-r--r--test/functional/terminal/buffer_spec.lua40
3 files changed, 51 insertions, 6 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 8b2ac1943f..c60d987ddd 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -246,7 +246,7 @@ edit (
)
{
if (curbuf->terminal) {
- terminal_enter(curbuf->terminal, true);
+ terminal_enter(true);
return false;
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index daba7b943f..9f4d81be19 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -353,8 +353,11 @@ void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
invalidate_terminal(term, -1, -1);
}
-void terminal_enter(Terminal *term, bool process_deferred)
+void terminal_enter(bool process_deferred)
{
+ Terminal *term = curbuf->terminal;
+ assert(term && "should only be called when curbuf has a terminal");
+
checkpcmark();
setpcmark();
int save_state = State;
@@ -373,7 +376,7 @@ void terminal_enter(Terminal *term, bool process_deferred)
int c;
bool close = false;
- for (;;) {
+ while (term->buf == curbuf) {
if (process_deferred) {
event_enable_deferred();
}
@@ -431,7 +434,7 @@ end:
invalidate_terminal(term, term->cursor.row, term->cursor.row + 1);
mapped_ctrl_c = save_mapped_ctrl_c;
unshowmode(true);
- redraw(false);
+ redraw(term->buf != curbuf);
ui_busy_stop();
if (close) {
term->opts.close_cb(term->opts.data);
@@ -1018,6 +1021,11 @@ static void refresh_screen(Terminal *term)
static void redraw(bool restore_cursor)
{
+ Terminal *term = curbuf->terminal;
+ if (!term) {
+ restore_cursor = true;
+ }
+
int save_row, save_col;
if (restore_cursor) {
// save the current row/col to restore after updating screen when not
@@ -1040,7 +1048,6 @@ static void redraw(bool restore_cursor)
showruler(false);
- Terminal *term = curbuf->terminal;
if (term && is_focused(term)) {
curwin->w_wrow = term->cursor.row;
curwin->w_wcol = term->cursor.col + win_col_off(curwin);
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 857679c4b3..0756508a4c 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -2,7 +2,9 @@ local helpers = require('test.functional.helpers')
local Screen = require('test.functional.ui.screen')
local thelpers = require('test.functional.terminal.helpers')
local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
-local wait, execute, eq = helpers.wait, helpers.execute, helpers.eq
+local wait = helpers.wait
+local eval, execute, source = helpers.eval, helpers.execute, helpers.source
+local eq, neq = helpers.eq, helpers.neq
describe('terminal buffer', function()
@@ -155,5 +157,41 @@ describe('terminal buffer', function()
:bnext |
]])
end)
+
+ it('handles loss of focus gracefully', function()
+ -- Temporarily change the statusline to avoid printing the file name, which
+ -- varies be where the test is run.
+ nvim('set_option', 'statusline', '==========')
+ execute('set laststatus=0')
+
+ -- Save the buffer number of the terminal for later testing.
+ local tbuf = eval('bufnr("%")')
+
+ source([[
+ function! SplitWindow()
+ new
+ endfunction
+
+ startinsert
+ call jobstart(['sh', '-c', 'exit'], {'on_exit': function("SplitWindow")})
+ ]])
+
+ -- We should be in a new buffer now.
+ screen:expect([[
+ ^ |
+ ~ |
+ ========== |
+ rows: 2, cols: 50 |
+ {2: } |
+ {1:========== }|
+ |
+ ]])
+
+ neq(tbuf, eval('bufnr("%")'))
+ execute('quit') -- Should exit the new window, not the terminal.
+ eq(tbuf, eval('bufnr("%")'))
+
+ execute('set laststatus=1') -- Restore laststatus to the default.
+ end)
end)