diff options
-rw-r--r-- | runtime/doc/channel.txt | 99 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 73 | ||||
-rw-r--r-- | src/nvim/buffer.c | 88 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 61 | ||||
-rw-r--r-- | src/nvim/getchar.c | 28 | ||||
-rw-r--r-- | src/nvim/message.c | 2 | ||||
-rw-r--r-- | src/nvim/ops.c | 32 | ||||
-rw-r--r-- | src/nvim/screen.c | 4 | ||||
-rw-r--r-- | src/nvim/sign.c | 6 | ||||
-rw-r--r-- | src/nvim/testdir/test_writefile.vim | 7 | ||||
-rw-r--r-- | test/functional/ui/messages_spec.lua | 37 | ||||
-rw-r--r-- | test/functional/ui/wildmode_spec.lua | 85 | ||||
-rw-r--r-- | test/unit/os/fs_spec.lua | 147 | ||||
-rw-r--r-- | third-party/CMakeLists.txt | 9 |
14 files changed, 382 insertions, 296 deletions
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 323d9ef004..f3fa3fa180 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -43,59 +43,66 @@ functions like |chansend()| consume channel ids. ============================================================================== 2. Reading and writing raw bytes *channel-bytes* -By default, channels opened by vimscript functions will operate with raw -bytes. Additionally, for a job channel using rpc, bytes can still be -read over its stderr. Similarily, only bytes can be written to nvim's own stderr. - - *channel-callback* *on_stdout* *on_stderr* *on_stdin* *on_data* -Scripts can react to channel activity (received data) via callback functions -assigned to the `on_stdout`, `on_stderr`, `on_stdin`, and `on_data` options. -Callbacks should be fast, avoid potentially slow/expensive work. +Channels opened by Vimscript functions operate with raw bytes by default. For +a job channel using RPC, bytes can still be read over its stderr. Similarily, +only bytes can be written to Nvim's own stderr. + + *channel-callback* +on_stdout({chan-id}, {data}, {name}) *on_stdout* +on_stderr({chan-id}, {data}, {name}) *on_stderr* +on_stdin({chan-id}, {data}, {name}) *on_stdin* +on_data({chan-id}, {data}, {name}) *on_data* + Scripts can react to channel activity (received data) via callback + functions assigned to the `on_stdout`, `on_stderr`, `on_stdin`, or + `on_data` option keys. Callbacks should be fast: avoid potentially + slow/expensive work. + + Parameters: ~ + {chan-id} Channel handle. |channel-id| + {data} Raw data (|readfile()|-style list of strings) read from + the channel. EOF is a single-item list: `['']`. First and + last items may be partial lines! |channel-lines| + {name} Stream name (string) like "stdout", so the same function + can handle multiple streams. Event names depend on how the + channel was opened and in what mode/protocol. *channel-buffered* -By default the callback is invoked immediately as data is available; empty -data indicates EOF (stream closed). Alternatively, set the `stdout_buffered`, -`stderr_buffered`, `stdin_buffered`, or `data_buffered` options to invoke the -callback only on EOF, after all output was gathered. + The callback is invoked immediately as data is available, where + a single-item list `['']` indicates EOF (stream closed). Alternatively + set the `stdout_buffered`, `stderr_buffered`, `stdin_buffered`, or + `data_buffered` option keys to invoke the callback only after all output + was gathered and the stream was closed. *E5210* -If the stream is set as buffered without assigning a callback, the data is -saved in the options dict with the stream name as key. This requires a new -options dict for each opened channel (|copy()|). If the stream name key -is already set, error E5210 is raised. - -Channel callback functions accept these arguments: - - 0: |channel-id| - 1: Raw data read from the channel, formatted as a |readfile()|-style - list. If EOF occured, a single empty string `['']` will be passed in. - Note that the items in this list do not directly correspond to actual - lines in the output. See |channel-lines| - 2: Stream name as a string, like `"stdout"`. This is to allow multiple - stream handlers to be implemented by the same function. The available - events depend on how the channel was opened and in what mode/protocol. + If a buffering mode is used without a callback, the data is saved in the + stream {name} key of the options dict. It is an error if the key exists. *channel-lines* - Note: - stream event handlers may receive partial (incomplete) lines. For a given - invocation of on_stdout etc, `a:data` is not guaranteed to end - with a newline. - - `abcdefg` may arrive as `['abc']`, `['defg']`. - - `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`, - `['','efg']`, or even `['ab']`, `['c','efg']`. - - If you only are interested in complete output when the process exits, - use buffered mode. Otherwise, an easy way to deal with this: - initialize a list as `['']`, then append to it as follows: > - let s:chunks = [''] - func! s:on_event(job_id, data, event) dict - let s:chunks[-1] .= a:data[0] - call extend(s:chunks, a:data[1:]) - endf + Stream event handlers receive data as it becomes available from the OS, + thus the first and last items in the {data} list may be partial lines. + Empty string completes the previous partial line. Examples (not including + the final `['']` emitted at EOF): + - `foobar` may arrive as `['fo'], ['obar']` + - `foo\nbar` may arrive as + `['foo','bar']` + or `['foo',''], ['bar']` + or `['foo'], ['','bar']` + or `['fo'], ['o','bar']` + There are two ways to deal with this: + 1. To wait for the entire output, use |channel-buffered| mode. + 2. To read line-by-line, use the following code: > + let s:lines = [''] + func! s:on_event(job_id, data, event) dict + let eof = (a:data == ['']) + " Complete the previous line. + let s:lines[-1] .= a:data[0] + " Append (last item may be a partial line, until EOF). + call extend(s:lines, a:data[1:]) + endf < -Additionally, if the callbacks are Dictionary functions, |self| can be used to -refer to the options dictionary containing the callbacks. |Partial|s can also be -used as callbacks. +If the callback functions are |Dictionary-function|s, |self| refers to the +options dictionary containing the callbacks. |Partial|s can also be used as +callbacks. Data can be sent to the channel using the |chansend()| function. Here is a simple example, echoing some data through a cat-process: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index cc97117ffd..00194b4613 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -634,15 +634,15 @@ Expression syntax summary, from least to most significant: expr5 isnot expr5 different |List| instance |expr5| expr6 - expr6 + expr6 .. number addition or list concatenation - expr6 - expr6 .. number subtraction - expr6 . expr6 .. string concatenation - expr6 .. expr6 .. string concatenation + expr6 + expr6 ... number addition, list or blob concatenation + expr6 - expr6 ... number subtraction + expr6 . expr6 ... string concatenation + expr6 .. expr6 ... string concatenation |expr6| expr7 - expr7 * expr7 .. number multiplication - expr7 / expr7 .. number division - expr7 % expr7 .. number modulo + expr7 * expr7 ... number multiplication + expr7 / expr7 ... number division + expr7 % expr7 ... number modulo |expr7| expr8 ! expr7 logical NOT @@ -708,7 +708,9 @@ use in a variable such as "a:1". expr2 and expr3 *expr2* *expr3* --------------- - *expr-barbar* *expr-&&* +expr3 || expr3 .. logical OR *expr-barbar* +expr4 && expr4 .. logical AND *expr-&&* + The "||" and "&&" operators take one argument on each side. The arguments are (converted to) Numbers. The result is: @@ -848,10 +850,10 @@ can be matched like an ordinary character. Examples: expr5 and expr6 *expr5* *expr6* --------------- -expr6 + expr6 .. Number addition or |List| concatenation *expr-+* -expr6 - expr6 .. Number subtraction *expr--* -expr6 . expr6 .. String concatenation *expr-.* -expr6 .. expr6 .. String concatenation *expr-..* +expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+* +expr6 - expr6 Number subtraction *expr--* +expr6 . expr6 String concatenation *expr-.* +expr6 .. expr6 String concatenation *expr-..* For |Lists| only "+" is possible and then both expr6 must be a list. The result is a new list with the two lists Concatenated. @@ -859,11 +861,11 @@ result is a new list with the two lists Concatenated. For String concatenation ".." is preferred, since "." is ambiguous, it is also used for |Dict| member access and floating point numbers. -expr7 * expr7 .. Number multiplication *expr-star* -expr7 / expr7 .. Number division *expr-/* -expr7 % expr7 .. Number modulo *expr-%* +expr7 * expr7 Number multiplication *expr-star* +expr7 / expr7 Number division *expr-/* +expr7 % expr7 Number modulo *expr-%* -For all, except ".", Strings are converted to Numbers. +For all, except "." and "..", Strings are converted to Numbers. For bitwise operators see |and()|, |or()| and |xor()|. Note the difference between "+" and ".": @@ -1054,11 +1056,6 @@ These are INVALID: 3. empty {M} 1e40 missing .{M} - *float-pi* *float-e* -A few useful values to copy&paste: > - :let pi = 3.14159265359 - :let e = 2.71828182846 - Rationale: Before floating point was introduced, the text "123.456" was interpreted as the two numbers "123" and "456", both converted to a string and concatenated, @@ -1067,6 +1064,15 @@ could not find it intentionally being used in Vim scripts, this backwards incompatibility was accepted in favor of being able to use the normal notation for floating point numbers. + *float-pi* *float-e* +A few useful values to copy&paste: > + :let pi = 3.14159265359 + :let e = 2.71828182846 +Or, if you don't want to write them in as floating-point literals, you can +also use functions, like the following: > + :let pi = acos(-1.0) + :let e = exp(1.0) +< *floating-point-precision* The precision and range of floating points numbers depends on what "double" means in the library Vim was compiled with. There is no way to change this at @@ -1106,8 +1112,10 @@ A string constant accepts these special characters: \\ backslash \" double quote \<xxx> Special key named "xxx". e.g. "\<C-W>" for CTRL-W. This is for use - in mappings, the 0x80 byte is escaped. Don't use <Char-xxxx> to get a - utf-8 character, use \uxxxx as mentioned above. + in mappings, the 0x80 byte is escaped. + To use the double quote character it must be escaped: "<M-\">". + Don't use <Char-xxxx> to get a utf-8 character, use \uxxxx as + mentioned above. Note that "\xff" is stored as the byte 255, which may be invalid in some encodings. Use "\u00ff" to store character 255 correctly as UTF-8. @@ -1216,8 +1224,8 @@ The arguments are optional. Example: > *closure* Lambda expressions can access outer scope variables and arguments. This is often called a closure. Example where "i" and "a:arg" are used in a lambda -while they exist in the function scope. They remain valid even after the -function returns: > +while they already exist in the function scope. They remain valid even after +the function returns: > :function Foo(arg) : let i = 3 : return {x -> x + i - a:arg} @@ -1225,8 +1233,11 @@ function returns: > :let Bar = Foo(4) :echo Bar(6) < 5 -See also |:func-closure|. Lambda and closure support can be checked with: > - if has('lambda') +Note that the variables must exist in the outer scope before the lamba is +defined for this to work. See also |:func-closure|. + +Lambda and closure support can be checked with: > + if has('lambda') Examples for using a lambda expression with |sort()|, |map()| and |filter()|: > :echo map([1, 2, 3], {idx, val -> val + 1}) @@ -5277,9 +5288,9 @@ jobstart({cmd}[, {opts}]) *jobstart()* *jobstart-options* {opts} is a dictionary with these keys: |on_stdout|: stdout event handler (function name or |Funcref|) - stdout_buffered : read stdout in |buffered| mode. + stdout_buffered : read stdout in |channel-buffered| mode. |on_stderr|: stderr event handler (function name or |Funcref|) - stderr_buffered : read stderr in |buffered| mode. + stderr_buffered : read stderr in |channel-buffered| mode. |on_exit| : exit event handler (function name or |Funcref|) cwd : Working directory of the job; defaults to |current-directory|. @@ -7686,7 +7697,7 @@ sockconnect({mode}, {address}, {opts}) *sockconnect()* {opts} is a dictionary with these keys: |on_data| : callback invoked when data was read from socket - data_buffered : read data from socket in |buffered| mode. + data_buffered : read socket data in |channel-buffered| mode. rpc : If set, |msgpack-rpc| will be used to communicate over the socket. Returns: @@ -7855,7 +7866,7 @@ stdioopen({opts}) *stdioopen()* {opts} is a dictionary with these keys: |on_stdin| : callback invoked when stdin is written to. - stdin_buffered : read stdin in |buffered| mode. + stdin_buffered : read stdin in |channel-buffered| mode. rpc : If set, |msgpack-rpc| will be used to communicate over stdio Returns: diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 078d4fe782..0c14656b33 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5256,92 +5256,28 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) return false; } -static int sign_compare(const void *a1, const void *a2) -{ - const signlist_T *s1 = *(const signlist_T **)a1; - const signlist_T *s2 = *(const signlist_T **)a2; - - // Sort by line number, priority and id - - if (s1->lnum > s2->lnum) { - return 1; - } - if (s1->lnum < s2->lnum) { - return -1; - } - if (s1->priority > s2->priority) { - return -1; - } - if (s1->priority < s2->priority) { - return 1; - } - if (s1->id > s2->id) { - return -1; - } - if (s1->id < s2->id) { - return 1; - } - - return 0; -} - int buf_signcols(buf_T *buf) { if (buf->b_signcols_max == -1) { signlist_T *sign; // a sign in the signlist - signlist_T **signs_array; - signlist_T **prev_sign; - int nr_signs = 0, i = 0, same; - - // Count the number of signs - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - nr_signs++; - } - - // Make an array of all the signs - signs_array = xcalloc((size_t)nr_signs, sizeof(*sign)); - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - signs_array[i] = sign; - i++; - } - - // Sort the array - qsort(signs_array, (size_t)nr_signs, sizeof(signlist_T *), - sign_compare); - - // Find the maximum amount of signs existing in a single line buf->b_signcols_max = 0; + int linesum = 0; + linenr_T curline = 0; - same = 1; - for (i = 1; i < nr_signs; i++) { - if (signs_array[i - 1]->lnum != signs_array[i]->lnum) { - if (buf->b_signcols_max < same) { - buf->b_signcols_max = same; - } - same = 1; - } else { - same++; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->lnum > curline) { + if (linesum > buf->b_signcols_max) { + buf->b_signcols_max = linesum; } + curline = sign->lnum; + linesum = 0; + } + linesum++; } - - if (nr_signs > 0 && buf->b_signcols_max < same) { - buf->b_signcols_max = same; - } - - // Recreate the linked list with the sorted order of the array - buf->b_signlist = NULL; - prev_sign = &buf->b_signlist; - - for (i = 0; i < nr_signs; i++) { - sign = signs_array[i]; - sign->next = NULL; - *prev_sign = sign; - - prev_sign = &sign->next; + if (linesum > buf->b_signcols_max) { + buf->b_signcols_max = linesum; } - xfree(signs_array); - // Check if we need to redraw if (buf->b_signcols_max != buf->b_signcols) { buf->b_signcols = buf->b_signcols_max; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9e056d449b..ec4b16fbb0 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1948,28 +1948,26 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Check for '|' to separate commands and '"' to start comments. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & TRLBAR) && !ea.usefilter) + if ((ea.argt & TRLBAR) && !ea.usefilter) { separate_nextcmd(&ea); - - /* - * Check for <newline> to end a shell command. - * Also do this for ":read !cmd", ":write !cmd" and ":global". - * Any others? - */ - else if (ea.cmdidx == CMD_bang - || ea.cmdidx == CMD_global - || ea.cmdidx == CMD_vglobal - || ea.usefilter) { - for (p = ea.arg; *p; ++p) { - /* Remove one backslash before a newline, so that it's possible to - * pass a newline to the shell and also a newline that is preceded - * with a backslash. This makes it impossible to end a shell - * command in a backslash, but that doesn't appear useful. - * Halving the number of backslashes is incompatible with previous - * versions. */ - if (*p == '\\' && p[1] == '\n') + } else if (ea.cmdidx == CMD_bang + || ea.cmdidx == CMD_terminal + || ea.cmdidx == CMD_global + || ea.cmdidx == CMD_vglobal + || ea.usefilter) { + // Check for <newline> to end a shell command. + // Also do this for ":read !cmd", ":write !cmd" and ":global". + // Any others? + for (p = ea.arg; *p; p++) { + // Remove one backslash before a newline, so that it's possible to + // pass a newline to the shell and also a newline that is preceded + // with a backslash. This makes it impossible to end a shell + // command in a backslash, but that doesn't appear useful. + // Halving the number of backslashes is incompatible with previous + // versions. + if (*p == '\\' && p[1] == '\n') { STRMOVE(p, p + 1); - else if (*p == '\n') { + } else if (*p == '\n') { ea.nextcmd = p + 1; *p = NUL; break; @@ -4122,13 +4120,14 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) if (!eap->usefilter && !escaped && eap->cmdidx != CMD_bang - && eap->cmdidx != CMD_make - && eap->cmdidx != CMD_lmake && eap->cmdidx != CMD_grep - && eap->cmdidx != CMD_lgrep && eap->cmdidx != CMD_grepadd - && eap->cmdidx != CMD_lgrepadd && eap->cmdidx != CMD_hardcopy + && eap->cmdidx != CMD_lgrep + && eap->cmdidx != CMD_lgrepadd + && eap->cmdidx != CMD_lmake + && eap->cmdidx != CMD_make + && eap->cmdidx != CMD_terminal && !(eap->argt & NOSPC) ) { char_u *l; @@ -4150,8 +4149,10 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) } } - /* For a shell command a '!' must be escaped. */ - if ((eap->usefilter || eap->cmdidx == CMD_bang) + // For a shell command a '!' must be escaped. + if ((eap->usefilter + || eap->cmdidx == CMD_bang + || eap->cmdidx == CMD_terminal) && vim_strpbrk(repl, (char_u *)"!") != NULL) { char_u *l; @@ -8399,7 +8400,7 @@ static void ex_pedit(exarg_T *eap) g_do_tagpreview = p_pvh; prepare_tagpreview(true); - keep_help_flag = curwin_save->w_buffer->b_help; + keep_help_flag = bt_help(curwin_save->w_buffer); do_exedit(eap, NULL); keep_help_flag = FALSE; if (curwin != curwin_save && win_valid(curwin_save)) { @@ -9046,7 +9047,7 @@ makeopens( for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) { if (ses_do_win(wp) && wp->w_buffer->b_ffname != NULL - && !wp->w_buffer->b_help + && !bt_help(wp->w_buffer) && !bt_nofile(wp->w_buffer) ) { if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 @@ -9337,7 +9338,7 @@ static int ses_do_win(win_T *wp) || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) { return ssop_flags & SSOP_BLANK; } - if (wp->w_buffer->b_help) { + if (bt_help(wp->w_buffer)) { return ssop_flags & SSOP_HELP; } return true; @@ -9477,7 +9478,7 @@ put_view( */ if ((*flagp & SSOP_FOLDS) && wp->w_buffer->b_ffname != NULL - && (*wp->w_buffer->b_p_bt == NUL || wp->w_buffer->b_help) + && (*wp->w_buffer->b_p_bt == NUL || bt_help(wp->w_buffer)) ) { if (put_folds(fd, wp) == FAIL) return FAIL; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ef522242c6..7e4a0e1321 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1422,8 +1422,8 @@ int vgetc(void) } - /* a keypad or special function key was not mapped, use it like - * its ASCII equivalent */ + // a keypad or special function key was not mapped, use it like + // its ASCII equivalent switch (c) { case K_KPLUS: c = '+'; break; case K_KMINUS: c = '-'; break; @@ -1475,25 +1475,25 @@ int vgetc(void) case K_XRIGHT: c = K_RIGHT; break; } - /* For a multi-byte character get all the bytes and return the - * converted character. - * Note: This will loop until enough bytes are received! - */ - if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) { + // For a multi-byte character get all the bytes and return the + // converted character. + // Note: This will loop until enough bytes are received! + if ((n = MB_BYTE2LEN_CHECK(c)) > 1) { no_mapping++; buf[0] = (char_u)c; for (i = 1; i < n; i++) { buf[i] = (char_u)vgetorpeek(true); if (buf[i] == K_SPECIAL ) { - /* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, - * which represents a K_SPECIAL (0x80), - * or a CSI - KS_EXTRA - KE_CSI sequence, which represents - * a CSI (0x9B), - * of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. */ - c = vgetorpeek(TRUE); - if (vgetorpeek(TRUE) == (int)KE_CSI && c == KS_EXTRA) + // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, + // which represents a K_SPECIAL (0x80), + // or a CSI - KS_EXTRA - KE_CSI sequence, which represents + // a CSI (0x9B), + // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. + c = vgetorpeek(true); + if (vgetorpeek(true) == (int)KE_CSI && c == KS_EXTRA) { buf[i] = CSI; + } } } no_mapping--; diff --git a/src/nvim/message.c b/src/nvim/message.c index 7498091ade..882fce504b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2053,7 +2053,7 @@ int msg_scrollsize(void) /* * Scroll the screen up one line for displaying the next message line. */ -static void msg_scroll_up(void) +void msg_scroll_up(void) { if (!msg_did_scroll) { ui_call_win_scroll_over_start(); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9b68b713ad..2a3b7beb8e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -983,25 +983,29 @@ do_execreg( EMSG(_(e_nolastcmd)); return FAIL; } - XFREE_CLEAR(new_last_cmdline); // don't keep the cmdline containing @: + // don't keep the cmdline containing @: + XFREE_CLEAR(new_last_cmdline); // Escape all control characters with a CTRL-V p = vim_strsave_escaped_ext( last_cmdline, - (char_u *) - "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037", - Ctrl_V, FALSE); - /* When in Visual mode "'<,'>" will be prepended to the command. - * Remove it when it's already there. */ - if (VIsual_active && STRNCMP(p, "'<,'>", 5) == 0) - retval = put_in_typebuf(p + 5, TRUE, TRUE, silent); - else - retval = put_in_typebuf(p, TRUE, TRUE, silent); + (char_u *)"\001\002\003\004\005\006\007" + "\010\011\012\013\014\015\016\017" + "\020\021\022\023\024\025\026\027" + "\030\031\032\033\034\035\036\037", + Ctrl_V, false); + // When in Visual mode "'<,'>" will be prepended to the command. + // Remove it when it's already there. + if (VIsual_active && STRNCMP(p, "'<,'>", 5) == 0) { + retval = put_in_typebuf(p + 5, true, true, silent); + } else { + retval = put_in_typebuf(p, true, true, silent); + } xfree(p); } else if (regname == '=') { p = get_expr_line(); if (p == NULL) return FAIL; - retval = put_in_typebuf(p, TRUE, colon, silent); + retval = put_in_typebuf(p, true, colon, silent); xfree(p); } else if (regname == '.') { /* use last inserted text */ p = get_last_insert_save(); @@ -1009,7 +1013,7 @@ do_execreg( EMSG(_(e_noinstext)); return FAIL; } - retval = put_in_typebuf(p, FALSE, colon, silent); + retval = put_in_typebuf(p, false, colon, silent); xfree(p); } else { yankreg_T *reg = get_yank_register(regname, YREG_PASTE); @@ -1075,8 +1079,8 @@ static void put_reedit_in_typebuf(int silent) */ static int put_in_typebuf( char_u *s, - int esc, - int colon, /* add ':' before the line */ + bool esc, + bool colon, // add ':' before the line int silent ) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index fe9fba7af6..a20c91845d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4765,12 +4765,12 @@ win_redr_status_matches ( row = cmdline_row - 1; if (row >= 0) { - if (wild_menu_showing == 0) { + if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) { if (msg_scrolled > 0) { /* Put the wildmenu just above the command line. If there is * no room, scroll the screen one line up. */ if (cmdline_row == Rows - 1) { - grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns); + msg_scroll_up(); msg_scrolled++; } else { cmdline_row++; diff --git a/src/nvim/sign.c b/src/nvim/sign.c index ac26fd0137..8c85fbdaa7 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -231,9 +231,11 @@ static void insert_sign_by_lnum_prio( { signlist_T *sign; - // keep signs sorted by lnum and by priority: insert new sign at + // keep signs sorted by lnum, priority and id: insert new sign at // the proper position in the list for this lnum. - while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) { + while (prev != NULL && prev->lnum == lnum + && (prev->priority < prio + || (prev->priority == prio && prev->id <= id))) { prev = prev->prev; } if (prev == NULL) { diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index b4585a72ef..aeee6ad88b 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -38,7 +38,7 @@ func Test_writefile_fails_conversion() endif " Without a backup file the write won't happen if there is a conversion " error. - set nobackup nowritebackup + set nobackup nowritebackup backupdir=. backupskip= new let contents = ["line one", "line two"] call writefile(contents, 'Xfile') @@ -49,7 +49,7 @@ func Test_writefile_fails_conversion() call delete('Xfile') bwipe! - set backup& writebackup& + set backup& writebackup& backupdir&vim backupskip&vim endfunc func Test_writefile_fails_conversion2() @@ -58,7 +58,7 @@ func Test_writefile_fails_conversion2() endif " With a backup file the write happens even if there is a conversion error, " but then the backup file must remain - set nobackup writebackup + set nobackup writebackup backupdir=. backupskip= let contents = ["line one", "line two"] call writefile(contents, 'Xfile_conversion_err') edit Xfile_conversion_err @@ -71,6 +71,7 @@ func Test_writefile_fails_conversion2() call delete('Xfile_conversion_err') call delete('Xfile_conversion_err~') bwipe! + set backup& writebackup& backupdir&vim backupskip&vim endfunc func SetFlag(timer) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 50e354a7ee..34b9ac8882 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -681,6 +681,43 @@ describe('ui/ext_messages', function() end) end) +describe('ui/ext_messages', function() + local screen + before_each(function() + clear() + screen = Screen.new(25, 7) + screen:attach({rgb=true, ext_messages=true}) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, reverse = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + end) + + it('wildmode=list', function() + command('set wildmenu wildmode=list') + feed(':set wildm<tab>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]], messages={{ + content = {{'wildmenu wildmode'}}, + kind = '', + }}, + cmdline={{ + firstc = ':', + content = {{ 'set wildm' }}, + pos = 9, + }}} + end) +end) + describe('ui/builtin messages', function() local screen before_each(function() diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index f4b80fd428..f960b793c9 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -169,6 +169,91 @@ describe("'wildmenu'", function() end) end) +describe("'wildmenu'", function() + local screen + before_each(function() + clear() + -- screen needs to be more than 5 rows + -- otherwise the tabline is covered and will be redrawn + screen = Screen.new(25, 7) + screen:attach() + end) + after_each(function() + screen:detach() + end) + + it('wildmode=list,full and display+=msgsep interact correctly #10092', function() + command('set display+=msgsep') + command('set wildmenu wildmode=list,full') + command('set showtabline=2') + feed(':set wildm<tab>') + screen:expect([[ + [No Name] | + | + ~ | + | + :set wildm | + wildmenu wildmode | + :set wildm^ | + ]]) + feed('<tab>') -- trigger wildmode full + screen:expect([[ + [No Name] | + | + | + :set wildm | + wildmenu wildmode | + wildmenu wildmode | + :set wildmenu^ | + ]]) + feed('<Esc>') + screen:expect([[ + [No Name] | + ^ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) + + it('wildmode=list,full and display-=msgsep interact correctly', function() + command('set display-=msgsep') + command('set wildmenu wildmode=list,full') + feed(':set wildm<tab>') + screen:expect([[ + ~ | + ~ | + ~ | + ~ | + :set wildm | + wildmenu wildmode | + :set wildm^ | + ]]) + feed('<tab>') -- trigger wildmode full + screen:expect([[ + ~ | + ~ | + ~ | + :set wildm | + wildmenu wildmode | + wildmenu wildmode | + :set wildmenu^ | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) +end) + describe('command line completion', function() local screen before_each(function() diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index ddb438eb3d..f1ec94ae87 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -860,86 +860,87 @@ describe('fs.c', function() describe('FileInfo', function() local function file_info_new() - local file_info = ffi.new('FileInfo[1]') - file_info[0].stat.st_ino = 0 - file_info[0].stat.st_dev = 0 - return file_info + local info = ffi.new('FileInfo[1]') + info[0].stat.st_ino = 0 + info[0].stat.st_dev = 0 + return info end - local function is_file_info_filled(file_info) - return file_info[0].stat.st_ino > 0 and file_info[0].stat.st_dev > 0 + -- Returns true if the FileInfo object has non-empty fields. + local function has_fileinfo(info) + return info[0].stat.st_ino > 0 and info[0].stat.st_dev > 0 end local function file_id_new() - local file_info = ffi.new('FileID[1]') - file_info[0].inode = 0 - file_info[0].device_id = 0 - return file_info + local info = ffi.new('FileID[1]') + info[0].inode = 0 + info[0].device_id = 0 + return info end describe('os_fileinfo', function() itp('returns false if path=NULL', function() - local file_info = file_info_new() - assert.is_false((fs.os_fileinfo(nil, file_info))) + local info = file_info_new() + assert.is_false((fs.os_fileinfo(nil, info))) end) itp('returns false if given a non-existing file', function() - local file_info = file_info_new() - assert.is_false((fs.os_fileinfo('/non-existent', file_info))) + local info = file_info_new() + assert.is_false((fs.os_fileinfo('/non-existent', info))) end) - itp('returns true if given an existing file and fills file_info', function() - local file_info = file_info_new() + itp('returns true if given an existing file and fills FileInfo', function() + local info = file_info_new() local path = 'unit-test-directory/test.file' - assert.is_true((fs.os_fileinfo(path, file_info))) - assert.is_true((is_file_info_filled(file_info))) + assert.is_true((fs.os_fileinfo(path, info))) + assert.is_true((has_fileinfo(info))) end) - itp('returns the file info of the linked file, not the link', function() - local file_info = file_info_new() + itp('returns the FileInfo of the linked file, not the link', function() + local info = file_info_new() local path = 'unit-test-directory/test_link.file' - assert.is_true((fs.os_fileinfo(path, file_info))) - assert.is_true((is_file_info_filled(file_info))) - local mode = tonumber(file_info[0].stat.st_mode) + assert.is_true((fs.os_fileinfo(path, info))) + assert.is_true((has_fileinfo(info))) + local mode = tonumber(info[0].stat.st_mode) return eq(ffi.C.kS_IFREG, (bit.band(mode, ffi.C.kS_IFMT))) end) end) describe('os_fileinfo_link', function() - itp('returns false if given a non-existing file', function() - local file_info = file_info_new() - assert.is_false((fs.os_fileinfo_link('/non-existent', file_info))) + itp('returns false for non-existing file', function() + local info = file_info_new() + assert.is_false((fs.os_fileinfo_link('/non-existent', info))) end) - itp('returns true if given an existing file and fills file_info', function() - local file_info = file_info_new() + itp('returns true and fills FileInfo for existing file', function() + local info = file_info_new() local path = 'unit-test-directory/test.file' - assert.is_true((fs.os_fileinfo_link(path, file_info))) - assert.is_true((is_file_info_filled(file_info))) + assert.is_true((fs.os_fileinfo_link(path, info))) + assert.is_true((has_fileinfo(info))) end) - itp('returns the file info of the link, not the linked file', function() - local file_info = file_info_new() - local path = 'unit-test-directory/test_link.file' - assert.is_true((fs.os_fileinfo_link(path, file_info))) - assert.is_true((is_file_info_filled(file_info))) - local mode = tonumber(file_info[0].stat.st_mode) + itp('returns FileInfo of the link, not its target', function() + local info = file_info_new() + local link = 'unit-test-directory/test_link.file' + assert.is_true((fs.os_fileinfo_link(link, info))) + assert.is_true((has_fileinfo(info))) + local mode = tonumber(info[0].stat.st_mode) eq(ffi.C.kS_IFLNK, (bit.band(mode, ffi.C.kS_IFMT))) end) end) describe('os_fileinfo_fd', function() itp('returns false if given an invalid file descriptor', function() - local file_info = file_info_new() - assert.is_false((fs.os_fileinfo_fd(-1, file_info))) + local info = file_info_new() + assert.is_false((fs.os_fileinfo_fd(-1, info))) end) - itp('returns true if given a file descriptor and fills file_info', function() - local file_info = file_info_new() + itp('returns true if given a file descriptor and fills FileInfo', function() + local info = file_info_new() local path = 'unit-test-directory/test.file' local fd = ffi.C.open(path, 0) - assert.is_true((fs.os_fileinfo_fd(fd, file_info))) - assert.is_true((is_file_info_filled(file_info))) + assert.is_true((fs.os_fileinfo_fd(fd, info))) + assert.is_true((has_fileinfo(info))) ffi.C.close(fd) end) end) @@ -976,24 +977,24 @@ describe('fs.c', function() end) describe('os_fileinfo_id', function() - itp('extracts ino/dev from file_info into file_id', function() - local file_info = file_info_new() + itp('extracts ino/dev from FileInfo into file_id', function() + local info = file_info_new() local file_id = file_id_new() local path = 'unit-test-directory/test.file' - assert.is_true((fs.os_fileinfo(path, file_info))) - fs.os_fileinfo_id(file_info, file_id) - eq(file_info[0].stat.st_ino, file_id[0].inode) - eq(file_info[0].stat.st_dev, file_id[0].device_id) + assert.is_true((fs.os_fileinfo(path, info))) + fs.os_fileinfo_id(info, file_id) + eq(info[0].stat.st_ino, file_id[0].inode) + eq(info[0].stat.st_dev, file_id[0].device_id) end) end) describe('os_fileinfo_inode', function() - itp('returns the inode from file_info', function() - local file_info = file_info_new() + itp('returns the inode from FileInfo', function() + local info = file_info_new() local path = 'unit-test-directory/test.file' - assert.is_true((fs.os_fileinfo(path, file_info))) - local inode = fs.os_fileinfo_inode(file_info) - eq(file_info[0].stat.st_ino, inode) + assert.is_true((fs.os_fileinfo(path, info))) + local inode = fs.os_fileinfo_inode(info) + eq(info[0].stat.st_ino, inode) end) end) @@ -1005,9 +1006,9 @@ describe('fs.c', function() file:flush() file:close() local size = lfs.attributes(path, 'size') - local file_info = file_info_new() - assert.is_true(fs.os_fileinfo(path, file_info)) - eq(size, fs.os_fileinfo_size(file_info)) + local info = file_info_new() + assert.is_true(fs.os_fileinfo(path, info)) + eq(size, fs.os_fileinfo_size(info)) end) end) @@ -1015,12 +1016,12 @@ describe('fs.c', function() itp('returns the correct number of hardlinks', function() local path = 'unit-test-directory/test.file' local path_link = 'unit-test-directory/test_hlink.file' - local file_info = file_info_new() - assert.is_true(fs.os_fileinfo(path, file_info)) - eq(1, fs.os_fileinfo_hardlinks(file_info)) + local info = file_info_new() + assert.is_true(fs.os_fileinfo(path, info)) + eq(1, fs.os_fileinfo_hardlinks(info)) lfs.link(path, path_link) - assert.is_true(fs.os_fileinfo(path, file_info)) - eq(2, fs.os_fileinfo_hardlinks(file_info)) + assert.is_true(fs.os_fileinfo(path, info)) + eq(2, fs.os_fileinfo_hardlinks(info)) end) end) @@ -1032,15 +1033,15 @@ describe('fs.c', function() -- https://github.com/keplerproject/luafilesystem/pull/44 -- using this workaround for now: local blksize = lfs.attributes(path).blksize - local file_info = file_info_new() - assert.is_true(fs.os_fileinfo(path, file_info)) + local info = file_info_new() + assert.is_true(fs.os_fileinfo(path, info)) if blksize then - eq(blksize, fs.os_fileinfo_blocksize(file_info)) + eq(blksize, fs.os_fileinfo_blocksize(info)) else -- luafs dosn't support blksize on windows -- libuv on windows returns a constant value as blocksize -- checking for this constant value should be enough - eq(2048, fs.os_fileinfo_blocksize(file_info)) + eq(2048, fs.os_fileinfo_blocksize(info)) end end) end) @@ -1080,23 +1081,23 @@ describe('fs.c', function() end) describe('os_fileid_equal_fileinfo', function() - itp('returns true if file_id and file_info represent the same file', function() + itp('returns true if file_id and FileInfo represent the same file', function() local file_id = file_id_new() - local file_info = file_info_new() + local info = file_info_new() local path = 'unit-test-directory/test.file' assert.is_true((fs.os_fileid(path, file_id))) - assert.is_true((fs.os_fileinfo(path, file_info))) - assert.is_true((fs.os_fileid_equal_fileinfo(file_id, file_info))) + assert.is_true((fs.os_fileinfo(path, info))) + assert.is_true((fs.os_fileid_equal_fileinfo(file_id, info))) end) - itp('returns false if file_id and file_info represent different files', function() + itp('returns false if file_id and FileInfo represent different files', function() local file_id = file_id_new() - local file_info = file_info_new() + local info = file_info_new() local path_1 = 'unit-test-directory/test.file' local path_2 = 'unit-test-directory/test_2.file' assert.is_true((fs.os_fileid(path_1, file_id))) - assert.is_true((fs.os_fileinfo(path_2, file_info))) - assert.is_false((fs.os_fileid_equal_fileinfo(file_id, file_info))) + assert.is_true((fs.os_fileinfo(path_2, info))) + assert.is_false((fs.os_fileid_equal_fileinfo(file_id, info))) end) end) end) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 0a41d502ad..db4dae4b39 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -124,11 +124,12 @@ endif() include(ExternalProject) if(WIN32) - set(LIBUV_URL https://github.com/neovim/libuv/archive/327f762644ccb964715cb99d08db0f1df43f651e.tar.gz) - set(LIBUV_SHA256 76e4ac06c7c74aeb471342c7f2d4a054af51ff054d399fac9f26e8fd5821dc92) + # "nvim" branch of https://github.com/neovim/libuv + set(LIBUV_URL https://github.com/neovim/libuv/archive/0ac136359903c70ab1db1838c3ad06da6fa5b912.tar.gz) + set(LIBUV_SHA256 600badb81e39578ddfc8f3636f78dd308ea0915e5bf1ee56e6cdfa314d3c463f) else() - set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.26.0.tar.gz) - set(LIBUV_SHA256 e414cf74615b7dae768f0f5667092f1d4975f5067c087bcbe0641e241ebe4693) + set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.29.1.tar.gz) + set(LIBUV_SHA256 bdde1140087ce97080ea323c3598553ece00a24ae63ac568be78bef3e97f3e25) endif() set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0/msgpack-3.0.0.tar.gz) |