diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/clint.py | 1 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 33 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 16 | ||||
-rw-r--r-- | src/nvim/buffer.c | 2 | ||||
-rw-r--r-- | src/nvim/eval.c | 44 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 353 | ||||
-rw-r--r-- | src/nvim/globals.h | 7 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 36 | ||||
-rw-r--r-- | src/nvim/screen.c | 23 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_retab.vim | 77 | ||||
-rw-r--r-- | src/nvim/version.c | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 8 |
13 files changed, 441 insertions, 162 deletions
diff --git a/src/clint.py b/src/clint.py index 4a41650ec4..e63175a69b 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2531,6 +2531,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): r'(?<!\bkhash_t)' r'(?<!\bkbtree_t)' r'(?<!\bkbitr_t)' + r'(?<!\bPMap)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index e736e29e2d..2944925a9c 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -667,6 +667,22 @@ tabpage_T *find_tab_by_handle(Tabpage tabpage, Error *err) return rv; } +/// Allocates a String consisting of a single char. Does not support multibyte +/// characters. The resulting string is also NUL-terminated, to facilitate +/// interoperating with code using C strings. +/// +/// @param char the char to convert +/// @return the resulting String, if the input char was NUL, an +/// empty String is returned +String cchar_to_string(char c) +{ + char buf[] = { c, NUL }; + return (String) { + .data = xmemdupz(buf, 1), + .size = (c != NUL) ? 1 : 0 + }; +} + /// Copies a C string into a String (binary safe string, characters + length). /// The resulting string is also NUL-terminated, to facilitate interoperating /// with code using C strings. @@ -687,6 +703,23 @@ String cstr_to_string(const char *str) }; } +/// Copies buffer to an allocated String. +/// The resulting string is also NUL-terminated, to facilitate interoperating +/// with code using C strings. +/// +/// @param buf the buffer to copy +/// @param size length of the buffer +/// @return the resulting String, if the input string was NULL, an +/// empty String is returned +String cbuf_to_string(const char *buf, size_t size) + FUNC_ATTR_NONNULL_ALL +{ + return (String) { + .data = xmemdupz(buf, size), + .size = size + }; +} + /// Creates a String using the given C string. Unlike /// cstr_to_string this function DOES NOT copy the C string. /// diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 1b5d17584f..65357d008a 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -68,4 +68,20 @@ void popupmenu_select(Integer selected) void tabline_update(Tabpage current, Array tabs) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_show(Array content, Integer pos, String firstc, String prompt, + Integer indent, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_pos(Integer pos, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_special_char(String c, Boolean shift, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_hide(Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_show(Array lines) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_append(Array lines) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_hide(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + #endif // NVIM_API_UI_EVENTS_IN_H diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index fc5bb90973..9d42fbc1b3 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1466,7 +1466,7 @@ void enter_buffer(buf_T *buf) if (buf->terminal) { terminal_resize(buf->terminal, - (uint16_t)curwin->w_width, + (uint16_t)(MAX(0, curwin->w_width - win_col_off(curwin))), (uint16_t)curwin->w_height); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index aab777955c..c7cb51ac29 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11147,17 +11147,18 @@ void get_user_input(const typval_T *const argvars, cmd_silent = false; // Want to see the prompt. // Only the part of the message after the last NL is considered as - // prompt for the command line. - const char *p = strrchr(prompt, '\n'); - if (p == NULL) { - p = prompt; - } else { - p++; - msg_start(); - msg_clr_eos(); - msg_puts_attr_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_is_external(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl+1; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } } cmdline_row = msg_row; @@ -16723,9 +16724,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd); - data->proc.pty.width = curwin->w_width; + data->proc.pty.width = term_width; data->proc.pty.height = curwin->w_height; data->proc.pty.term_name = xstrdup("xterm-256color"); if (!common_job_start(data, rettv)) { @@ -16733,7 +16735,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } TerminalOptions topts; topts.data = data; - topts.width = curwin->w_width; + topts.width = term_width; topts.height = curwin->w_height; topts.write_cb = term_write; topts.resize_cb = term_resize; @@ -19642,6 +19644,7 @@ void ex_function(exarg_T *eap) int todo; hashitem_T *hi; int sourcing_lnum_off; + bool show_block = false; /* * ":function" without argument: list functions. @@ -19815,6 +19818,11 @@ void ex_function(exarg_T *eap) goto errret_2; } + if (KeyTyped && ui_is_external(kUICmdline)) { + show_block = true; + ui_ext_cmdline_block_append(0, (const char *)eap->cmd); + } + // find extra arguments "range", "dict", "abort" and "closure" for (;; ) { p = skipwhite(p); @@ -19867,7 +19875,9 @@ void ex_function(exarg_T *eap) if (!eap->skip && did_emsg) goto erret; - msg_putchar('\n'); /* don't overwrite the function name */ + if (!ui_is_external(kUICmdline)) { + msg_putchar('\n'); // don't overwrite the function name + } cmdline_row = msg_row; } @@ -19901,6 +19911,9 @@ void ex_function(exarg_T *eap) EMSG(_("E126: Missing :endfunction")); goto erret; } + if (show_block) { + ui_ext_cmdline_block_append(indent, (const char *)theline); + } /* Detect line continuation: sourcing_lnum increased more than one. */ if (sourcing_lnum > sourcing_lnum_off + 1) @@ -20193,6 +20206,9 @@ ret_free: xfree(name); did_emsg |= saved_did_emsg; need_wait_return |= saved_wait_return; + if (show_block) { + ui_ext_cmdline_block_leave(); + } } /// Get a function name, translating "<SID>" and "<SNR>". diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 54e5bcb9ff..e79476ab53 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -67,6 +67,30 @@ #include "nvim/api/private/helpers.h" #include "nvim/highlight_defs.h" +/// Command-line colors: one chunk +/// +/// Defines a region which has the same highlighting. +typedef struct { + int start; ///< Colored chunk start. + int end; ///< Colored chunk end (exclusive, > start). + int attr; ///< Highlight attr. +} CmdlineColorChunk; + +/// Command-line colors +/// +/// Holds data about all colors. +typedef kvec_t(CmdlineColorChunk) CmdlineColors; + +/// Command-line coloring +/// +/// Holds both what are the colors and what have been colored. Latter is used to +/// suppress unnecessary calls to coloring callbacks. +typedef struct { + unsigned prompt_id; ///< ID of the prompt which was colored last. + char *cmdbuff; ///< What exactly was colored last time or NULL. + CmdlineColors colors; ///< Last colors. +} ColoredCmdline; + /* * Variables shared between getcmdline(), redrawcmdline() and others. * These need to be saved when using CTRL-R |, that's why they are in a @@ -91,6 +115,11 @@ struct cmdline_info { int input_fn; // when TRUE Invoked for input() function unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. Callback highlight_callback; ///< Callback used for coloring user input. + ColoredCmdline last_colors; ///< Last cmdline colors + int level; // current cmdline level + struct cmdline_info *prev_ccline; ///< pointer to saved cmdline state + char special_char; ///< last putcmdline char (used for redraws) + bool special_shift; ///< shift of last putcmdline char }; /// Last value of prompt_id, incremented when doing new prompt static unsigned last_prompt_id = 0; @@ -143,36 +172,6 @@ typedef struct command_line_state { struct cmdline_info save_ccline; } CommandLineState; -/// Command-line colors: one chunk -/// -/// Defines a region which has the same highlighting. -typedef struct { - int start; ///< Colored chunk start. - int end; ///< Colored chunk end (exclusive, > start). - int attr; ///< Highlight attr. -} CmdlineColorChunk; - -/// Command-line colors -/// -/// Holds data about all colors. -typedef kvec_t(CmdlineColorChunk) CmdlineColors; - -/// Command-line coloring -/// -/// Holds both what are the colors and what have been colored. Latter is used to -/// suppress unnecessary calls to coloring callbacks. -typedef struct { - unsigned prompt_id; ///< ID of the prompt which was colored last. - char *cmdbuff; ///< What exactly was colored last time or NULL. - CmdlineColors colors; ///< Last colors. -} ColoredCmdline; - -/// Last command-line colors. -ColoredCmdline last_ccline_colors = { - .cmdbuff = NULL, - .colors = KV_INITIAL_VALUE -}; - typedef struct cmdline_info CmdlineInfo; /* The current cmdline_info. It is initialized in getcmdline() and after that @@ -185,6 +184,8 @@ static int cmd_showtail; /* Only show path tail in lists ? */ static int new_cmdpos; /* position set by set_cmdline_pos() */ +static Array cmdline_block; ///< currently displayed block of context + /* * Type used by call_user_expand_func */ @@ -239,6 +240,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } ccline.prompt_id = last_prompt_id++; + ccline.level++; ccline.overstrike = false; // always start in insert mode clearpos(&s->match_end); s->save_cursor = curwin->w_cursor; // may be restored later @@ -258,6 +260,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.cmdlen = ccline.cmdpos = 0; ccline.cmdbuff[0] = NUL; + ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL, + .colors = KV_INITIAL_VALUE }; + // autoindent for :insert and :append if (s->firstc <= 0) { memset(ccline.cmdbuff, ' ', s->indent); @@ -408,12 +413,19 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) setmouse(); ui_cursor_shape(); // may show different cursor shape xfree(s->save_p_icm); + xfree(ccline.last_colors.cmdbuff); + kv_destroy(ccline.last_colors.colors); { char_u *p = ccline.cmdbuff; // Make ccline empty, getcmdline() may try to use it. ccline.cmdbuff = NULL; + + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_hide(ccline.level); + } + ccline.level--; return p; } } @@ -804,7 +816,9 @@ static int command_line_execute(VimState *state, int key) } if (!cmd_silent) { - ui_cursor_goto(msg_row, 0); + if (!ui_is_external(kUICmdline)) { + ui_cursor_goto(msg_row, 0); + } ui_flush(); } return 0; @@ -1134,7 +1148,7 @@ static int command_line_handle_key(CommandLineState *s) xfree(ccline.cmdbuff); // no commandline to return ccline.cmdbuff = NULL; - if (!cmd_silent) { + if (!cmd_silent && !ui_is_external(kUICmdline)) { if (cmdmsg_rl) { msg_col = Columns; } else { @@ -1586,9 +1600,14 @@ static int command_line_handle_key(CommandLineState *s) s->do_abbr = false; // don't do abbreviation now // may need to remove ^ when composing char was typed if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) { - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_putchar(' '); - cursorcmd(); + if (ui_is_external(kUICmdline)) { + // TODO(bfredl): why not make unputcmdline also work with true? + unputcmdline(); + } else { + draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); + msg_putchar(' '); + cursorcmd(); + } } break; @@ -2346,8 +2365,7 @@ enum { MAX_CB_ERRORS = 1 }; /// Should use built-in command parser or user-specified one. Currently only the /// latter is supported. /// -/// @param[in] colored_ccline Command-line to color. -/// @param[out] ret_ccline_colors What should be colored. Also holds a cache: +/// @param[in,out] colored_ccline Command-line to color. Also holds a cache: /// if ->prompt_id and ->cmdbuff values happen /// to be equal to those from colored_cmdline it /// will just do nothing, assuming that ->colors @@ -2357,11 +2375,11 @@ enum { MAX_CB_ERRORS = 1 }; /// /// @return true if draw_cmdline may proceed, false if it does not need anything /// to do. -static bool color_cmdline(const CmdlineInfo *const colored_ccline, - ColoredCmdline *const ret_ccline_colors) +static bool color_cmdline(CmdlineInfo *colored_ccline) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { bool printed_errmsg = false; + #define PRINT_ERRMSG(...) \ do { \ msg_putchar('\n'); \ @@ -2370,19 +2388,21 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, } while (0) bool ret = true; + ColoredCmdline *ccline_colors = &colored_ccline->last_colors; + // Check whether result of the previous call is still valid. - if (ret_ccline_colors->prompt_id == colored_ccline->prompt_id - && ret_ccline_colors->cmdbuff != NULL - && STRCMP(ret_ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { + if (ccline_colors->prompt_id == colored_ccline->prompt_id + && ccline_colors->cmdbuff != NULL + && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { return ret; } - kv_size(ret_ccline_colors->colors) = 0; + kv_size(ccline_colors->colors) = 0; if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) { // Nothing to do, exiting. - xfree(ret_ccline_colors->cmdbuff); - ret_ccline_colors->cmdbuff = NULL; + xfree(ccline_colors->cmdbuff); + ccline_colors->cmdbuff = NULL; return ret; } @@ -2395,7 +2415,7 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, static unsigned prev_prompt_id = UINT_MAX; static int prev_prompt_errors = 0; - Callback color_cb = { .type = kCallbackNone }; + Callback color_cb = CALLBACK_NONE; bool can_free_cb = false; TryState tstate; Error err = ERROR_INIT; @@ -2504,7 +2524,7 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, goto color_cmdline_error; } if (start != prev_end) { - kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { .start = prev_end, .end = start, .attr = 0, @@ -2533,14 +2553,14 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, } const int id = syn_name2id((char_u *)group); const int attr = (id == 0 ? 0 : syn_id2attr(id)); - kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { .start = start, .end = end, .attr = attr, })); } if (prev_end < colored_ccline->cmdlen) { - kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { .start = prev_end, .end = colored_ccline->cmdlen, .attr = 0, @@ -2552,14 +2572,14 @@ color_cmdline_end: if (can_free_cb) { callback_free(&color_cb); } - xfree(ret_ccline_colors->cmdbuff); + xfree(ccline_colors->cmdbuff); // Note: errors “output” is cached just as well as regular results. - ret_ccline_colors->prompt_id = colored_ccline->prompt_id; + ccline_colors->prompt_id = colored_ccline->prompt_id; if (arg_allocated) { - ret_ccline_colors->cmdbuff = (char *)arg.vval.v_string; + ccline_colors->cmdbuff = (char *)arg.vval.v_string; } else { - ret_ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff, - (size_t)colored_ccline->cmdlen); + ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff, + (size_t)colored_ccline->cmdlen); } tv_clear(&tv); return ret; @@ -2572,7 +2592,7 @@ color_cmdline_error: (void)printed_errmsg; prev_prompt_errors++; - kv_size(ret_ccline_colors->colors) = 0; + kv_size(ccline_colors->colors) = 0; redrawcmdline(); ret = false; goto color_cmdline_end; @@ -2585,7 +2605,13 @@ color_cmdline_error: */ static void draw_cmdline(int start, int len) { - if (!color_cmdline(&ccline, &last_ccline_colors)) { + if (!color_cmdline(&ccline)) { + return; + } + + if (ui_is_external(kUICmdline)) { + ccline.special_char = NUL; + ui_ext_cmdline_show(&ccline); return; } @@ -2687,9 +2713,9 @@ static void draw_cmdline(int start, int len) msg_outtrans_len(arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: - if (kv_size(last_ccline_colors.colors)) { - for (size_t i = 0; i < kv_size(last_ccline_colors.colors); i++) { - CmdlineColorChunk chunk = kv_A(last_ccline_colors.colors, i); + if (kv_size(ccline.last_colors.colors)) { + for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) { + CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i); if (chunk.end <= start) { continue; } @@ -2704,6 +2730,107 @@ draw_cmdline_no_arabicshape: } } +static void ui_ext_cmdline_show(CmdlineInfo *line) +{ + Array content = ARRAY_DICT_INIT; + if (cmdline_star) { + size_t len = 0; + for (char_u *p = ccline.cmdbuff; *p; mb_ptr_adv(p)) { + len++; + } + char *buf = xmallocz(len); + memset(buf, '*', len); + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); + ADD(content, ARRAY_OBJ(item)); + } else if (kv_size(line->last_colors.colors)) { + for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { + CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); + Array item = ARRAY_DICT_INIT; + + if (chunk.attr) { + attrentry_T *aep = syn_cterm_attr2entry(chunk.attr); + // TODO(bfredl): this desicion could be delayed by making attr_code a + // recognized type + HlAttrs rgb_attrs = attrentry2hlattrs(aep, true); + ADD(item, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs))); + } else { + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + } + ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, + chunk.end-chunk.start))); + ADD(content, ARRAY_OBJ(item)); + } + } else { + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); + ADD(content, ARRAY_OBJ(item)); + } + ui_call_cmdline_show(content, line->cmdpos, + cchar_to_string((char)line->cmdfirstc), + cstr_to_string((char *)(line->cmdprompt)), + line->cmdindent, + line->level); + if (line->special_char) { + ui_call_cmdline_special_char(cchar_to_string((char)(line->special_char)), + line->special_shift, + line->level); + } +} + +void ui_ext_cmdline_block_append(int indent, const char *line) +{ + char *buf = xmallocz(indent + strlen(line)); + memset(buf, ' ', indent); + memcpy(buf+indent, line, strlen(line)); + + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(cstr_as_string(buf))); + Array content = ARRAY_DICT_INIT; + ADD(content, ARRAY_OBJ(item)); + ADD(cmdline_block, ARRAY_OBJ(content)); + if (cmdline_block.size > 1) { + ui_call_cmdline_block_append(copy_array(content)); + } else { + ui_call_cmdline_block_show(copy_array(cmdline_block)); + } +} + +void ui_ext_cmdline_block_leave(void) +{ + api_free_array(cmdline_block); + ui_call_cmdline_block_hide(); +} + +/// Extra redrawing needed for redraw! and on ui_attach +/// assumes "redrawcmdline()" will already be invoked +void cmdline_screen_cleared(void) +{ + if (!ui_is_external(kUICmdline)) { + return; + } + + if (cmdline_block.size) { + ui_call_cmdline_block_show(copy_array(cmdline_block)); + } + + int prev_level = ccline.level-1; + CmdlineInfo *prev_ccline = ccline.prev_ccline; + while (prev_level > 0 && prev_ccline) { + if (prev_ccline->level == prev_level) { + // don't redraw a cmdline already shown in the cmdline window + if (prev_level != cmdwin_level) { + ui_ext_cmdline_show(prev_ccline); + } + prev_level--; + } + prev_ccline = prev_ccline->prev_ccline; + } +} + /* * Put a character on the command line. Shifts the following text to the * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc. @@ -2711,33 +2838,39 @@ draw_cmdline_no_arabicshape: */ void putcmdline(int c, int shift) { - if (cmd_silent) + if (cmd_silent) { return; - msg_no_more = TRUE; - msg_putchar(c); - if (shift) - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_no_more = FALSE; + } + if (!ui_is_external(kUICmdline)) { + msg_no_more = true; + msg_putchar(c); + if (shift) { + draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); + } + msg_no_more = false; + } else { + ccline.special_char = c; + ccline.special_shift = shift; + ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift, + ccline.level); + } cursorcmd(); ui_cursor_shape(); } -/* - * Undo a putcmdline(c, FALSE). - */ +/// Undo a putcmdline(c, FALSE). void unputcmdline(void) { - if (cmd_silent) + if (cmd_silent) { return; - msg_no_more = TRUE; - if (ccline.cmdlen == ccline.cmdpos) + } + msg_no_more = true; + if (ccline.cmdlen == ccline.cmdpos && !ui_is_external(kUICmdline)) { msg_putchar(' '); - else if (has_mbyte) - draw_cmdline(ccline.cmdpos, - (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos)); - else - draw_cmdline(ccline.cmdpos, 1); - msg_no_more = FALSE; + } else { + draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos)); + } + msg_no_more = false; cursorcmd(); ui_cursor_shape(); } @@ -2871,9 +3004,6 @@ void put_on_cmdline(char_u *str, int len, int redraw) msg_check(); } -static struct cmdline_info prev_ccline; -static int prev_ccline_used = FALSE; - /* * Save ccline, because obtaining the "=" register may execute "normal :cmd" * and overwrite it. But get_cmdline_str() may need it, thus make it @@ -2881,15 +3011,12 @@ static int prev_ccline_used = FALSE; */ static void save_cmdline(struct cmdline_info *ccp) { - if (!prev_ccline_used) { - memset(&prev_ccline, 0, sizeof(struct cmdline_info)); - prev_ccline_used = TRUE; - } - *ccp = prev_ccline; - prev_ccline = ccline; + *ccp = ccline; + ccline.prev_ccline = ccp; ccline.cmdbuff = NULL; ccline.cmdprompt = NULL; ccline.xpc = NULL; + ccline.special_char = NUL; } /* @@ -2897,8 +3024,7 @@ static void save_cmdline(struct cmdline_info *ccp) */ static void restore_cmdline(struct cmdline_info *ccp) { - ccline = prev_ccline; - prev_ccline = *ccp; + ccline = *ccp; } /* @@ -3066,17 +3192,25 @@ static void redrawcmdprompt(void) if (cmd_silent) return; - if (ccline.cmdfirstc != NUL) + if (ui_is_external(kUICmdline)) { + ui_ext_cmdline_show(&ccline); + return; + } + if (ccline.cmdfirstc != NUL) { msg_putchar(ccline.cmdfirstc); + } if (ccline.cmdprompt != NULL) { msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr); ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns; - /* do the reverse of set_cmdspos() */ - if (ccline.cmdfirstc != NUL) - --ccline.cmdindent; - } else - for (i = ccline.cmdindent; i > 0; --i) + // do the reverse of set_cmdspos() + if (ccline.cmdfirstc != NUL) { + ccline.cmdindent--; + } + } else { + for (i = ccline.cmdindent; i > 0; i--) { msg_putchar(' '); + } + } } /* @@ -3087,6 +3221,11 @@ void redrawcmd(void) if (cmd_silent) return; + if (ui_is_external(kUICmdline)) { + draw_cmdline(0, ccline.cmdlen); + return; + } + /* when 'incsearch' is set there may be no command line while redrawing */ if (ccline.cmdbuff == NULL) { ui_cursor_goto(cmdline_row, 0); @@ -3130,6 +3269,11 @@ static void cursorcmd(void) if (cmd_silent) return; + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_pos(ccline.cmdpos, ccline.level); + return; + } + if (cmdmsg_rl) { msg_row = cmdline_row + (ccline.cmdspos / (int)(Columns - 1)); msg_col = (int)Columns - (ccline.cmdspos % (int)(Columns - 1)) - 1; @@ -3147,6 +3291,9 @@ static void cursorcmd(void) void gotocmdline(int clr) { + if (ui_is_external(kUICmdline)) { + return; + } msg_start(); if (cmdmsg_rl) msg_col = Columns - 1; @@ -5204,13 +5351,15 @@ int get_history_idx(int histype) */ static struct cmdline_info *get_ccline_ptr(void) { - if ((State & CMDLINE) == 0) + if ((State & CMDLINE) == 0) { return NULL; - if (ccline.cmdbuff != NULL) + } else if (ccline.cmdbuff != NULL) { return &ccline; - if (prev_ccline_used && prev_ccline.cmdbuff != NULL) - return &prev_ccline; - return NULL; + } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) { + return ccline.prev_ccline; + } else { + return NULL; + } } /* @@ -5646,6 +5795,7 @@ static int ex_window(void) return K_IGNORE; } cmdwin_type = get_cmdline_type(); + cmdwin_level = ccline.level; // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); @@ -5698,6 +5848,9 @@ static int ex_window(void) curwin->w_cursor.col = ccline.cmdpos; changed_line_abv_curs(); invalidate_botline(); + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_hide(ccline.level); + } redraw_later(SOME_VALID); // Save the command line info, can be used recursively. @@ -5740,6 +5893,7 @@ static int ex_window(void) // Restore the command line info. restore_cmdline(&save_ccline); cmdwin_type = 0; + cmdwin_level = 0; exmode_active = save_exmode; @@ -5974,3 +6128,4 @@ static void set_search_match(pos_T *t) coladvance((colnr_T)MAXCOL); } } + diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 300e506854..0f3e132bc0 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -981,9 +981,10 @@ EXTERN int fill_diff INIT(= '-'); EXTERN int km_stopsel INIT(= FALSE); EXTERN int km_startsel INIT(= FALSE); -EXTERN int cedit_key INIT(= -1); /* key value of 'cedit' option */ -EXTERN int cmdwin_type INIT(= 0); /* type of cmdline window or 0 */ -EXTERN int cmdwin_result INIT(= 0); /* result of cmdline window or 0 */ +EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option +EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0 +EXTERN int cmdwin_result INIT(= 0); ///< result of cmdline window or 0 +EXTERN int cmdwin_level INIT(= 0); ///< cmdline recursion level EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--")); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 88232a55de..5efdb9a194 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -56,7 +56,6 @@ typedef struct { typedef struct { uint64_t id; size_t refcount; - size_t pending_requests; PMap(cstr_t) *subscribed_events; bool closed; ChannelType type; @@ -71,7 +70,6 @@ typedef struct { } data; uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; - kvec_t(WBuffer *) delayed_notifications; MultiQueue *events; } Channel; @@ -205,14 +203,7 @@ bool channel_send_event(uint64_t id, const char *name, Array args) } if (channel) { - if (channel->pending_requests) { - // Pending request, queue the notification for later sending. - const String method = cstr_as_string((char *)name); - WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); - kv_push(channel->delayed_notifications, buffer); - } else { - send_event(channel, name, args); - } + send_event(channel, name, args); } else { broadcast_event(name, args); } @@ -248,10 +239,8 @@ Object channel_send_call(uint64_t id, // Push the frame ChannelCallFrame frame = { request_id, false, false, NIL }; kv_push(channel->call_stack, &frame); - channel->pending_requests++; LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned); (void)kv_pop(channel->call_stack); - channel->pending_requests--; if (frame.errored) { if (frame.result.type == kObjectTypeString) { @@ -276,10 +265,6 @@ Object channel_send_call(uint64_t id, api_free_object(frame.result); } - if (!channel->pending_requests) { - send_delayed_notifications(channel); - } - decref(channel); return frame.errored ? NIL : frame.result; @@ -704,11 +689,7 @@ static void broadcast_event(const char *name, Array args) for (size_t i = 0; i < kv_size(subscribed); i++) { Channel *channel = kv_A(subscribed, i); - if (channel->pending_requests) { - kv_push(channel->delayed_notifications, buffer); - } else { - channel_write(channel, buffer); - } + channel_write(channel, buffer); } end: @@ -786,7 +767,6 @@ static void free_channel(Channel *channel) pmap_free(cstr_t)(channel->subscribed_events); kv_destroy(channel->call_stack); - kv_destroy(channel->delayed_notifications); if (channel->type != kChannelTypeProc) { multiqueue_free(channel->events); } @@ -811,11 +791,9 @@ static Channel *register_channel(ChannelType type, uint64_t id, rv->closed = false; rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); rv->id = id > 0 ? id : next_chan_id++; - rv->pending_requests = 0; rv->subscribed_events = pmap_new(cstr_t)(); rv->next_request_id = 1; kv_init(rv->call_stack); - kv_init(rv->delayed_notifications); pmap_put(uint64_t)(channels, rv->id, rv); ILOG("new channel %" PRIu64 " (%s): %s", rv->id, @@ -912,16 +890,6 @@ static WBuffer *serialize_response(uint64_t channel_id, return rv; } -static void send_delayed_notifications(Channel* channel) -{ - for (size_t i = 0; i < kv_size(channel->delayed_notifications); i++) { - WBuffer *buffer = kv_A(channel->delayed_notifications, i); - channel_write(channel, buffer); - } - - kv_size(channel->delayed_notifications) = 0; -} - static void incref(Channel *channel) { channel->refcount++; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index f5730cf70a..41e900eb4c 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -345,8 +345,9 @@ void update_screen(int type) if (need_highlight_changed) highlight_changed(); - if (type == CLEAR) { /* first clear screen */ - screenclear(); /* will reset clear_cmdline */ + if (type == CLEAR) { // first clear screen + screenclear(); // will reset clear_cmdline + cmdline_screen_cleared(); // clear external cmdline state type = NOT_VALID; } @@ -692,12 +693,18 @@ static void win_update(win_T *wp) if (wp->w_nrwidth != i) { type = NOT_VALID; wp->w_nrwidth = i; - } else if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0) { - /* - * When there are both inserted/deleted lines and specific lines to be - * redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw - * everything (only happens when redrawing is off for while). - */ + + if (buf->terminal) { + terminal_resize(buf->terminal, + (uint16_t)(MAX(0, curwin->w_width - win_col_off(curwin))), + (uint16_t)curwin->w_height); + } + } else if (buf->b_mod_set + && buf->b_mod_xlines != 0 + && wp->w_redraw_top != 0) { + // When there are both inserted/deleted lines and specific lines to be + // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw + // everything (only happens when redrawing is off for while). type = NOT_VALID; } else { /* diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index fb68292c77..ccf999c7ee 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -64,6 +64,7 @@ NEW_TESTS ?= \ test_normal.res \ test_profile.res \ test_quickfix.res \ + test_retab.res \ test_search.res \ test_signs.res \ test_smartindent.res \ diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim new file mode 100644 index 0000000000..f11a32bade --- /dev/null +++ b/src/nvim/testdir/test_retab.vim @@ -0,0 +1,77 @@ +" Test :retab +func SetUp() + new + call setline(1, "\ta \t b c ") +endfunc + +func TearDown() + bwipe! +endfunc + +func Retab(bang, n) + let l:old_tabstop = &tabstop + let l:old_line = getline(1) + exe "retab" . a:bang . a:n + let l:line = getline(1) + call setline(1, l:old_line) + if a:n > 0 + " :retab changes 'tabstop' to n with argument n > 0. + call assert_equal(a:n, &tabstop) + exe 'set tabstop=' . l:old_tabstop + else + " :retab does not change 'tabstop' with empty or n <= 0. + call assert_equal(l:old_tabstop, &tabstop) + endif + return l:line +endfunc + +func Test_retab() + set tabstop=8 noexpandtab + call assert_equal("\ta\t b c ", Retab('', '')) + call assert_equal("\ta\t b c ", Retab('', 0)) + call assert_equal("\ta\t b c ", Retab('', 8)) + call assert_equal("\ta\t b\t c\t ", Retab('!', '')) + call assert_equal("\ta\t b\t c\t ", Retab('!', 0)) + call assert_equal("\ta\t b\t c\t ", Retab('!', 8)) + + call assert_equal("\t\ta\t\t\tb c ", Retab('', 4)) + call assert_equal("\t\ta\t\t\tb\t\t c\t ", Retab('!', 4)) + + call assert_equal(" a\t\tb c ", Retab('', 10)) + call assert_equal(" a\t\tb c ", Retab('!', 10)) + + set tabstop=8 expandtab + call assert_equal(" a b c ", Retab('', '')) + call assert_equal(" a b c ", Retab('', 0)) + call assert_equal(" a b c ", Retab('', 8)) + call assert_equal(" a b c ", Retab('!', '')) + call assert_equal(" a b c ", Retab('!', 0)) + call assert_equal(" a b c ", Retab('!', 8)) + + call assert_equal(" a b c ", Retab(' ', 4)) + call assert_equal(" a b c ", Retab('!', 4)) + + call assert_equal(" a b c ", Retab(' ', 10)) + call assert_equal(" a b c ", Retab('!', 10)) + + set tabstop=4 noexpandtab + call assert_equal("\ta\t\tb c ", Retab('', '')) + call assert_equal("\ta\t\tb\t\t c\t ", Retab('!', '')) + call assert_equal("\t a\t\t\tb c ", Retab('', 3)) + call assert_equal("\t a\t\t\tb\t\t\tc\t ", Retab('!', 3)) + call assert_equal(" a\t b c ", Retab('', 5)) + call assert_equal(" a\t b\t\t c\t ", Retab('!', 5)) + + set tabstop=4 expandtab + call assert_equal(" a b c ", Retab('', '')) + call assert_equal(" a b c ", Retab('!', '')) + call assert_equal(" a b c ", Retab('', 3)) + call assert_equal(" a b c ", Retab('!', 3)) + call assert_equal(" a b c ", Retab('', 5)) + call assert_equal(" a b c ", Retab('!', 5)) +endfunc + +func Test_retab_error() + call assert_fails('retab -1', 'E487:') + call assert_fails('retab! -1', 'E487:') +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index e8d82d3b87..a31381eddf 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -898,7 +898,7 @@ static const int included_patches[] = { 209, 208, // 207, - // 206, + 206, 205, // 204, // 203 NA diff --git a/src/nvim/window.c b/src/nvim/window.c index c2d0a9b3b1..2d64409a1c 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3747,7 +3747,9 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, do_autochdir(); if (curbuf->terminal) { - terminal_resize(curbuf->terminal, curwin->w_width, curwin->w_height); + terminal_resize(curbuf->terminal, + (uint16_t)(MAX(0, curwin->w_width - win_col_off(curwin))), + (uint16_t)curwin->w_height); } } @@ -4946,7 +4948,9 @@ void win_new_width(win_T *wp, int width) if (wp->w_buffer->terminal) { if (wp->w_height != 0) { - terminal_resize(wp->w_buffer->terminal, wp->w_width, 0); + terminal_resize(wp->w_buffer->terminal, + (uint16_t)(MAX(0, curwin->w_width - win_col_off(curwin))), + 0); } } } |