aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/channel.txt99
-rw-r--r--runtime/doc/eval.txt73
-rw-r--r--src/nvim/buffer.c88
-rw-r--r--src/nvim/ex_docmd.c61
-rw-r--r--src/nvim/getchar.c28
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/ops.c32
-rw-r--r--src/nvim/screen.c4
-rw-r--r--src/nvim/sign.c6
-rw-r--r--src/nvim/testdir/test_writefile.vim7
-rw-r--r--test/functional/ui/messages_spec.lua37
-rw-r--r--test/functional/ui/wildmode_spec.lua85
-rw-r--r--test/unit/os/fs_spec.lua147
-rw-r--r--third-party/CMakeLists.txt9
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)