diff options
-rw-r--r-- | src/nvim/message.c | 93 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 3 | ||||
-rw-r--r-- | src/nvim/screen.c | 6 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/ui/messages_spec.lua | 311 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 6 |
6 files changed, 369 insertions, 54 deletions
diff --git a/src/nvim/message.c b/src/nvim/message.c index b5a5f656a0..c499aa5f0c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -150,10 +150,16 @@ void msg_grid_set_pos(int row, bool scrolled) } } +bool msg_use_grid(void) +{ + return default_grid.chars && msg_use_msgsep() + && !ui_has(kUIMessages); +} + void msg_grid_validate(void) { grid_assign_handle(&msg_grid); - bool should_alloc = msg_dothrottle(); + bool should_alloc = msg_use_grid(); if (should_alloc && (msg_grid.Rows != Rows || msg_grid.Columns != Columns || !msg_grid.chars)) { // TODO(bfredl): eventually should be set to "invalid". I e all callers @@ -2027,7 +2033,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, // Tricky: if last cell will be written, delay the throttle until // after the first scroll. Otherwise we would need to keep track of it. - if (has_last_char && msg_dothrottle()) { + if (has_last_char && msg_do_throttle()) { if (!msg_grid.throttled) { msg_grid_scroll_discount++; } @@ -2158,12 +2164,6 @@ int msg_scrollsize(void) return msg_scrolled + p_ch + 1; } -bool msg_dothrottle(void) -{ - return default_grid.chars && msg_use_msgsep() - && !ui_has(kUIMessages); -} - bool msg_use_msgsep(void) { // the full-screen scroll behavior doesn't really make sense with @@ -2171,12 +2171,15 @@ bool msg_use_msgsep(void) return ((dy_flags & DY_MSGSEP) || ui_has(kUIMultigrid)); } -/* - * Scroll the screen up one line for displaying the next message line. - */ +bool msg_do_throttle(void) +{ + return msg_use_grid() && !(rdb_flags & RDB_NOTHROTTLE); +} + +/// Scroll the screen up one line for displaying the next message line. void msg_scroll_up(bool may_throttle) { - if (may_throttle && msg_dothrottle()) { + if (may_throttle && msg_do_throttle()) { msg_grid.throttled = true; } msg_did_scroll = true; @@ -2214,37 +2217,36 @@ void msg_scroll_up(bool may_throttle) /// we get throttling "for free" using standard redraw_win_later code paths. void msg_scroll_flush(void) { - if (!msg_grid.throttled) { - return; - } - msg_grid.throttled = false; - int pos_delta = msg_grid_pos_at_flush - msg_grid_pos; - assert(pos_delta >= 0); - int delta = MIN(msg_scrolled - msg_scrolled_at_flush, msg_grid.Rows); + if (msg_grid.throttled) { + msg_grid.throttled = false; + int pos_delta = msg_grid_pos_at_flush - msg_grid_pos; + assert(pos_delta >= 0); + int delta = MIN(msg_scrolled - msg_scrolled_at_flush, msg_grid.Rows); - if (pos_delta > 0) { - ui_ext_msg_set_pos(msg_grid_pos, true); - msg_grid_pos_at_flush = msg_grid_pos; - } + if (pos_delta > 0) { + ui_ext_msg_set_pos(msg_grid_pos, true); + } - int to_scroll = delta-pos_delta-msg_grid_scroll_discount; - assert(to_scroll >= 0); + int to_scroll = delta-pos_delta-msg_grid_scroll_discount; + assert(to_scroll >= 0); - // TODO(bfredl): msg_grid_pos should be 0 already when starting scrolling - // but this sometimes fails in "headless" message printing. - if (to_scroll > 0 && msg_grid_pos == 0) { - ui_call_grid_scroll(msg_grid.handle, 0, Rows, 0, Columns, to_scroll, 0); - } + // TODO(bfredl): msg_grid_pos should be 0 already when starting scrolling + // but this sometimes fails in "headless" message printing. + if (to_scroll > 0 && msg_grid_pos == 0) { + ui_call_grid_scroll(msg_grid.handle, 0, Rows, 0, Columns, to_scroll, 0); + } - for (int i = MAX(Rows-MAX(delta, 1), 0); i < Rows; i++) { - int row = i-msg_grid_pos; - assert(row >= 0); - ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.Columns, - HL_ATTR(HLF_MSG), false); - msg_grid.dirty_col[row] = 0; + for (int i = MAX(Rows-MAX(delta, 1), 0); i < Rows; i++) { + int row = i-msg_grid_pos; + assert(row >= 0); + ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.Columns, + HL_ATTR(HLF_MSG), false); + msg_grid.dirty_col[row] = 0; + } } msg_scrolled_at_flush = msg_scrolled; msg_grid_scroll_discount = 0; + msg_grid_pos_at_flush = msg_grid_pos; } void msg_reset_scroll(void) @@ -2255,7 +2257,7 @@ void msg_reset_scroll(void) } // TODO(bfredl): some duplicate logic with update_screen(). Later on // we should properly disentangle message clear with full screen redraw. - if (msg_dothrottle()) { + if (msg_use_grid()) { msg_grid.throttled = false; // TODO(bfredl): risk for extra flicker i e with // "nvim -o has_swap also_has_swap" @@ -2462,10 +2464,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) mp = mp->sb_next; } - if (msg_col < Columns) { - grid_fill(&msg_grid_adj, row, row+1, msg_col, Columns, ' ', ' ', - HL_ATTR(HLF_MSG)); - } return mp->sb_next; } @@ -2697,12 +2695,16 @@ static int do_more_prompt(int typed_char) if (toscroll == -1) { grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); + grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ', + HL_ATTR(HLF_MSG)); // display line at top (void)disp_sb_line(0, mp); } else { // redisplay all lines // TODO(bfredl): this case is not optimized (though only concerns // event fragmentization, not unnecessary scroll events). + grid_fill(&msg_grid_adj, 0, Rows, 0, Columns, ' ', ' ', + HL_ATTR(HLF_MSG)); for (i = 0; mp != NULL && i < Rows - 1; i++) { mp = disp_sb_line(i, mp); ++msg_scrolled; @@ -2713,7 +2715,7 @@ static int do_more_prompt(int typed_char) } else { /* First display any text that we scrolled back. */ while (toscroll > 0 && mp_last != NULL) { - if (msg_dothrottle() && !msg_grid.throttled) { + if (msg_do_throttle() && !msg_grid.throttled) { // Tricky: we redraw at one line higher than usual. Therefore // the non-flushed area is one line larger. msg_scrolled_at_flush--; @@ -2722,6 +2724,8 @@ static int do_more_prompt(int typed_char) // scroll up, display line at bottom msg_scroll_up(true); inc_msg_scrolled(); + grid_fill(&msg_grid_adj, Rows-2, Rows-1, 0, Columns, ' ', ' ', + HL_ATTR(HLF_MSG)); mp_last = disp_sb_line(Rows - 2, mp_last); toscroll--; } @@ -2743,7 +2747,8 @@ static int do_more_prompt(int typed_char) } // clear the --more-- message - grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', 0); + grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', + HL_ATTR(HLF_MSG)); redraw_cmdline = true; clear_cmdline = false; mode_displayed = false; @@ -2817,7 +2822,7 @@ void msg_moremsg(int full) int attr; char_u *s = (char_u *)_("-- More --"); - attr = HL_ATTR(HLF_M); + attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr); if (full) { grid_puts(&msg_grid_adj, (char_u *) diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index d846bd0ae0..94f98af524 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -518,9 +518,10 @@ EXTERN long p_pyx; // 'pyxversion' EXTERN char_u *p_rdb; // 'redrawdebug' EXTERN unsigned rdb_flags; # ifdef IN_OPTION_C -static char *(p_rdb_values[]) = { "compositor", NULL }; +static char *(p_rdb_values[]) = { "compositor", "nothrottle", NULL }; # endif # define RDB_COMPOSITOR 0x001 +# define RDB_NOTHROTTLE 0x002 EXTERN long p_rdt; // 'redrawtime' EXTERN int p_remap; // 'remap' diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 13eb088813..5bcd2c808d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -404,7 +404,9 @@ int update_screen(int type) default_grid.valid = true; } - if (type == NOT_VALID && (msg_dothrottle() || msg_grid.chars)) { + // After disabling msgsep the grid might not have been deallocated yet, + // hence we also need to check msg_grid.chars + if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) { grid_fill(&default_grid, Rows-p_ch, Rows, 0, Columns, ' ', ' ', 0); } @@ -6250,7 +6252,7 @@ void screenclear(void) msg_scrolled = 0; // can't scroll back msg_didany = false; msg_didout = false; - if (HL_ATTR(HLF_MSG) > 0 && msg_dothrottle() && msg_grid.chars) { + if (HL_ATTR(HLF_MSG) > 0 && msg_use_grid() && msg_grid.chars) { grid_invalidate(&msg_grid); msg_grid_validate(); msg_grid_invalid = false; diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 99b25cbf48..6c7d992f40 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -132,7 +132,7 @@ describe('TUI', function() -- TODO(bfredl): messes up the output (just like vim does). feed_data('g') screen:expect{grid=[[ - {8:FAIL 1} ) | + ) | {8:Error detected while processing function ManyErr:} | {11:line 2:} | {10:-- More --}{1: } | @@ -141,7 +141,7 @@ describe('TUI', function() screen:try_resize(50,10) screen:expect{grid=[[ - {8:FAIL 1} ) | + ) | {8:Error detected while processing function ManyErr:} | {11:line 2:} | {10:-- More --} | diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index a6b9ef9387..90874a92ef 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -10,6 +10,7 @@ local test_build_dir = helpers.test_build_dir local nvim_prog = helpers.nvim_prog local iswin = helpers.iswin local exc_exec = helpers.exc_exec +local exec_lua = helpers.exec_lua describe('ui/ext_messages', function() local screen @@ -1058,16 +1059,34 @@ describe('pager', function() before_each(function() clear() - screen = Screen.new(25, 5) + screen = Screen.new(35, 8) screen:attach() screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red, special=Screen.colors.Yellow}, [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [5] = {special = Screen.colors.Yellow}, + [6] = {special = Screen.colors.Yellow, bold = true, foreground = Screen.colors.SeaGreen4}, + [7] = {foreground = Screen.colors.Grey0, background = Screen.colors.Grey100}, + [8] = {foreground = Screen.colors.Gray90, background = Screen.colors.Grey100}, + [9] = {foreground = tonumber('0x00000c'), background = Screen.colors.Grey100}, + [10] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff')}, + [11] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber ('0x2b8452')}, }) + command("set more") + + exec_lua('_G.x = ...', [[ +Lorem ipsum dolor sit amet, consectetur +adipisicing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud xercitation +ullamco laboris nisi ut +aliquip ex ea commodo consequat.]]) end) it('can be quit', function() - command("set more") + screen:try_resize(25,5) feed(':echon join(map(range(0, &lines*2), "v:val"), "\\n")<cr>') screen:expect{grid=[[ 0 | @@ -1085,4 +1104,292 @@ describe('pager', function() | ]]} end) + + it('handles wrapped lines with line scroll', function() + feed(':lua error(_G.x)<cr>') + screen:expect{grid=[[ + {2:E5105: Error while calling lua chun}| + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {4:-- More --}^ | + ]]} + + feed('j') + screen:expect{grid=[[ + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {2:a aliqua.} | + {4:-- More --}^ | + ]]} + + feed('k') + screen:expect{grid=[[ + {2:E5105: Error while calling lua chun}| + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {4:-- More --}^ | + ]]} + + feed('j') + screen:expect{grid=[[ + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {2:a aliqua.} | + {4:-- More --}^ | + ]]} + + end) + + it('handles wrapped lines with page scroll', function() + feed(':lua error(_G.x)<cr>') + screen:expect{grid=[[ + {2:E5105: Error while calling lua chun}| + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {4:-- More --}^ | + ]]} + feed('d') + screen:expect{grid=[[ + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {2:a aliqua.} | + {2:Ut enim ad minim veniam, quis nostr}| + {2:ud xercitation} | + {2:ullamco laboris nisi ut} | + {4:-- More --}^ | + ]]} + feed('u') + screen:expect{grid=[[ + {2:E5105: Error while calling lua chun}| + {2:k: [string "<VimL compiled string>"}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {4:-- More --}^ | + ]]} + feed('d') + screen:expect{grid=[[ + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {2:a aliqua.} | + {2:Ut enim ad minim veniam, quis nostr}| + {2:ud xercitation} | + {2:ullamco laboris nisi ut} | + {4:-- More --}^ | + ]]} + end) + + it('handles wrapped lines with line scroll and MsgArea highlight', function() + command("hi MsgArea guisp=Yellow") + + feed(':lua error(_G.x)<cr>') + screen:expect{grid=[[ + {3:E5105: Error while calling lua chun}| + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {6:-- More --}{5:^ }| + ]]} + + feed('j') + screen:expect{grid=[[ + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {3:a aliqua.}{5: }| + {6:-- More --}{5:^ }| + ]]} + + feed('k') + screen:expect{grid=[[ + {3:E5105: Error while calling lua chun}| + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {6:-- More --}{5:^ }| + ]]} + + feed('j') + screen:expect{grid=[[ + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {3:a aliqua.}{5: }| + {6:-- More --}{5:^ }| + ]]} + end) + + it('handles wrapped lines with page scroll and MsgArea highlight', function() + command("hi MsgArea guisp=Yellow") + feed(':lua error(_G.x)<cr>') + screen:expect{grid=[[ + {3:E5105: Error while calling lua chun}| + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {6:-- More --}{5:^ }| + ]]} + feed('d') + screen:expect{grid=[[ + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {3:a aliqua.}{5: }| + {3:Ut enim ad minim veniam, quis nostr}| + {3:ud xercitation}{5: }| + {3:ullamco laboris nisi ut}{5: }| + {6:-- More --}{5:^ }| + ]]} + feed('u') + screen:expect{grid=[[ + {3:E5105: Error while calling lua chun}| + {3:k: [string "<VimL compiled string>"}| + {3:]:1: Lorem ipsum dolor sit amet, co}| + {3:nsectetur}{5: }| + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {6:-- More --}{5:^ }| + ]]} + feed('d') + screen:expect{grid=[[ + {3:adipisicing elit, sed do eiusmod te}| + {3:mpor}{5: }| + {3:incididunt ut labore et dolore magn}| + {3:a aliqua.}{5: }| + {3:Ut enim ad minim veniam, quis nostr}| + {3:ud xercitation}{5: }| + {3:ullamco laboris nisi ut}{5: }| + {6:-- More --}{5:^ }| + ]]} + end) + + it('preserves MsgArea highlighting after more prompt', function() + screen:try_resize(70,6) + command("hi MsgArea guisp=Yellow") + command("map x Lorem ipsum labore et dolore magna aliqua") + command("map y adipisicing elit") + command("map z incididunt ut") + command("map a labore et dolore") + command("map b ex ea commodo") + command("map xx yy") + command("map xy yz") + feed(':map<cr>') + screen:expect{grid=[[ + {5: a labore et dolore }| + {5: b ex ea commodo }| + {5: xy yz }| + {5: xx yy }| + {5: x Lorem ipsum labore et dolore magna aliqua }| + {6:-- More --}{5:^ }| + ]]} + feed('j') + screen:expect{grid=[[ + {5: b ex ea commodo }| + {5: xy yz }| + {5: xx yy }| + {5: x Lorem ipsum labore et dolore magna aliqua }| + {5: y adipisicing elit }| + {6:-- More --}{5:^ }| + ]]} + feed('j') + screen:expect{grid=[[ + {5: xy yz }| + {5: xx yy }| + {5: x Lorem ipsum labore et dolore magna aliqua }| + {5: y adipisicing elit }| + {5: z incididunt ut }| + {6:Press ENTER or type command to continue}{5:^ }| + ]]} + end) + + it('clears "-- more --" message', function() + command("hi MsgArea guisp=Yellow blend=10") + feed(':echon join(range(20), "\\n")<cr>') + screen:expect{grid=[[ + {7:0}{8: }| + {9:1}{10: }| + {9:2}{10: }| + {9:3}{10: }| + {9:4}{10: }| + {9:5}{10: }| + {9:6}{10: }| + {11:--}{8: }{11:More}{8: }{11:--}{8:^ }| + ]]} + + feed('j') + screen:expect{grid=[[ + {7:1}{8: }| + {9:2}{10: }| + {9:3}{10: }| + {9:4}{10: }| + {9:5}{10: }| + {9:6}{10: }| + {9:7}{10: }| + {11:--}{8: }{11:More}{8: }{11:--}{8:^ }| + ]]} + + feed('k') + screen:expect{grid=[[ + {7:0}{8: }{7:)}{8: }| + {9:1}{10: }| + {9:2}{10: }| + {9:3}{10: }| + {9:4}{10: }| + {9:5}{10: }| + {9:6}{10: }| + {11:--}{8: }{11:More}{8: }{11:--}{8:^ }| + ]]} + + feed('j') + screen:expect{grid=[[ + {7:1}{8: }| + {9:2}{10: }| + {9:3}{10: }| + {9:4}{10: }| + {9:5}{10: }| + {9:6}{10: }| + {9:7}{10: }| + {11:--}{8: }{11:More}{8: }{11:--}{8:^ }| + ]]} + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 0e3b903398..12abfb2ad9 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -859,7 +859,7 @@ function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols) -- clear invalid rows for i = stop + step, stop + rows, step do - self:_clear_row_section(grid, i, left, right) + self:_clear_row_section(grid, i, left, right, true) end end @@ -1065,10 +1065,10 @@ function Screen:_clear_block(grid, top, bot, left, right) end end -function Screen:_clear_row_section(grid, rownum, startcol, stopcol) +function Screen:_clear_row_section(grid, rownum, startcol, stopcol, invalid) local row = grid.rows[rownum] for i = startcol, stopcol do - row[i].text = ' ' + row[i].text = (invalid and '�' or ' ') row[i].attrs = self._clear_attrs end end |