diff options
-rw-r--r-- | runtime/doc/starting.txt | 3 | ||||
-rwxr-xr-x | scripts/vim-patch.sh | 6 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 80 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 43 | ||||
-rw-r--r-- | test/functional/ex_cmds/cmd_map_spec.lua | 28 |
5 files changed, 100 insertions, 60 deletions
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 24fb87a6b4..c9ce2b9dc1 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -408,9 +408,6 @@ accordingly. Vim proceeds in this order: Windows $XDG_CONFIG_HOME/nvim/init.vim (default for $XDG_CONFIG_HOME is ~/AppData/Local) - The files are searched in the order specified above and only the first - one that is found is read. - RECOMMENDATION: Put all your Vim configuration stuff in the $HOME/.config/nvim/ directory. That makes it easy to copy it to another system. diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 6dd411a34e..9ea43383b6 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -85,12 +85,12 @@ get_vim_sources() { git clone https://github.com/vim/vim.git "${VIM_SOURCE_DIR}" cd "${VIM_SOURCE_DIR}" else - if [[ ! -d "${VIM_SOURCE_DIR}/.git" ]]; then + cd "${VIM_SOURCE_DIR}" + if [[ "$(git rev-parse --show-toplevel)" != "${VIM_SOURCE_DIR}" ]]; then msg_err "${VIM_SOURCE_DIR} does not appear to be a git repository." echo " Please remove it and try again." exit 1 fi - cd "${VIM_SOURCE_DIR}" echo "Updating Vim sources: ${VIM_SOURCE_DIR}" git pull && msg_ok "Updated Vim sources." || @@ -202,7 +202,7 @@ get_vimpatch() { printf "Pre-processing patch...\n" preprocess_patch "${NVIM_SOURCE_DIR}/${patch_file}" - msg_ok "Saved patch to '${NVIM_SOURCE_DIR}/${patch_file}'.\n" + msg_ok "Saved patch to '${NVIM_SOURCE_DIR}/${patch_file}'." } stage_patch() { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1810056a4a..7f40a984d1 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -177,10 +177,6 @@ typedef struct command_line_state { int break_ctrl_c; expand_T xpc; long *b_im_ptr; - // Everything that may work recursively should save and restore the - // current command line in save_ccline. That includes update_screen(), a - // custom status line may invoke ":normal". - struct cmdline_info save_ccline; } CommandLineState; typedef struct cmdline_info CmdlineInfo; @@ -225,11 +221,19 @@ static bool need_cursor_update = false; # include "ex_getln.c.generated.h" #endif -static int cmd_hkmap = 0; /* Hebrew mapping during command line */ +static int cmd_hkmap = 0; // Hebrew mapping during command line +static int cmd_fkmap = 0; // Farsi mapping during command line -static int cmd_fkmap = 0; /* Farsi mapping during command line */ +/// Internal entry point for cmdline mode. +/// +/// caller must use save_cmdline and restore_cmdline. Best is to use +/// getcmdline or getcmdline_prompt, instead of calling this directly. static uint8_t *command_line_enter(int firstc, long count, int indent) { + // can be invoked recursively, identify each level + static int cmdline_level = 0; + cmdline_level++; + CommandLineState state, *s = &state; memset(s, 0, sizeof(CommandLineState)); s->firstc = firstc; @@ -257,7 +261,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } ccline.prompt_id = last_prompt_id++; - ccline.level++; + ccline.level = cmdline_level; ccline.overstrike = false; // always start in insert mode clearpos(&s->match_end); s->save_cursor = curwin->w_cursor; // may be restored later @@ -489,19 +493,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) sb_text_end_cmdline(); - { - char_u *p = ccline.cmdbuff; - - // Make ccline empty, getcmdline() may try to use it. - ccline.cmdbuff = NULL; + char_u *p = ccline.cmdbuff; - if (ui_is_external(kUICmdline)) { - ccline.redraw_state = kCmdRedrawNone; - ui_call_cmdline_hide(ccline.level); - } - ccline.level--; - return p; + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_hide(ccline.level); } + + cmdline_level--; + return p; } static int command_line_check(VimState *state) @@ -641,9 +640,7 @@ static int command_line_execute(VimState *state, int key) p_ls = save_p_ls; p_wmh = save_p_wmh; last_status(false); - save_cmdline(&s->save_ccline); update_screen(VALID); // redraw the screen NOW - restore_cmdline(&s->save_ccline); redrawcmd(); save_p_ls = -1; wild_menu_showing = 0; @@ -818,18 +815,17 @@ static int command_line_execute(VimState *state, int key) new_cmdpos = ccline.cmdpos; } - save_cmdline(&s->save_ccline); s->c = get_expr_register(); - restore_cmdline(&s->save_ccline); if (s->c == '=') { // Need to save and restore ccline. And set "textlock" // to avoid nasty things like going to another buffer when // evaluating an expression. - save_cmdline(&s->save_ccline); - ++textlock; + CmdlineInfo save_ccline; + save_cmdline(&save_ccline); + textlock++; p = get_expr_line(); - --textlock; - restore_cmdline(&s->save_ccline); + textlock--; + restore_cmdline(&save_ccline); if (p != NULL) { len = (int)STRLEN(p); @@ -1362,9 +1358,10 @@ static int command_line_handle_key(CommandLineState *s) beep_flush(); s->c = ESC; } else { - save_cmdline(&s->save_ccline); + CmdlineInfo save_ccline; + save_cmdline(&save_ccline); s->c = get_expr_register(); - restore_cmdline(&s->save_ccline); + restore_cmdline(&save_ccline); } } @@ -1899,9 +1896,7 @@ static int command_line_changed(CommandLineState *s) curwin->w_redr_status = true; } - save_cmdline(&s->save_ccline); update_screen(SOME_VALID); - restore_cmdline(&s->save_ccline); restore_last_search_pattern(); // Leave it at the end to make CTRL-R CTRL-W work. @@ -1996,7 +1991,14 @@ getcmdline ( int indent // indent for inside conditionals ) { - return command_line_enter(firstc, count, indent); + // Be prepared for situations where cmdline can be invoked recursively. + // That includes cmd mappings, event handlers, as well as update_screen() + // (custom status line eval), which all may invoke ":normal :". + CmdlineInfo save_ccline; + save_cmdline(&save_ccline); + char_u *retval = command_line_enter(firstc, count, indent); + restore_cmdline(&save_ccline); + return retval; } /// Get a command line with a prompt @@ -2020,7 +2022,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, { const int msg_col_save = msg_col; - struct cmdline_info save_ccline; + CmdlineInfo save_ccline; save_cmdline(&save_ccline); ccline.prompt_id = last_prompt_id++; @@ -2034,7 +2036,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, int msg_silent_saved = msg_silent; msg_silent = 0; - char *const ret = (char *)getcmdline(firstc, 1L, 0); + char *const ret = (char *)command_line_enter(firstc, 1L, 0); restore_cmdline(&save_ccline); msg_silent = msg_silent_saved; @@ -2176,6 +2178,7 @@ getexline ( /* When executing a register, remove ':' that's in front of each line. */ if (exec_from_reg && vpeekc() == ':') (void)vgetc(); + return getcmdline(c, 1L, indent); } @@ -3019,16 +3022,16 @@ void cmdline_screen_cleared(void) } int prev_level = ccline.level-1; - CmdlineInfo *prev_ccline = ccline.prev_ccline; - while (prev_level > 0 && prev_ccline) { - if (prev_ccline->level == prev_level) { + CmdlineInfo *line = ccline.prev_ccline; + while (prev_level > 0 && line) { + if (line->level == prev_level) { // don't redraw a cmdline already shown in the cmdline window if (prev_level != cmdwin_level) { - prev_ccline->redraw_state = kCmdRedrawAll; + line->redraw_state = kCmdRedrawAll; } prev_level--; } - prev_ccline = prev_ccline->prev_ccline; + line = line->prev_ccline; } need_cursor_update = true; @@ -3244,6 +3247,7 @@ static void save_cmdline(struct cmdline_info *ccp) ccline.cmdprompt = NULL; ccline.xpc = NULL; ccline.special_char = NUL; + ccline.level = 0; } /* diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index df14ddf988..0781b03965 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -88,6 +88,7 @@ typedef struct { bool cont_received; UGrid grid; kvec_t(Rect) invalid_regions; + bool did_resize; int row, col; int out_fd; bool scroll_region_is_full_screen; @@ -95,6 +96,7 @@ typedef struct { bool can_set_lr_margin; bool can_set_left_right_margin; bool immediate_wrap_after_last_column; + bool bce; bool mouse_enabled; bool busy, is_invisible; bool cork, overflow; @@ -236,6 +238,7 @@ static void terminfo_start(UI *ui) data->immediate_wrap_after_last_column = terminfo_is_term_family(term, "cygwin") || terminfo_is_term_family(term, "interix"); + data->bce = unibi_get_bool(data->ut, unibi_back_color_erase); data->normlen = unibi_pre_fmt_str(data, unibi_cursor_normal, data->norm, sizeof data->norm); data->invislen = unibi_pre_fmt_str(data, unibi_cursor_invisible, @@ -435,6 +438,12 @@ static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) } } +static bool no_bg(UI *ui, HlAttrs attrs) +{ + return ui->rgb ? attrs.rgb_bg_color == -1 + : attrs.cterm_bg_color == 0; +} + static void update_attrs(UI *ui, HlAttrs attrs) { TUIData *data = ui->data; @@ -597,8 +606,6 @@ static void cursor_goto(UI *ui, int row, int col) if (row == grid->row && col == grid->col) { return; } - grid->row = row; - grid->col = col; if (0 == row && 0 == col) { unibi_out(ui, unibi_cursor_home); ugrid_goto(grid, row, col); @@ -693,10 +700,11 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, UGrid *grid = &data->grid; bool cleared = false; - // TODO(bfredl): support BCE for non-default background - bool nobg = ui->rgb ? attrs.rgb_bg_color == -1 - : attrs.cterm_bg_color == 0; - if (nobg && right == ui->width -1) { + + // non-BCE terminals can't clear with non-default background color + bool can_clear = data->bce || no_bg(ui, attrs); + + if (can_clear && right == ui->width -1) { // Background is set to the default color and the right edge matches the // screen end, try to use terminal codes for clearing the requested area. update_attrs(ui, attrs); @@ -729,6 +737,14 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, cursor_goto(ui, row, col); print_cell(ui, cell); }); + + if (data->did_resize && top == 0) { + // TODO(bfredl): the first line of the screen doesn't gets properly + // cleared after resize by the loop above, so redraw the final state + // after the next flush. + invalidate(ui, 0, bot, left, right); + data->did_resize = false; + } } // restore cursor @@ -804,6 +820,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) TUIData *data = ui->data; UGrid *grid = &data->grid; ugrid_resize(grid, (int)width, (int)height); + data->did_resize = true; // resize might not always be followed by a clear before flush // so clip the invalid region @@ -984,18 +1001,13 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot, ugrid_scroll(grid, (int)rows, &clear_top, &clear_bot); if (can_use_scroll(ui)) { - bool scroll_clears_to_current_colour = - unibi_get_bool(data->ut, unibi_back_color_erase); - // Change terminal scroll region and move cursor to the top if (!data->scroll_region_is_full_screen) { set_scroll_region(ui); } cursor_goto(ui, grid->top, grid->left); // also set default color attributes or some terminals can become funny - if (scroll_clears_to_current_colour) { - update_attrs(ui, data->clear_attrs); - } + update_attrs(ui, data->clear_attrs); if (rows > 0) { if (rows == 1) { @@ -1019,7 +1031,7 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot, } cursor_goto(ui, data->row, data->col); - if (!scroll_clears_to_current_colour) { + if (!(data->bce || no_bg(ui, data->clear_attrs))) { // Scrolling will leave wrong background in the cleared area on non-BCE // terminals. Update the cleared area. clear_region(ui, clear_top, clear_bot, grid->left, grid->right, @@ -1382,6 +1394,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, bool roxterm = !!os_getenv("ROXTERM_ID"); #endif bool xterm = terminfo_is_term_family(term, "xterm"); + bool kitty = terminfo_is_term_family(term, "xterm-kitty"); bool linuxvt = terminfo_is_term_family(term, "linux"); bool rxvt = terminfo_is_term_family(term, "rxvt"); bool teraterm = terminfo_is_term_family(term, "teraterm"); @@ -1437,8 +1450,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, } } - if (!true_xterm) { - // Cannot trust terminfo; safer to disable BCE. #7624 + if (tmux || screen || kitty) { + // Disable BCE in some cases we know it is not working. #8806 unibi_set_bool(ut, unibi_back_color_erase, false); } diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index 77d025dcc7..acb76e382d 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -656,7 +656,6 @@ describe('mappings with <Cmd>', function() end) it('works in cmdline mode', function() - cmdmap('<F2>', 'call setcmdpos(2)') feed(':text<F3>') eq('c', eval('m')) -- didn't leave cmdline mode @@ -768,5 +767,32 @@ describe('mappings with <Cmd>', function() end) + it("doesn't crash when invoking cmdline mode recursively #8859", function() + cmdmap('<F2>', 'norm! :foo') + feed(':bar') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :bar^ | + ]]) + + feed('<f2>x') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :barx^ | + ]]) + end) + end) |