Merge pull request #10930 from bfredl/pagerthrottle

fixes for pager glitches and crashes
This commit is contained in:
Björn Linse
2019-09-06 20:28:26 +02:00
committed by GitHub
6 changed files with 369 additions and 54 deletions

View File

@@ -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) void msg_grid_validate(void)
{ {
grid_assign_handle(&msg_grid); 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 if (should_alloc && (msg_grid.Rows != Rows || msg_grid.Columns != Columns
|| !msg_grid.chars)) { || !msg_grid.chars)) {
// TODO(bfredl): eventually should be set to "invalid". I e all callers // 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 // Tricky: if last cell will be written, delay the throttle until
// after the first scroll. Otherwise we would need to keep track of it. // 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) { if (!msg_grid.throttled) {
msg_grid_scroll_discount++; msg_grid_scroll_discount++;
} }
@@ -2158,12 +2164,6 @@ int msg_scrollsize(void)
return msg_scrolled + p_ch + 1; 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) bool msg_use_msgsep(void)
{ {
// the full-screen scroll behavior doesn't really make sense with // 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)); return ((dy_flags & DY_MSGSEP) || ui_has(kUIMultigrid));
} }
/* bool msg_do_throttle(void)
* Scroll the screen up one line for displaying the next message line. {
*/ 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) void msg_scroll_up(bool may_throttle)
{ {
if (may_throttle && msg_dothrottle()) { if (may_throttle && msg_do_throttle()) {
msg_grid.throttled = true; msg_grid.throttled = true;
} }
msg_did_scroll = true; msg_did_scroll = true;
@@ -2214,9 +2217,7 @@ void msg_scroll_up(bool may_throttle)
/// we get throttling "for free" using standard redraw_win_later code paths. /// we get throttling "for free" using standard redraw_win_later code paths.
void msg_scroll_flush(void) void msg_scroll_flush(void)
{ {
if (!msg_grid.throttled) { if (msg_grid.throttled) {
return;
}
msg_grid.throttled = false; msg_grid.throttled = false;
int pos_delta = msg_grid_pos_at_flush - msg_grid_pos; int pos_delta = msg_grid_pos_at_flush - msg_grid_pos;
assert(pos_delta >= 0); assert(pos_delta >= 0);
@@ -2224,7 +2225,6 @@ void msg_scroll_flush(void)
if (pos_delta > 0) { if (pos_delta > 0) {
ui_ext_msg_set_pos(msg_grid_pos, true); ui_ext_msg_set_pos(msg_grid_pos, true);
msg_grid_pos_at_flush = msg_grid_pos;
} }
int to_scroll = delta-pos_delta-msg_grid_scroll_discount; int to_scroll = delta-pos_delta-msg_grid_scroll_discount;
@@ -2243,8 +2243,10 @@ void msg_scroll_flush(void)
HL_ATTR(HLF_MSG), false); HL_ATTR(HLF_MSG), false);
msg_grid.dirty_col[row] = 0; msg_grid.dirty_col[row] = 0;
} }
}
msg_scrolled_at_flush = msg_scrolled; msg_scrolled_at_flush = msg_scrolled;
msg_grid_scroll_discount = 0; msg_grid_scroll_discount = 0;
msg_grid_pos_at_flush = msg_grid_pos;
} }
void msg_reset_scroll(void) void msg_reset_scroll(void)
@@ -2255,7 +2257,7 @@ void msg_reset_scroll(void)
} }
// TODO(bfredl): some duplicate logic with update_screen(). Later on // TODO(bfredl): some duplicate logic with update_screen(). Later on
// we should properly disentangle message clear with full screen redraw. // we should properly disentangle message clear with full screen redraw.
if (msg_dothrottle()) { if (msg_use_grid()) {
msg_grid.throttled = false; msg_grid.throttled = false;
// TODO(bfredl): risk for extra flicker i e with // TODO(bfredl): risk for extra flicker i e with
// "nvim -o has_swap also_has_swap" // "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; 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; return mp->sb_next;
} }
@@ -2697,12 +2695,16 @@ static int do_more_prompt(int typed_char)
if (toscroll == -1) { if (toscroll == -1) {
grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); 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 // display line at top
(void)disp_sb_line(0, mp); (void)disp_sb_line(0, mp);
} else { } else {
// redisplay all lines // redisplay all lines
// TODO(bfredl): this case is not optimized (though only concerns // TODO(bfredl): this case is not optimized (though only concerns
// event fragmentization, not unnecessary scroll events). // 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++) { for (i = 0; mp != NULL && i < Rows - 1; i++) {
mp = disp_sb_line(i, mp); mp = disp_sb_line(i, mp);
++msg_scrolled; ++msg_scrolled;
@@ -2713,7 +2715,7 @@ static int do_more_prompt(int typed_char)
} else { } else {
/* First display any text that we scrolled back. */ /* First display any text that we scrolled back. */
while (toscroll > 0 && mp_last != NULL) { 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 // Tricky: we redraw at one line higher than usual. Therefore
// the non-flushed area is one line larger. // the non-flushed area is one line larger.
msg_scrolled_at_flush--; msg_scrolled_at_flush--;
@@ -2722,6 +2724,8 @@ static int do_more_prompt(int typed_char)
// scroll up, display line at bottom // scroll up, display line at bottom
msg_scroll_up(true); msg_scroll_up(true);
inc_msg_scrolled(); 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); mp_last = disp_sb_line(Rows - 2, mp_last);
toscroll--; toscroll--;
} }
@@ -2743,7 +2747,8 @@ static int do_more_prompt(int typed_char)
} }
// clear the --more-- message // 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; redraw_cmdline = true;
clear_cmdline = false; clear_cmdline = false;
mode_displayed = false; mode_displayed = false;
@@ -2817,7 +2822,7 @@ void msg_moremsg(int full)
int attr; int attr;
char_u *s = (char_u *)_("-- More --"); 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); grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
if (full) { if (full) {
grid_puts(&msg_grid_adj, (char_u *) grid_puts(&msg_grid_adj, (char_u *)

View File

@@ -518,9 +518,10 @@ EXTERN long p_pyx; // 'pyxversion'
EXTERN char_u *p_rdb; // 'redrawdebug' EXTERN char_u *p_rdb; // 'redrawdebug'
EXTERN unsigned rdb_flags; EXTERN unsigned rdb_flags;
# ifdef IN_OPTION_C # ifdef IN_OPTION_C
static char *(p_rdb_values[]) = { "compositor", NULL }; static char *(p_rdb_values[]) = { "compositor", "nothrottle", NULL };
# endif # endif
# define RDB_COMPOSITOR 0x001 # define RDB_COMPOSITOR 0x001
# define RDB_NOTHROTTLE 0x002
EXTERN long p_rdt; // 'redrawtime' EXTERN long p_rdt; // 'redrawtime'
EXTERN int p_remap; // 'remap' EXTERN int p_remap; // 'remap'

View File

@@ -404,7 +404,9 @@ int update_screen(int type)
default_grid.valid = true; 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); 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_scrolled = 0; // can't scroll back
msg_didany = false; msg_didany = false;
msg_didout = 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); grid_invalidate(&msg_grid);
msg_grid_validate(); msg_grid_validate();
msg_grid_invalid = false; msg_grid_invalid = false;

View File

@@ -132,7 +132,7 @@ describe('TUI', function()
-- TODO(bfredl): messes up the output (just like vim does). -- TODO(bfredl): messes up the output (just like vim does).
feed_data('g') feed_data('g')
screen:expect{grid=[[ screen:expect{grid=[[
{8:FAIL 1} ) | ) |
{8:Error detected while processing function ManyErr:} | {8:Error detected while processing function ManyErr:} |
{11:line 2:} | {11:line 2:} |
{10:-- More --}{1: } | {10:-- More --}{1: } |
@@ -141,7 +141,7 @@ describe('TUI', function()
screen:try_resize(50,10) screen:try_resize(50,10)
screen:expect{grid=[[ screen:expect{grid=[[
{8:FAIL 1} ) | ) |
{8:Error detected while processing function ManyErr:} | {8:Error detected while processing function ManyErr:} |
{11:line 2:} | {11:line 2:} |
{10:-- More --} | {10:-- More --} |

View File

@@ -10,6 +10,7 @@ local test_build_dir = helpers.test_build_dir
local nvim_prog = helpers.nvim_prog local nvim_prog = helpers.nvim_prog
local iswin = helpers.iswin local iswin = helpers.iswin
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local exec_lua = helpers.exec_lua
describe('ui/ext_messages', function() describe('ui/ext_messages', function()
local screen local screen
@@ -1058,16 +1059,34 @@ describe('pager', function()
before_each(function() before_each(function()
clear() clear()
screen = Screen.new(25, 5) screen = Screen.new(35, 8)
screen:attach() screen:attach()
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1}, [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}, [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) end)
it('can be quit', function() 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>') feed(':echon join(map(range(0, &lines*2), "v:val"), "\\n")<cr>')
screen:expect{grid=[[ screen:expect{grid=[[
0 | 0 |
@@ -1085,4 +1104,292 @@ describe('pager', function()
| |
]]} ]]}
end) 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) end)

View File

@@ -859,7 +859,7 @@ function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols)
-- clear invalid rows -- clear invalid rows
for i = stop + step, stop + rows, step do 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
end end
@@ -1065,10 +1065,10 @@ function Screen:_clear_block(grid, top, bot, left, right)
end end
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] local row = grid.rows[rownum]
for i = startcol, stopcol do for i = startcol, stopcol do
row[i].text = ' ' row[i].text = (invalid and '<EFBFBD>' or ' ')
row[i].attrs = self._clear_attrs row[i].attrs = self._clear_attrs
end end
end end