diff options
-rw-r--r-- | src/nvim/api/ui.c | 9 | ||||
-rw-r--r-- | src/nvim/log.c | 27 | ||||
-rw-r--r-- | src/nvim/os/time.c | 4 | ||||
-rw-r--r-- | src/nvim/screen.c | 8 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 13 | ||||
-rw-r--r-- | src/nvim/ugrid.c | 2 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 38 | ||||
-rw-r--r-- | test/functional/ui/screen_basic_spec.lua | 95 |
8 files changed, 165 insertions, 31 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 63c2c4a1b9..d0db43c588 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -302,6 +302,15 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, args = (Array)ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); + + // some clients have "clear" being affected by scroll region, + // so reset it. + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->height-1)); + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->width-1)); + push_call(ui, "set_scroll_region", args); } } diff --git a/src/nvim/log.c b/src/nvim/log.c index 7bfe5c4089..e485d4c338 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -7,6 +7,9 @@ #include <stdbool.h> #include <stdint.h> #include <stdio.h> +#if !defined(WIN32) +# include <sys/time.h> // for gettimeofday() +#endif #include <uv.h> #include "nvim/log.h" @@ -260,25 +263,33 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, }; assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); - // format current timestamp in local time + // Format the timestamp. struct tm local_time; - if (os_get_localtime(&local_time) == NULL) { + if (os_localtime(&local_time) == NULL) { return false; } char date_time[20]; - if (strftime(date_time, sizeof(date_time), "%Y/%m/%d %H:%M:%S", + if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", &local_time) == 0) { return false; } - // print the log message prefixed by the current timestamp and pid + int millis = 0; +#if !defined(WIN32) + struct timeval curtime; + if (gettimeofday(&curtime, NULL) == 0) { + millis = (int)curtime.tv_usec / 1000; + } +#endif + + // Print the log message. int64_t pid = os_get_pid(); int rv = (line_num == -1 || func_name == NULL) - ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time, - log_levels[log_level], pid, + ? fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s", + log_levels[log_level], date_time, millis, pid, (context == NULL ? "?:" : context)) - : fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time, - log_levels[log_level], pid, + : fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s%s:%d: ", + log_levels[log_level], date_time, millis, pid, (context == NULL ? "" : context), func_name, line_num); if (rv < 0) { diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 290d421acc..31ef1a0cd6 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -114,12 +114,12 @@ struct tm *os_localtime_r(const time_t *restrict clock, #endif } -/// Obtains the current Unix timestamp and adjusts it to local time. +/// Gets the current Unix timestamp and adjusts it to local time. /// /// @param result Pointer to a 'struct tm' where the result should be placed /// @return A pointer to a 'struct tm' in the current time zone (the 'result' /// argument) or NULL in case of error -struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL +struct tm *os_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL { time_t rawtime = time(NULL); return os_localtime_r(&rawtime, result); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 65a3c17286..e8dbc11710 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -283,8 +283,11 @@ void update_screen(int type) if (msg_scrolled) { clear_cmdline = true; if (dy_flags & DY_MSGSEP) { + int valid = MAX(Rows - msg_scrollsize(), 0); + if (valid == 0) { + redraw_tabline = true; + } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - int valid = Rows - msg_scrollsize(); if (wp->w_winrow + wp->w_height > valid) { wp->w_redr_type = NOT_VALID; wp->w_lines_valid = 0; @@ -292,9 +295,6 @@ void update_screen(int type) if (wp->w_winrow + wp->w_height + wp->w_status_height > valid) { wp->w_redr_status = true; } - if (valid == 0) { - redraw_tabline = true; - } } } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 56c47ed6cc..508d25cd3b 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -802,7 +802,16 @@ static void reset_scroll_region(UI *ui) static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) { TUIData *data = ui->data; - ugrid_resize(&data->grid, (int)width, (int)height); + UGrid *grid = &data->grid; + ugrid_resize(grid, (int)width, (int)height); + + // resize might not always be followed by a clear before flush + // so clip the invalid region + for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { + Rect *r = &kv_A(data->invalid_regions, i); + r->bot = MIN(r->bot, grid->height-1); + r->right = MIN(r->right, grid->width-1); + } if (!got_winch) { // Try to resize the terminal window. UNIBI_SET_NUM_VAR(data->params[0], (int)height); @@ -823,7 +832,7 @@ static void tui_grid_clear(UI *ui, Integer g) UGrid *grid = &data->grid; ugrid_clear(grid); kv_size(data->invalid_regions) = 0; - clear_region(ui, grid->top, grid->bot, grid->left, grid->right, + clear_region(ui, 0, grid->height-1, 0, grid->width-1, data->clear_attrs); } diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 48f3cff2d7..36936970f8 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -44,7 +44,7 @@ void ugrid_resize(UGrid *grid, int width, int height) void ugrid_clear(UGrid *grid) { - clear_region(grid, grid->top, grid->bot, grid->left, grid->right, + clear_region(grid, 0, grid->height-1, 0, grid->width-1, HLATTRS_INIT); } diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index d71d8cf3a8..c40b2210ff 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -73,6 +73,7 @@ local helpers = require('test.functional.helpers')(nil) local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths +local eq = helpers.eq local dedent = helpers.dedent local Screen = {} @@ -389,12 +390,21 @@ function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info) end function Screen:_handle_clear() + -- the first implemented UI protocol clients (python-gui and builitin TUI) + -- allowed the cleared region to be restricted by setting the scroll region. + -- this was never used by nvim tough, and not documented and implemented by + -- newer clients, to check we remain compatible with both kind of clients, + -- ensure the scroll region is in a reset state. + local expected_region = { + top = 1, bot = self._height, left = 1, right = self._width + } + eq(expected_region, self._scroll_region) self:_clear_block(1, self._height, 1, self._width) end function Screen:_handle_grid_clear(grid) assert(grid == 1) - self:_handle_clear() + self:_clear_block(1, self._height, 1, self._width) end function Screen:_handle_eol_clear() @@ -446,22 +456,30 @@ function Screen:_handle_scroll(count) local bot = self._scroll_region.bot local left = self._scroll_region.left local right = self._scroll_region.right + self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) +end + +function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) + top = top+1 + left = left+1 + assert(grid == 1) + assert(cols == 0) local start, stop, step - if count > 0 then + if rows > 0 then start = top - stop = bot - count + stop = bot - rows step = 1 else start = bot - stop = top - count + stop = top - rows step = -1 end -- shift scroll region for i = start, stop, step do local target = self._rows[i] - local source = self._rows[i + count] + local source = self._rows[i + rows] for j = left, right do target[j].text = source[j].text target[j].attrs = source[j].attrs @@ -470,19 +488,11 @@ function Screen:_handle_scroll(count) end -- clear invalid rows - for i = stop + step, stop + count, step do + for i = stop + step, stop + rows, step do self:_clear_row_section(i, left, right) end end -function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) - assert(grid == 1) - assert(cols == 0) - -- TODO: if we truly believe we should translate the other way - self:_handle_set_scroll_region(top,bot-1,left,right-1) - self:_handle_scroll(rows) -end - function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) self._attr_table[id] = {rgb_attrs, cterm_attrs} self._hl_info[id] = info diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 75a2d4978d..957d8c0915 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -354,6 +354,101 @@ local function screen_tests(newgrid) {0:~ }| | ]]) + + feed(':echo "'..string.rep('x\\n', 12)..'"<cr>') + screen:expect([[ + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + x | + | + {7:Press ENTER or type command to continue}^ | + ]]) + + feed('<cr>') + screen:expect([[ + {4: [No Name] }{2: [No Name] }{3: }{4:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + + end) + + it('redraws properly with :tab split right after scroll', function() + feed('30Ofoo<esc>gg') + + command('vsplit') + screen:expect([[ + ^foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + {1:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + + feed('<PageDown>') + screen:expect([[ + ^foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + foo {3:│}foo | + {1:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + + command('tab split') + screen:expect([[ + {4: }{5:2}{4:+ [No Name] }{2: + [No Name] }{3: }{4:X}| + ^foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + foo | + | + ]]) end) end) |