terminal: Follow output only if cursor is at end.

Closes #2257
Closes #2636
References #2683
This commit is contained in:
Justin M. Keyes 2017-02-21 15:06:44 +01:00
parent e7bbd35c81
commit 4ceec30cd0
3 changed files with 27 additions and 14 deletions

View File

@ -370,8 +370,7 @@ void terminal_enter(void)
State = TERM_FOCUS; State = TERM_FOCUS;
mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt. mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt.
RedrawingDisabled = false; RedrawingDisabled = false;
// go to the bottom when the terminal is focused adjust_topline(s->term, buf, 0); // scroll to end
adjust_topline(s->term, buf, false);
// erase the unfocused cursor // erase the unfocused cursor
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1); invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
showmode(); showmode();
@ -966,14 +965,15 @@ static void refresh_terminal(Terminal *term)
} }
return; return;
} }
bool pending_resize = term->pending_resize; long ml_before = buf->b_ml.ml_line_count;
WITH_BUFFER(buf, { WITH_BUFFER(buf, {
refresh_size(term, buf); refresh_size(term, buf);
refresh_scrollback(term, buf); refresh_scrollback(term, buf);
refresh_screen(term, buf); refresh_screen(term, buf);
redraw_buf_later(buf, NOT_VALID); redraw_buf_later(buf, NOT_VALID);
}); });
adjust_topline(term, buf, pending_resize); long ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
} }
// Calls refresh_terminal() on all invalidated_terminals. // Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data) static void refresh_timer_cb(TimeWatcher *watcher, void *data)
@ -1153,21 +1153,21 @@ static void redraw(bool restore_cursor)
ui_flush(); ui_flush();
} }
static void adjust_topline(Terminal *term, buf_T *buf, bool force) static void adjust_topline(Terminal *term, buf_T *buf, long added)
{ {
int height, width; int height, width;
vterm_get_size(term->vt, &height, &width); vterm_get_size(term->vt, &height, &width);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf) { if (wp->w_buffer == buf) {
// for every window that displays a terminal, ensure the cursor is in a linenr_T ml_end = buf->b_ml.ml_line_count;
// valid line bool following = ml_end == wp->w_cursor.lnum + added; // cursor at end?
wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, buf->b_ml.ml_line_count); if (following || (wp == curwin && is_focused(term))) {
if (force || curbuf != buf || is_focused(term)) { // "Follow" the terminal output
// if the terminal is not in the current window or if it's focused, wp->w_cursor.lnum = ml_end;
// adjust topline/cursor so the window will "follow" the terminal
// output
wp->w_cursor.lnum = buf->b_ml.ml_line_count;
set_topline(wp, MAX(wp->w_cursor.lnum - height + 1, 1)); set_topline(wp, MAX(wp->w_cursor.lnum - height + 1, 1));
} else {
// Ensure valid cursor for each window displaying this terminal.
wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end);
} }
} }
} }

View File

@ -38,7 +38,8 @@ describe(':edit term://*', function()
meths.set_option('shellcmdflag', 'REP ' .. rep) meths.set_option('shellcmdflag', 'REP ' .. rep)
local rep_size = rep:byte() -- 'a' => 97 local rep_size = rep:byte() -- 'a' => 97
local sb = 10 local sb = 10
command('autocmd TermOpen * :setlocal scrollback='..tostring(sb)) command('autocmd TermOpen * :setlocal scrollback='..tostring(sb)
..'|call feedkeys("G", "n")')
command('edit term://foobar') command('edit term://foobar')
local bufcontents = {} local bufcontents = {}

View File

@ -36,6 +36,18 @@ describe(':terminal', function()
]]) ]])
end) end)
it("in normal-mode :split does not move cursor", function()
execute([[terminal while true; do echo foo; sleep .1; done]])
helpers.feed([[<C-\><C-N>M]]) -- move cursor away from last line
wait()
eq(3, eval("line('$')")) -- window height
eq(2, eval("line('.')")) -- cursor is in the middle
execute('vsplit')
eq(2, eval("line('.')")) -- cursor stays where we put it
execute('split')
eq(2, eval("line('.')")) -- cursor stays where we put it
end)
end) end)
describe(':terminal (with fake shell)', function() describe(':terminal (with fake shell)', function()