aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake.config/iwyu/mapping.imp1
-rw-r--r--runtime/autoload/provider/clipboard.vim4
-rw-r--r--runtime/doc/autocmd.txt2
-rw-r--r--runtime/doc/builtin.txt7
-rw-r--r--runtime/doc/cmdline.txt22
-rw-r--r--runtime/doc/editing.txt47
-rw-r--r--runtime/doc/eval.txt7
-rw-r--r--runtime/doc/filetype.txt2
-rw-r--r--runtime/doc/news.txt14
-rw-r--r--runtime/doc/options.txt12
-rw-r--r--runtime/doc/pattern.txt2
-rw-r--r--runtime/doc/recover.txt9
-rw-r--r--runtime/doc/treesitter.txt10
-rw-r--r--runtime/doc/vim_diff.txt12
-rw-r--r--runtime/lua/vim/_editor.lua17
-rw-r--r--runtime/lua/vim/_meta/options.lua3
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua5
-rw-r--r--runtime/lua/vim/lsp/_snippet.lua500
-rw-r--r--runtime/lua/vim/lsp/_snippet_grammar.lua143
-rw-r--r--runtime/lua/vim/lsp/buf.lua12
-rw-r--r--runtime/lua/vim/lsp/util.lua35
-rw-r--r--runtime/lua/vim/treesitter.lua12
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua92
-rw-r--r--src/nvim/api/command.c1
-rw-r--r--src/nvim/api/vim.c6
-rw-r--r--src/nvim/arglist.c1
-rw-r--r--src/nvim/arglist.h1
-rw-r--r--src/nvim/autocmd.c7
-rw-r--r--src/nvim/autocmd.h1
-rw-r--r--src/nvim/buffer.c6
-rw-r--r--src/nvim/buffer_defs.h14
-rw-r--r--src/nvim/bufwrite.c4
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/cmdexpand.c27
-rw-r--r--src/nvim/cmdexpand.h1
-rw-r--r--src/nvim/cmdexpand_defs.h113
-rw-r--r--src/nvim/cmdhist.c1
-rw-r--r--src/nvim/cmdhist.h1
-rw-r--r--src/nvim/cursor_shape.c6
-rw-r--r--src/nvim/cursor_shape.h6
-rw-r--r--src/nvim/debugger.c1
-rw-r--r--src/nvim/diff.c6
-rw-r--r--src/nvim/drawscreen.c18
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/eval.c12
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua6
-rw-r--r--src/nvim/eval/funcs.c3
-rw-r--r--src/nvim/eval/funcs.h1
-rw-r--r--src/nvim/eval/typval_defs.h13
-rw-r--r--src/nvim/eval/userfunc.c7
-rw-r--r--src/nvim/eval/userfunc.h1
-rw-r--r--src/nvim/ex_cmds.c56
-rw-r--r--src/nvim/ex_cmds_defs.h115
-rw-r--r--src/nvim/ex_docmd.c9
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--src/nvim/ex_eval_defs.h39
-rw-r--r--src/nvim/ex_getln.c27
-rw-r--r--src/nvim/ex_getln.h1
-rw-r--r--src/nvim/fileio.c12
-rw-r--r--src/nvim/fileio.h3
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua2
-rw-r--r--src/nvim/generators/gen_options.lua5
-rw-r--r--src/nvim/getchar.c10
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/grid.c16
-rw-r--r--src/nvim/highlight_group.c6
-rw-r--r--src/nvim/highlight_group.h1
-rw-r--r--src/nvim/indent.c89
-rw-r--r--src/nvim/insexpand.c1
-rw-r--r--src/nvim/lua/executor.c1
-rw-r--r--src/nvim/lua/executor.h1
-rw-r--r--src/nvim/mapping.c1
-rw-r--r--src/nvim/mapping.h1
-rw-r--r--src/nvim/mbyte.c16
-rw-r--r--src/nvim/mbyte.h1
-rw-r--r--src/nvim/memline.c264
-rw-r--r--src/nvim/menu.c7
-rw-r--r--src/nvim/menu.h1
-rw-r--r--src/nvim/menu_defs.h2
-rw-r--r--src/nvim/message.c360
-rw-r--r--src/nvim/message.h1
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/normal.c4
-rw-r--r--src/nvim/normal.h2
-rw-r--r--src/nvim/ops.c57
-rw-r--r--src/nvim/option.c386
-rw-r--r--src/nvim/option.h10
-rw-r--r--src/nvim/option_defs.h51
-rw-r--r--src/nvim/option_vars.h15
-rw-r--r--src/nvim/options.lua78
-rw-r--r--src/nvim/optionstr.c827
-rw-r--r--src/nvim/optionstr.h1
-rw-r--r--src/nvim/os/env.c2
-rw-r--r--src/nvim/os/fileio.c3
-rw-r--r--src/nvim/os/fs.c8
-rw-r--r--src/nvim/os/lang.c3
-rw-r--r--src/nvim/os/lang.h1
-rw-r--r--src/nvim/os/os.h5
-rw-r--r--src/nvim/os/users.c2
-rw-r--r--src/nvim/path.c8
-rw-r--r--src/nvim/profile.c2
-rw-r--r--src/nvim/profile.h1
-rw-r--r--src/nvim/runtime.c1
-rw-r--r--src/nvim/runtime.h1
-rw-r--r--src/nvim/shada.c5
-rw-r--r--src/nvim/sign.c1
-rw-r--r--src/nvim/sign.h1
-rw-r--r--src/nvim/spellfile.c70
-rw-r--r--src/nvim/spellsuggest.c1
-rw-r--r--src/nvim/syntax.c3
-rw-r--r--src/nvim/syntax.h1
-rw-r--r--src/nvim/tag.c1
-rw-r--r--src/nvim/textformat.c2
-rw-r--r--src/nvim/types.h2
-rw-r--r--src/nvim/undo.c40
-rw-r--r--src/nvim/undo_defs.h18
-rw-r--r--src/nvim/usercmd.c1
-rw-r--r--src/nvim/usercmd.h1
-rw-r--r--src/nvim/vim.h73
-rw-r--r--test/functional/api/vim_spec.lua12
-rw-r--r--test/functional/core/fileio_spec.lua7
-rw-r--r--test/functional/core/path_spec.lua34
-rw-r--r--test/functional/editor/completion_spec.lua9
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua58
-rw-r--r--test/functional/legacy/digraph_spec.lua6
-rw-r--r--test/functional/legacy/edit_spec.lua2
-rw-r--r--test/functional/plugin/lsp/snippet_spec.lua243
-rw-r--r--test/functional/plugin/lsp_spec.lua48
-rw-r--r--test/functional/terminal/window_spec.lua41
-rw-r--r--test/functional/treesitter/fold_spec.lua113
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua2
-rw-r--r--test/functional/ui/popupmenu_spec.lua4
-rw-r--r--test/functional/ui/screen_basic_spec.lua2
-rw-r--r--test/old/testdir/test_functions.vim18
-rw-r--r--test/old/testdir/test_history.vim5
-rw-r--r--test/old/testdir/test_options.vim312
137 files changed, 3127 insertions, 1773 deletions
diff --git a/cmake.config/iwyu/mapping.imp b/cmake.config/iwyu/mapping.imp
index 737b9b0e57..030f9520c0 100644
--- a/cmake.config/iwyu/mapping.imp
+++ b/cmake.config/iwyu/mapping.imp
@@ -192,6 +192,7 @@
# of its behavior.
{ include: [ '"nvim/arglist_defs.h"', public, '"nvim/arglist.h"', public ] },
{ include: [ '"nvim/buffer_defs.h"', public, '"nvim/buffer.h"', public ] },
+ { include: [ '"nvim/cmdexpand_defs.h"', public, '"nvim/cmdexpand.h"', public ] },
{ include: [ '"nvim/eval/typval_defs.h"', public, '"nvim/eval/typval.h"', public ] },
{ include: [ '"nvim/ex_cmds_defs.h"', public, '"nvim/ex_cmds.h"', public ] },
{ include: [ '"nvim/ex_eval_defs.h"', public, '"nvim/ex_eval.h"', public ] },
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 6ba28dcbfc..05f6bdb871 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -93,9 +93,9 @@ function! provider#clipboard#Executable() abort
let s:cache_enabled = 0
return 'pbcopy'
elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste')
- let s:copy['+'] = ['wl-copy', '--foreground', '--type', 'text/plain']
+ let s:copy['+'] = ['wl-copy', '--type', 'text/plain']
let s:paste['+'] = ['wl-paste', '--no-newline']
- let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain']
+ let s:copy['*'] = ['wl-copy', '--primary', '--type', 'text/plain']
let s:paste['*'] = ['wl-paste', '--no-newline', '--primary']
return 'wl-copy'
elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste')
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 90211fc5db..7ca9996e15 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -118,7 +118,7 @@ manually. Mostly the screen will not scroll up, thus there is no hit-enter
prompt. When one command outputs two messages this can happen anyway.
==============================================================================
-3. Removing autocommands *autocmd-remove*
+3. Removing autocommands *autocmd!* *autocmd-remove*
:au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd}
Remove all autocommands associated with {event} and
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index d0866b1c42..6de7166f83 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -925,7 +925,7 @@ cosh({expr}) *cosh()*
echo cosh(-0.5)
< -1.127626
-count({comp}, {expr} [, {ic} [, {start}]]) *count()*
+count({comp}, {expr} [, {ic} [, {start}]]) *count()* *E706*
Return the number of times an item with value {expr} appears
in |String|, |List| or |Dictionary| {comp}.
@@ -5116,6 +5116,7 @@ printf({fmt}, {expr1} ...) *printf()*
using a {n$} positional argument specifier. See |printf-$|.
+ *E1520*
The conversion specifiers and their meanings are:
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
@@ -7842,8 +7843,8 @@ swapinfo({fname}) *swapinfo()*
user user name
host host name
fname original file name
- pid PID of the Vim process that created the swap
- file
+ pid PID of the Nvim process that created the swap
+ file, or zero if not running.
mtime last modification time in seconds
inode Optional: INODE number of the file
dirty 1 if file was modified, 0 if not
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 5ee1a2af13..63967f84a1 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -493,16 +493,26 @@ example, to match only files that end in ".c": >
:e *.c$
This will not match a file ending in ".cpp". Without the "$" it does match.
-The old value of an option can be obtained by hitting 'wildchar' just after
-the '='. For example, typing 'wildchar' after ":set dir=" will insert the
-current value of 'dir'. This overrules file name completion for the options
-that take a file name.
-
If you would like using <S-Tab> for CTRL-P in an xterm, put this command in
your .cshrc: >
xmodmap -e "keysym Tab = Tab Find"
And this in your vimrc: >
:cmap <Esc>[1~ <C-P>
+< *complete-set-option*
+When setting an option using |:set=|, the old value of an option can be
+obtained by hitting 'wildchar' just after the '='. For example, typing
+'wildchar' after ":set dir=" will insert the current value of 'dir'. This
+overrules file name completion for the options that take a file name.
+
+When using |:set=|, |:set+=|, or |:set^=|, string options that have
+pre-defined names or syntax (e.g. 'diffopt', 'listchars') or are a list of
+single-character flags (e.g. 'shortmess') will also present a list of possible
+values for completion when using 'wildchar'.
+
+When using |:set-=|, comma-separated options like 'diffopt' or 'backupdir'
+will show each item separately. Flag list options like 'shortmess' will show
+both the entire old value and the individual flags. Otherwise completion will
+just fill in with the entire old value.
==============================================================================
3. Ex command-lines *cmdline-lines*
@@ -732,7 +742,7 @@ An example for subtracting (which isn't very useful): >
On this text:
1 one ~
2 two ~
- 3 three FOLDED~
+ 3 three FOLDED ~
4 four FOLDED ~
5 five FOLDED ~
6 six FOLDED ~
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 6e7963c066..7df2eb9742 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -169,33 +169,26 @@ If you want to keep the changed buffer without saving it, switch on the
2. Editing a file *edit-a-file*
*:e* *:edit* *reload*
-:e[dit] [++opt] [+cmd] Edit the current file. This is useful to re-edit the
+:e[dit][!] [++opt] [+cmd]
+ Edit the current file. This is useful to re-edit the
current file, when it has been changed outside of Vim.
- This fails when changes have been made to the current
- buffer and 'autowriteall' isn't set or the file can't
- be written.
- Also see |++opt| and |+cmd|.
-
*:edit!* *discard*
-:e[dit]! [++opt] [+cmd]
- Edit the current file always. Discard any changes to
- the current buffer. This is useful if you want to
- start all over again.
+ If [!] is given, unsaved changes in the current buffer
+ are discarded. Without [!] the command fails if there
+ are unsaved changes, unless 'autowriteall' is set and
+ the file can be written.
Also see |++opt| and |+cmd|.
*:edit_f*
-:e[dit] [++opt] [+cmd] {file}
+:e[dit][!] [++opt] [+cmd] {file}
Edit {file}.
- This fails when changes have been made to the current
- buffer, unless 'hidden' is set or 'autowriteall' is
- set and the file can be written.
- Also see |++opt| and |+cmd|.
-
*:edit!_f*
-:e[dit]! [++opt] [+cmd] {file}
- Edit {file} always. Discard any changes to the
- current buffer.
+ If [!] is given, unsaved changes in the current buffer
+ are discarded. Without [!] the command fails if there
+ are unsaved changes, unless 'hidden' is set or
+ 'autowriteall' is set and the file can be written.
Also see |++opt| and |+cmd|.
+
*:edit_#* *:e#*
:e[dit] [++opt] [+cmd] #[count]
Edit the [count]th buffer (as shown by |:files|).
@@ -357,8 +350,8 @@ is to use "path\[[]abc]", this matches the file "path\[abc]".
*starstar-wildcard*
Expanding "**" is possible on Unix, Win32, macOS and a few other systems (but
-it may depend on your 'shell' setting. It's known to work correctly for zsh; for
-bash this requires at least bash version >= 4.X).
+it may depend on your 'shell' setting on Unix and macOS. It's known to work
+correctly for zsh; for bash this requires at least bash version >= 4.X).
This allows searching a directory tree. This goes up to 100 directories deep.
Note there are some commands where this works slightly differently, see
|file-searching|.
@@ -1078,8 +1071,8 @@ will get the ACL info of the original file.
The ACL info is also used to check if a file is read-only (when opening the
file).
- *xattr* *E1506* *E1507* *E1508* *E1509*
-xattr stands for Extended Attributes It is an advanced way to save metadata
+ *xattr* *E1506* *E1508* *E1509*
+xattr stands for Extended Attributes. It is an advanced way to save metadata
alongside the file in the filesystem. It depends on the actual filesystem
being used and Vim supports it only on a Linux system.
Vim attempts to preserve the extended attribute info when writing a file.
@@ -1224,10 +1217,10 @@ MULTIPLE WINDOWS AND BUFFERS *window-exit*
*:confirm* *:conf*
:conf[irm] {command} Execute {command}, and use a dialog when an
operation has to be confirmed. Can be used on the
- |:q|, |:qa| and |:w| commands (the latter to override
- a read-only setting), and any other command that can
- fail in such a way, such as |:only|, |:buffer|,
- |:bdelete|, etc.
+ |:edit|, |:q|, |:qa| and |:w| commands (the latter to
+ override a read-only setting), and any commands that
+ can fail because of unsaved changes, such as |:only|,
+ |:buffer|, |:bdelete|, etc.
Examples: >
:confirm w foo
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 3cd5ea13f6..c41237b862 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2276,12 +2276,13 @@ v:stderr |channel-id| corresponding to stderr. The value is always 2;
:call chansend(v:stderr, "error: toaster empty\n")
<
*v:swapname* *swapname-variable*
-v:swapname Only valid when executing |SwapExists| autocommands: Name of
- the swap file found. Read-only.
+v:swapname Name of the swapfile found.
+ Only valid during |SwapExists| event.
+ Read-only.
*v:swapchoice* *swapchoice-variable*
v:swapchoice |SwapExists| autocommands can set this to the selected choice
- for handling an existing swap file:
+ for handling an existing swapfile:
'o' Open read-only
'e' Edit anyway
'r' Recover
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index 27ec92dbc6..ed21dc1c37 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -396,7 +396,7 @@ So to enable this only for ruby, set the following variable: >
:let g:ruby_exec = 1
If both, the global `plugin_exec` and the `<filetype>_exec` specific variable
-are set, the filetpe specific variable should have precedent.
+are set, the filetype specific variable should have precedent.
AWK *ft-awk-plugin*
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index b632cf0932..f0d01f92e7 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -68,6 +68,10 @@ The following changes may require adaptations in user config or plugins.
• Float window support hide and show by setting `hide` on `nvim_open_win` and
`nvim_win_set_config`.
+• |vim.lsp.util.parse_snippet()| will now strictly follow the snippet grammar
+ defined by LSP, and hence previously parsed snippets might now be considered
+ invalid input.
+
==============================================================================
NEW FEATURES *news-features*
@@ -105,9 +109,17 @@ The following new APIs and features were added.
• |nvim_set_keymap()| and |nvim_del_keymap()| now support abbreviations.
+• Better cmdline completion for string option value. |complete-set-option|
+
• Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers in a
terminal emulator that supports |tui-csiu|.
+• Editor
+ • By default, the swapfile "ATTENTION" |E325| dialog is skipped if the
+ swapfile is owned by a running Nvim process, instead of prompting. If you
+ always want the swapfile dialog, delete the default SwapExists handler:
+ `autocmd! nvim_swapfile`. |default-autocmds|
+
• LSP
• LSP method names are available in |vim.lsp.protocol.Methods|.
• Implemented LSP inlay hints: |vim.lsp.inlay_hint()|
@@ -143,6 +155,8 @@ The following new APIs and features were added.
• Added `vim.treesitter.query.edit()`, for live editing of treesitter
queries.
• Improved error messages for query parsing.
+ • Added |vim.treesitter.foldtext()| to apply treesitter highlighting to
+ foldtext.
• |vim.ui.open()| opens URIs using the system default handler (macOS `open`,
Windows `explorer`, Linux `xdg-open`, etc.)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 84cff775f6..bc207d7755 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -52,14 +52,16 @@ achieve special effects. These options come in three forms:
'lines'
Warning: This may have a lot of side effects.
- *:set-args* *E487* *E521*
+ *:set-args* *:set=* *E487* *E521*
:se[t] {option}={value} or
:se[t] {option}:{value}
Set string or number option to {value}.
For numeric options the value can be given in decimal,
hex (preceded with 0x) or octal (preceded with '0').
The old value can be inserted by typing 'wildchar' (by
- default this is a <Tab>). See |cmdline-completion|.
+ default this is a <Tab>). Many string options with
+ fixed syntax also support completing known values.
+ See |cmdline-completion| and |complete-set-option|.
White space between {option} and '=' is allowed and
will be ignored. White space between '=' and {value}
is not allowed.
@@ -93,6 +95,9 @@ achieve special effects. These options come in three forms:
When the option is a list of flags, {value} must be
exactly as they appear in the option. Remove flags
one by one to avoid problems.
+ The individual values from a comma separated list or
+ list of flags can be inserted by typing 'wildchar'.
+ See |complete-set-option|.
Also see |:set-args| above.
The {option} arguments to ":set" may be repeated. For example: >
@@ -1961,7 +1966,8 @@ A jump table for the options with a short description can be found at |Q_op|.
When omitted a context of six lines is used.
When using zero the context is actually one,
since folds require a line in between, also
- for a deleted line.
+ for a deleted line. Set it to a very large
+ value (999999) to disable folding completely.
See |fold-diff|.
iblank Ignore changes where lines are all blank. Adds
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index d74757de4f..09119b0918 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -1065,7 +1065,7 @@ match ASCII characters, as indicated by the range.
\(\) A pattern enclosed by escaped parentheses. */\(* */\(\)* */\)*
E.g., "\(^a\)" matches 'a' at the start of a line.
- There can only be ten of these. You can use "\%(" to add more, but
+ There can only be nine of these. You can use "\%(" to add more, but
not counting it as a sub-expression.
*E51* *E54* *E55* *E872* *E873*
diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt
index b05bcd6bc8..e6b5b06744 100644
--- a/runtime/doc/recover.txt
+++ b/runtime/doc/recover.txt
@@ -83,6 +83,15 @@ Detecting an existing swap file ~
You can find this in the user manual, section |11.3|.
+ *W325*
+The default |SwapExists| handler (|default-autocmds|) skips the |E325| prompt
+(selects "(E)dit") if the swapfile owner process (1) is still running and (2)
+was started by the current user. This presumes that you normally don't want
+to be bothered with the |ATTENTION| message just because you happen to edit
+the same file from multiple Nvim instances. In the worst case (a system
+crash) there will be more than one swapfile for the file; use |:recover| to
+inspect all of its swapfiles.
+
Updating the swapfile ~
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 34971c7acf..e19fda8fd1 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -560,6 +560,16 @@ foldexpr({lnum}) *vim.treesitter.foldexpr()*
Return: ~
(string)
+foldtext() *vim.treesitter.foldtext()*
+ Returns the highlighted content of the first line of the fold or falls
+ back to |foldtext()| if no treesitter parser is found. Can be set directly
+ to 'foldtext': >lua
+ vim.wo.foldtext = 'v:lua.vim.treesitter.foldtext()'
+<
+
+ Return: ~
+ `{ [1]: string, [2]: string[] }[]` | string
+
*vim.treesitter.get_captures_at_cursor()*
get_captures_at_cursor({winnr})
Returns a list of highlight capture names under the cursor
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index e636746616..e9c04443a6 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -139,6 +139,11 @@ nvim_terminal:
nvim_cmdwin:
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|.
+nvim_swapfile:
+- SwapExists: Skips the swapfile prompt (sets |v:swapchoice| to "e") when the
+ swapfile is owned by a running Nvim process. Shows |W325| "Ignoring
+ swapfile…" message.
+
==============================================================================
New Features *nvim-features*
@@ -287,13 +292,12 @@ Options:
'diffopt' "linematch" feature
'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The
user is prompted whether to trust the file.
- 'fillchars' flags: "msgsep", "horiz", "horizup",
- "horizdown", "vertleft", "vertright", "verthoriz"
+ 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown",
+ "vertleft", "vertright", "verthoriz"
'foldcolumn' supports up to 9 dynamic/fixed columns
'guicursor' works in the terminal (TUI)
'inccommand' shows interactive results for |:substitute|-like commands
and |:command-preview| commands
- 'jumpoptions' "stack" behavior
'jumpoptions' "view" tries to restore the |mark-view| when moving through
the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
'laststatus' global statusline support
@@ -304,6 +308,7 @@ Options:
'signcolumn' supports up to 9 dynamic/fixed columns
'statuscolumn' full control of columns using 'statusline' format
'tabline' %@Func@foo%X can call any function on mouse-click
+ 'termpastefilter'
'ttimeout', 'ttimeoutlen' behavior was simplified
'winblend' pseudo-transparency in floating windows |api-floatwin|
'winhighlight' window-local highlights
@@ -382,6 +387,7 @@ Upstreamed features *nvim-upstreamed*
These Nvim features were later integrated into Vim.
- 'fillchars' flags: "eob"
+- 'jumpoptions' "stack" behavior
- 'wildoptions' flags: "pum" enables popupmenu for wildmode completion
- |<Cmd>|
- |WinClosed|
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 7f09fc8038..1ba7d6163d 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1147,11 +1147,28 @@ function vim._init_default_autocmds()
end
end,
})
+
vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, {
pattern = '[:>]',
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
command = 'syntax sync minlines=1 maxlines=1',
})
+
+ vim.api.nvim_create_autocmd({ 'SwapExists' }, {
+ pattern = '*',
+ group = vim.api.nvim_create_augroup('nvim_swapfile', {}),
+ callback = function()
+ local info = vim.fn.swapinfo(vim.v.swapname)
+ local user = vim.uv.os_get_passwd().username
+ local iswin = 1 == vim.fn.has('win32')
+ if info.error or info.pid <= 0 or (not iswin and info.user ~= user) then
+ vim.v.swapchoice = '' -- Show the prompt.
+ return
+ end
+ vim.v.swapchoice = 'e' -- Choose "(E)dit".
+ vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid))
+ end,
+ })
end
function vim._init_defaults()
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index ed0d844748..08ee1f97ba 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -1591,7 +1591,8 @@ vim.go.dex = vim.go.diffexpr
--- When omitted a context of six lines is used.
--- When using zero the context is actually one,
--- since folds require a line in between, also
---- for a deleted line.
+--- for a deleted line. Set it to a very large
+--- value (999999) to disable folding completely.
--- See `fold-diff`.
---
--- iblank Ignore changes where lines are all blank. Adds
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 86e8781160..5a3a5f3890 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -6099,6 +6099,7 @@ function vim.fn.prevnonblank(lnum) end
--- using a {n$} positional argument specifier. See |printf-$|.
---
---
+--- *E1520*
--- The conversion specifiers and their meanings are:
---
--- *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
@@ -9311,8 +9312,8 @@ function vim.fn.swapfilelist() end
--- user user name
--- host host name
--- fname original file name
---- pid PID of the Vim process that created the swap
---- file
+--- pid PID of the Nvim process that created the swap
+--- file, or zero if not running.
--- mtime last modification time in seconds
--- inode Optional: INODE number of the file
--- dirty 1 if file was modified, 0 if not
diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua
deleted file mode 100644
index e7ada5415f..0000000000
--- a/runtime/lua/vim/lsp/_snippet.lua
+++ /dev/null
@@ -1,500 +0,0 @@
-local P = {}
-
----Take characters until the target characters (The escape sequence is '\' + char)
----@param targets string[] The character list for stop consuming text.
----@param specials string[] If the character isn't contained in targets/specials, '\' will be left.
-P.take_until = function(targets, specials)
- targets = targets or {}
- specials = specials or {}
-
- return function(input, pos)
- local new_pos = pos
- local raw = {}
- local esc = {}
- while new_pos <= #input do
- local c = string.sub(input, new_pos, new_pos)
- if c == '\\' then
- table.insert(raw, '\\')
- new_pos = new_pos + 1
- c = string.sub(input, new_pos, new_pos)
- if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then
- table.insert(esc, '\\')
- end
- table.insert(raw, c)
- table.insert(esc, c)
- new_pos = new_pos + 1
- else
- if vim.list_contains(targets, c) then
- break
- end
- table.insert(raw, c)
- table.insert(esc, c)
- new_pos = new_pos + 1
- end
- end
-
- if new_pos == pos then
- return P.unmatch(pos)
- end
-
- return {
- parsed = true,
- value = {
- raw = table.concat(raw, ''),
- esc = table.concat(esc, ''),
- },
- pos = new_pos,
- }
- end
-end
-
-P.unmatch = function(pos)
- return {
- parsed = false,
- value = nil,
- pos = pos,
- }
-end
-
-P.map = function(parser, map)
- return function(input, pos)
- local result = parser(input, pos)
- if result.parsed then
- return {
- parsed = true,
- value = map(result.value),
- pos = result.pos,
- }
- end
- return P.unmatch(pos)
- end
-end
-
-P.lazy = function(factory)
- return function(input, pos)
- return factory()(input, pos)
- end
-end
-
-P.token = function(token)
- return function(input, pos)
- local maybe_token = string.sub(input, pos, pos + #token - 1)
- if token == maybe_token then
- return {
- parsed = true,
- value = maybe_token,
- pos = pos + #token,
- }
- end
- return P.unmatch(pos)
- end
-end
-
-P.pattern = function(p)
- return function(input, pos)
- local maybe_match = string.match(string.sub(input, pos), '^' .. p)
- if maybe_match then
- return {
- parsed = true,
- value = maybe_match,
- pos = pos + #maybe_match,
- }
- end
- return P.unmatch(pos)
- end
-end
-
-P.many = function(parser)
- return function(input, pos)
- local values = {}
- local new_pos = pos
- while new_pos <= #input do
- local result = parser(input, new_pos)
- if not result.parsed then
- break
- end
- table.insert(values, result.value)
- new_pos = result.pos
- end
- if #values > 0 then
- return {
- parsed = true,
- value = values,
- pos = new_pos,
- }
- end
- return P.unmatch(pos)
- end
-end
-
-P.any = function(...)
- local parsers = { ... }
- return function(input, pos)
- for _, parser in ipairs(parsers) do
- local result = parser(input, pos)
- if result.parsed then
- return result
- end
- end
- return P.unmatch(pos)
- end
-end
-
-P.opt = function(parser)
- return function(input, pos)
- local result = parser(input, pos)
- return {
- parsed = true,
- value = result.value,
- pos = result.pos,
- }
- end
-end
-
-P.seq = function(...)
- local parsers = { ... }
- return function(input, pos)
- local values = {}
- local new_pos = pos
- for i, parser in ipairs(parsers) do
- local result = parser(input, new_pos)
- if result.parsed then
- values[i] = result.value
- new_pos = result.pos
- else
- return P.unmatch(pos)
- end
- end
- return {
- parsed = true,
- value = values,
- pos = new_pos,
- }
- end
-end
-
-local Node = {}
-
-Node.Type = {
- SNIPPET = 0,
- TABSTOP = 1,
- PLACEHOLDER = 2,
- VARIABLE = 3,
- CHOICE = 4,
- TRANSFORM = 5,
- FORMAT = 6,
- TEXT = 7,
-}
-
-function Node:__tostring()
- local insert_text = {}
- if self.type == Node.Type.SNIPPET then
- for _, c in ipairs(self.children) do
- table.insert(insert_text, tostring(c))
- end
- elseif self.type == Node.Type.CHOICE then
- table.insert(insert_text, self.items[1])
- elseif self.type == Node.Type.PLACEHOLDER then
- for _, c in ipairs(self.children or {}) do
- table.insert(insert_text, tostring(c))
- end
- elseif self.type == Node.Type.TEXT then
- table.insert(insert_text, self.esc)
- end
- return table.concat(insert_text, '')
-end
-
---@see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar
-
-local S = {}
-S.dollar = P.token('$')
-S.open = P.token('{')
-S.close = P.token('}')
-S.colon = P.token(':')
-S.slash = P.token('/')
-S.comma = P.token(',')
-S.pipe = P.token('|')
-S.plus = P.token('+')
-S.minus = P.token('-')
-S.question = P.token('?')
-S.int = P.map(P.pattern('[0-9]+'), function(value)
- return tonumber(value, 10)
-end)
-S.var = P.pattern('[%a_][%w_]+')
-S.text = function(targets, specials)
- return P.map(P.take_until(targets, specials), function(value)
- return setmetatable({
- type = Node.Type.TEXT,
- raw = value.raw,
- esc = value.esc,
- }, Node)
- end)
-end
-
-S.toplevel = P.lazy(function()
- return P.any(S.placeholder, S.tabstop, S.variable, S.choice)
-end)
-
-S.format = P.any(
- P.map(P.seq(S.dollar, S.int), function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[2],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- }, Node)
- end),
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.colon,
- S.slash,
- P.any(
- P.token('upcase'),
- P.token('downcase'),
- P.token('capitalize'),
- P.token('camelcase'),
- P.token('pascalcase')
- ),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- modifier = values[6],
- }, Node)
- end
- ),
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.colon,
- P.seq(
- S.question,
- P.opt(P.take_until({ ':' }, { '\\' })),
- S.colon,
- P.opt(P.take_until({ '}' }, { '\\' }))
- ),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = values[5][2] and values[5][2].esc or '',
- else_text = values[5][4] and values[5][4].esc or '',
- }, Node)
- end
- ),
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.colon,
- P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = values[5][2] and values[5][2].esc or '',
- else_text = '',
- }, Node)
- end
- ),
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.colon,
- S.minus,
- P.opt(P.take_until({ '}' }, { '\\' })),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = '',
- else_text = values[6] and values[6].esc or '',
- }, Node)
- end
- ),
- P.map(
- P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close),
- function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = '',
- else_text = values[5] and values[5].esc or '',
- }, Node)
- end
- )
-)
-
-S.transform = P.map(
- P.seq(
- S.slash,
- P.take_until({ '/' }, { '\\' }),
- S.slash,
- P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
- S.slash,
- P.opt(P.pattern('[ig]+'))
- ),
- function(values)
- return setmetatable({
- type = Node.Type.TRANSFORM,
- pattern = values[2].raw,
- format = values[4],
- option = values[6],
- }, Node)
- end
-)
-
-S.tabstop = P.any(
- P.map(P.seq(S.dollar, S.int), function(values)
- return setmetatable({
- type = Node.Type.TABSTOP,
- tabstop = values[2],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values)
- return setmetatable({
- type = Node.Type.TABSTOP,
- tabstop = values[3],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values)
- return setmetatable({
- type = Node.Type.TABSTOP,
- tabstop = values[3],
- transform = values[4],
- }, Node)
- end)
-)
-
-S.placeholder = P.any(
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.colon,
- P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.PLACEHOLDER,
- tabstop = values[3],
- -- insert empty text if opt did not match.
- children = values[5] or {
- setmetatable({
- type = Node.Type.TEXT,
- raw = '',
- esc = '',
- }, Node),
- },
- }, Node)
- end
- )
-)
-
-S.choice = P.map(
- P.seq(
- S.dollar,
- S.open,
- S.int,
- S.pipe,
- P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
- return values[1].esc
- end)),
- S.pipe,
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.CHOICE,
- tabstop = values[3],
- items = values[5],
- }, Node)
- end
-)
-
-S.variable = P.any(
- P.map(P.seq(S.dollar, S.var), function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[2],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[3],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[3],
- transform = values[4],
- }, Node)
- end),
- P.map(
- P.seq(
- S.dollar,
- S.open,
- S.var,
- S.colon,
- P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))),
- S.close
- ),
- function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[3],
- children = values[5],
- }, Node)
- end
- )
-)
-
-S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)
- return setmetatable({
- type = Node.Type.SNIPPET,
- children = values,
- }, Node)
-end)
-
-local M = {}
-
----The snippet node type enum
----@types table<string, integer>
-M.NodeType = Node.Type
-
----Parse snippet string and returns the AST
----@param input string
----@return table
-function M.parse(input)
- local result = S.snippet(input, 1)
- if not result.parsed then
- error('snippet parsing failed.')
- end
- return result.value
-end
-
-return M
diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua
new file mode 100644
index 0000000000..86f00fea51
--- /dev/null
+++ b/runtime/lua/vim/lsp/_snippet_grammar.lua
@@ -0,0 +1,143 @@
+--- Grammar for LSP snippets, based on https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax
+
+local lpeg = vim.lpeg
+local P, S, R, V = lpeg.P, lpeg.S, lpeg.R, lpeg.V
+local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct
+
+local M = {}
+
+local alpha = R('az', 'AZ')
+local backslash = P('\\')
+local colon = P(':')
+local dollar = P('$')
+local int = R('09') ^ 1
+local l_brace, r_brace = P('{'), P('}')
+local pipe = P('|')
+local slash = P('/')
+local underscore = P('_')
+local var = Cg((underscore + alpha) * ((underscore + alpha + int) ^ 0), 'name')
+local format_capture = Cg(int / tonumber, 'capture')
+local format_modifier = Cg(P('upcase') + P('downcase') + P('capitalize'), 'modifier')
+local tabstop = Cg(int / tonumber, 'tabstop')
+
+--- Returns a function that unescapes occurrences of "special" characters.
+---
+--- @param special string
+--- @return fun(match: string): string
+local function escape_text(special)
+ return function(match)
+ local escaped = match:gsub('\\(.)', function(c)
+ return special:find(c) and c or '\\' .. c
+ end)
+ return escaped
+ end
+end
+
+-- Text nodes match "any character", but $, \, and } must be escaped.
+local escapable = '$}\\'
+local text = (backslash * S(escapable)) + (P(1) - S(escapable))
+local text_0, text_1 = (text ^ 0) / escape_text(escapable), text ^ 1
+-- Within choice nodes, \ also escapes comma and pipe characters.
+local choice_text = C(((backslash * S(escapable .. ',|')) + (P(1) - S(escapable .. ',|'))) ^ 1)
+ / escape_text(escapable .. ',|')
+local if_text, else_text = Cg(text_0, 'if_text'), Cg(text_0, 'else_text')
+-- Within format nodes, make sure we stop at /
+local format_text = C(((backslash * S(escapable)) + (P(1) - S(escapable .. '/'))) ^ 1)
+ / escape_text(escapable)
+-- Within ternary condition format nodes, make sure we stop at :
+local if_till_colon_text = Cg(
+ C(((backslash * S(escapable)) + (P(1) - S(escapable .. ':'))) ^ 1) / escape_text(escapable),
+ 'if_text'
+)
+
+-- Matches the string inside //, allowing escaping of the closing slash.
+local regex = Cg(((backslash * slash) + (P(1) - slash)) ^ 1, 'regex')
+
+-- Regex constructor flags (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp#parameters).
+local options = Cg(S('dgimsuvy') ^ 0, 'options')
+
+--- @enum vim.snippet.Type
+local Type = {
+ Tabstop = 1,
+ Placeholder = 2,
+ Choice = 3,
+ Variable = 4,
+ Format = 5,
+ Text = 6,
+ Snippet = 7,
+}
+M.NodeType = Type
+
+--- @class vim.snippet.Node<T>: { type: vim.snippet.Type, data: T }
+--- @class vim.snippet.TabstopData: { tabstop: number }
+--- @class vim.snippet.TextData: { text: string }
+--- @class vim.snippet.PlaceholderData: { tabstop: vim.snippet.TabstopData, value: vim.snippet.Node<any> }
+--- @class vim.snippet.ChoiceData: { tabstop: vim.snippet.TabstopData, values: string[] }
+--- @class vim.snippet.VariableData: { name: string, default?: vim.snippet.Node<any>, regex?: string, format?: vim.snippet.Node<vim.snippet.FormatData|vim.snippet.TextData>[], options?: string }
+--- @class vim.snippet.FormatData: { capture: number, modifier?: string, if_text?: string, else_text?: string }
+--- @class vim.snippet.SnippetData: { children: vim.snippet.Node<any>[] }
+
+--- Returns a function that constructs a snippet node of the given type.
+---
+--- @generic T
+--- @param type vim.snippet.Type
+--- @return fun(data: T): vim.snippet.Node<T>
+local function node(type)
+ return function(data)
+ return { type = type, data = data }
+ end
+end
+
+-- stylua: ignore
+local G = P({
+ 'snippet';
+ snippet = Ct(Cg(
+ Ct((
+ V('any') +
+ (Ct(Cg(text_1 / escape_text(escapable), 'text')) / node(Type.Text))
+ ) ^ 1), 'children'
+ )) / node(Type.Snippet),
+ any_or_text = V('any') + (Ct(Cg(text_0 / escape_text(escapable), 'text')) / node(Type.Text)),
+ any = V('placeholder') + V('tabstop') + V('choice') + V('variable'),
+ tabstop = Ct(dollar * (tabstop + (l_brace * tabstop * r_brace))) / node(Type.Tabstop),
+ placeholder = Ct(dollar * l_brace * tabstop * colon * Cg(V('any_or_text'), 'value') * r_brace) / node(Type.Placeholder),
+ choice = Ct(dollar *
+ l_brace *
+ tabstop *
+ pipe *
+ Cg(Ct(choice_text * (P(',') * choice_text) ^ 0), 'values') *
+ pipe *
+ r_brace) / node(Type.Choice),
+ variable = Ct(dollar * (
+ var + (
+ l_brace * var * (
+ r_brace +
+ (colon * Cg(V('any_or_text'), 'default') * r_brace) +
+ (slash * regex * slash * Cg(Ct((V('format') + (C(format_text) / node(Type.Text))) ^ 1), 'format') * slash * options * r_brace)
+ ))
+ )) / node(Type.Variable),
+ format = Ct(dollar * (
+ format_capture + (
+ l_brace * format_capture * (
+ r_brace +
+ (colon * (
+ (slash * format_modifier * r_brace) +
+ (P('+') * if_text * r_brace) +
+ (P('?') * if_till_colon_text * colon * else_text * r_brace) +
+ (P('-') * else_text * r_brace) +
+ (else_text * r_brace)
+ ))
+ ))
+ )) / node(Type.Format),
+})
+
+--- Parses the given input into a snippet tree.
+--- @param input string
+--- @return vim.snippet.Node<vim.snippet.SnippetData>
+function M.parse(input)
+ local snippet = G:match(input)
+ assert(snippet, 'snippet parsing failed')
+ return snippet --- @type vim.snippet.Node<vim.snippet.SnippetData>
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 8a29fac2b5..a906512e24 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -652,7 +652,7 @@ local function on_code_action_results(results, ctx, options)
-- arguments?: any[]
--
---@type lsp.Client
- local client = vim.lsp.get_client_by_id(action_tuple[1])
+ local client = assert(vim.lsp.get_client_by_id(action_tuple[1]))
local action = action_tuple[2]
local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr })
@@ -663,10 +663,14 @@ local function on_code_action_results(results, ctx, options)
if not action.edit and client and supports_resolve then
client.request(ms.codeAction_resolve, action, function(err, resolved_action)
if err then
- vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
- return
+ if action.command then
+ apply_action(action, client)
+ else
+ vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
+ end
+ else
+ apply_action(resolved_action, client)
end
- apply_action(resolved_action, client)
end, ctx.bufnr)
else
apply_action(action, client)
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 51ed87219c..a4c8959b99 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1,5 +1,5 @@
local protocol = require('vim.lsp.protocol')
-local snippet = require('vim.lsp._snippet')
+local snippet = require('vim.lsp._snippet_grammar')
local validate = vim.validate
local api = vim.api
local list_extend = vim.list_extend
@@ -610,12 +610,41 @@ end
---@return string parsed snippet
function M.parse_snippet(input)
local ok, parsed = pcall(function()
- return tostring(snippet.parse(input))
+ return snippet.parse(input)
end)
if not ok then
return input
end
- return parsed
+
+ --- @param node vim.snippet.Node<any>
+ --- @return string
+ local function node_to_string(node)
+ local insert_text = {}
+ if node.type == snippet.NodeType.Snippet then
+ for _, child in
+ ipairs((node.data --[[@as vim.snippet.SnippetData]]).children)
+ do
+ table.insert(insert_text, node_to_string(child))
+ end
+ elseif node.type == snippet.NodeType.Choice then
+ table.insert(insert_text, (node.data --[[@as vim.snippet.ChoiceData]]).values[1])
+ elseif node.type == snippet.NodeType.Placeholder then
+ table.insert(
+ insert_text,
+ node_to_string((node.data --[[@as vim.snippet.PlaceholderData]]).value)
+ )
+ elseif node.type == snippet.NodeType.Text then
+ table.insert(
+ insert_text,
+ node
+ .data --[[@as vim.snippet.TextData]]
+ .text
+ )
+ end
+ return table.concat(insert_text)
+ end
+
+ return node_to_string(parsed)
end
--- Sorts by CompletionItem.sortText.
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 0e34cbcbcc..f863942d3b 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -508,4 +508,16 @@ function M.foldexpr(lnum)
return require('vim.treesitter._fold').foldexpr(lnum)
end
+--- Returns the highlighted content of the first line of the fold or falls back to |foldtext()|
+--- if no treesitter parser is found. Can be set directly to 'foldtext':
+---
+--- ```lua
+--- vim.wo.foldtext = 'v:lua.vim.treesitter.foldtext()'
+--- ```
+---
+---@return { [1]: string, [2]: string[] }[] | string
+function M.foldtext()
+ return require('vim.treesitter._fold').foldtext()
+end
+
return M
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 8bc08c9c2e..5c1cc06908 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -361,4 +361,96 @@ function M.foldexpr(lnum)
return foldinfos[bufnr].levels[lnum] or '0'
end
+---@package
+---@return { [1]: string, [2]: string[] }[]|string
+function M.foldtext()
+ local foldstart = vim.v.foldstart
+ local bufnr = api.nvim_get_current_buf()
+
+ ---@type boolean, LanguageTree
+ local ok, parser = pcall(ts.get_parser, bufnr)
+ if not ok then
+ return vim.fn.foldtext()
+ end
+
+ local query = ts.query.get(parser:lang(), 'highlights')
+ if not query then
+ return vim.fn.foldtext()
+ end
+
+ local tree = parser:parse({ foldstart - 1, foldstart })[1]
+
+ local line = api.nvim_buf_get_lines(bufnr, foldstart - 1, foldstart, false)[1]
+ if not line then
+ return vim.fn.foldtext()
+ end
+
+ ---@type { [1]: string, [2]: string[], range: { [1]: integer, [2]: integer } }[] | { [1]: string, [2]: string[] }[]
+ local result = {}
+
+ local line_pos = 0
+
+ for id, node, metadata in query:iter_captures(tree:root(), 0, foldstart - 1, foldstart) do
+ local name = query.captures[id]
+ local start_row, start_col, end_row, end_col = node:range()
+
+ local priority = tonumber(metadata.priority or vim.highlight.priorities.treesitter)
+
+ if start_row == foldstart - 1 and end_row == foldstart - 1 then
+ -- check for characters ignored by treesitter
+ if start_col > line_pos then
+ table.insert(result, {
+ line:sub(line_pos + 1, start_col),
+ {},
+ range = { line_pos, start_col },
+ })
+ end
+ line_pos = end_col
+
+ local text = line:sub(start_col + 1, end_col)
+ table.insert(result, { text, { { '@' .. name, priority } }, range = { start_col, end_col } })
+ end
+ end
+
+ local i = 1
+ while i <= #result do
+ -- find first capture that is not in current range and apply highlights on the way
+ local j = i + 1
+ while
+ j <= #result
+ and result[j].range[1] >= result[i].range[1]
+ and result[j].range[2] <= result[i].range[2]
+ do
+ for k, v in ipairs(result[i][2]) do
+ if not vim.tbl_contains(result[j][2], v) then
+ table.insert(result[j][2], k, v)
+ end
+ end
+ j = j + 1
+ end
+
+ -- remove the parent capture if it is split into children
+ if j > i + 1 then
+ table.remove(result, i)
+ else
+ -- highlights need to be sorted by priority, on equal prio, the deeper nested capture (earlier
+ -- in list) should be considered higher prio
+ if #result[i][2] > 1 then
+ table.sort(result[i][2], function(a, b)
+ return a[2] < b[2]
+ end)
+ end
+
+ result[i][2] = vim.tbl_map(function(tbl)
+ return tbl[1]
+ end, result[i][2])
+ result[i] = { result[i][1], result[i][2] }
+
+ i = i + 1
+ end
+ end
+
+ return result
+end
+
return M
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index eb32f46143..f4c6f646eb 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -17,6 +17,7 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/decoration.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 675aaf1006..b8e0934669 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -941,6 +941,12 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
goto fail;
}
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ buf->b_last_changedtick = buf_get_changedtick(buf);
+ buf->b_last_changedtick_i = buf_get_changedtick(buf);
+ buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+
// Only strictly needed for scratch, but could just as well be consistent
// and do this now. buffer is created NOW, not when it latter first happen
// to reach a window or aucmd_prepbuf() ..
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index a4221a1562..fb8849541d 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -14,6 +14,7 @@
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/window.h"
diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h
index 52894b73a0..bd5cfb4a11 100644
--- a/src/nvim/arglist.h
+++ b/src/nvim/arglist.h
@@ -2,6 +2,7 @@
#define NVIM_ARGLIST_H
#include "nvim/arglist_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 657760914f..a40f7d8c26 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2213,6 +2213,13 @@ char *expand_get_event_name(expand_T *xp, int idx)
return event_names[idx - next_augroup_id].name;
}
+/// Function given to ExpandGeneric() to obtain the list of event names. Don't
+/// include groups.
+char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ return event_names[idx].name;
+}
+
/// Check whether given autocommand is supported
///
/// @param[in] event Event to check.
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index b3de57311e..324a31be47 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/macros.h"
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 5025e86771..076cf63913 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -3480,8 +3480,8 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
return;
}
- long above; // number of lines above window
- long below; // number of lines below window
+ linenr_T above; // number of lines above window
+ linenr_T below; // number of lines below window
above = wp->w_topline - 1;
above += win_get_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -3580,7 +3580,7 @@ void ex_buffer_all(exarg_T *eap)
bool p_ea_save;
int open_wins = 0;
int r;
- long count; // Maximum number of windows to open.
+ linenr_T count; // Maximum number of windows to open.
int all; // When true also load inactive buffers.
int had_tab = cmdmod.cmod_tab;
tabpage_T *tpnext;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index a9ad0051ed..1a2e2fbdae 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -488,13 +488,13 @@ struct file_buffer {
u_header_T *b_u_newhead; // pointer to newest header; may not be valid
// if b_u_curhead is not NULL
u_header_T *b_u_curhead; // pointer to current header
- int b_u_numhead; // current number of headers
- bool b_u_synced; // entry lists are synced
- long b_u_seq_last; // last used undo sequence number
- long b_u_save_nr_last; // counter for last file write
- long b_u_seq_cur; // uh_seq of header below which we are now
- time_t b_u_time_cur; // uh_time of header below which we are now
- long b_u_save_nr_cur; // file write nr after which we are now
+ int b_u_numhead; // current number of headers
+ bool b_u_synced; // entry lists are synced
+ int b_u_seq_last; // last used undo sequence number
+ int b_u_save_nr_last; // counter for last file write
+ int b_u_seq_cur; // uh_seq of header below which we are now
+ time_t b_u_time_cur; // uh_time of header below which we are now
+ int b_u_save_nr_cur; // file write nr after which we are now
// variables for "U" command in undo.c
char *b_u_line_ptr; // saved line for "U" command
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index db813a3ae1..2e4dda78fc 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -1368,7 +1368,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
int no_eol = false; // no end-of-line written
- long nchars;
+ int nchars;
linenr_T lnum;
int fileformat;
int checking_conversion;
@@ -1789,7 +1789,7 @@ restore_backup:
if (msg_add_fileformat(fileformat)) {
insert_space = true;
}
- msg_add_lines(insert_space, (long)lnum, nchars); // add line/char count
+ msg_add_lines(insert_space, lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
if (append) {
xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"), IOSIZE);
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 48dc02b65b..abbfe2505e 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -1941,7 +1941,7 @@ void truncate_line(int fixpos)
/// Delete "nlines" lines at the cursor.
/// Saves the lines for undo first if "undo" is true.
-void del_lines(long nlines, bool undo)
+void del_lines(linenr_T nlines, bool undo)
{
int n;
linenr_T first = curwin->w_cursor.lnum;
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index d733ffe6ab..c2469b6574 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -69,9 +69,6 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-/// Type used by ExpandGeneric()
-typedef char *(*CompleteListItemGetter)(expand_T *, int);
-
/// Type used by call_user_expand_func
typedef void *(*user_expand_func_T)(const char *, int, typval_T *);
@@ -107,6 +104,8 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_LUA
&& xp->xp_context != EXPAND_OLD_SETTING
+ && xp->xp_context != EXPAND_STRING_SETTING
+ && xp->xp_context != EXPAND_SETTING_SUBTRACT
&& xp->xp_context != EXPAND_OWNSYNTAX
&& xp->xp_context != EXPAND_PACKADD
&& xp->xp_context != EXPAND_RUNTIME
@@ -2599,7 +2598,7 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches
{ EXPAND_MENUNAMES, get_menu_names, false, true },
{ EXPAND_SYNTAX, get_syntax_name, true, true },
{ EXPAND_SYNTIME, get_syntime_arg, true, true },
- { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false },
+ { EXPAND_HIGHLIGHT, get_highlight_name, true, false },
{ EXPAND_EVENTS, expand_get_event_name, true, false },
{ EXPAND_AUGROUP, expand_get_augroup_name, true, false },
{ EXPAND_SIGN, get_sign_name, true, true },
@@ -2694,8 +2693,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
return OK;
}
if (xp->xp_context == EXPAND_OLD_SETTING) {
- ExpandOldSetting(numMatches, matches);
- return OK;
+ return ExpandOldSetting(numMatches, matches);
}
if (xp->xp_context == EXPAND_BUFFERS) {
return ExpandBufnames(pat, numMatches, matches, options);
@@ -2765,6 +2763,10 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_SETTINGS) {
ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches, fuzzy);
+ } else if (xp->xp_context == EXPAND_STRING_SETTING) {
+ ret = ExpandStringSetting(xp, &regmatch, numMatches, matches);
+ } else if (xp->xp_context == EXPAND_SETTING_SUBTRACT) {
+ ret = ExpandSettingSubtract(xp, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_MAPPINGS) {
ret = ExpandMappings(pat, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
@@ -2788,9 +2790,8 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
/// program. Matching strings are copied into an array, which is returned.
///
/// @param func returns a string from the list
-static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch,
- char ***matches, int *numMatches, CompleteListItemGetter func,
- int escaped)
+void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch, char ***matches,
+ int *numMatches, CompleteListItemGetter func, bool escaped)
{
const bool fuzzy = cmdline_fuzzy_complete(pat);
*matches = NULL;
@@ -2863,6 +2864,7 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
// in the specified order.
const bool sort_matches = !fuzzy
&& xp->xp_context != EXPAND_MENUNAMES
+ && xp->xp_context != EXPAND_STRING_SETTING
&& xp->xp_context != EXPAND_MENUS
&& xp->xp_context != EXPAND_SCRIPTNAMES;
@@ -3076,7 +3078,7 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re
*matches = NULL;
*numMatches = 0;
- char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp);
+ char *const retstr = call_user_expand_func(call_func_retstr, xp);
if (retstr == NULL) {
return FAIL;
}
@@ -3148,7 +3150,7 @@ static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches)
{
*matches = NULL;
*numMatches = 0;
- list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp);
+ list_T *const retlist = call_user_expand_func(call_func_retlist, xp);
if (retlist == NULL) {
return FAIL;
}
@@ -3221,8 +3223,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir
char **p;
int num_p = 0;
- (void)ExpandFromContext(&xpc, buf, &p, &num_p,
- WILD_SILENT | expand_options);
+ (void)ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options);
if (num_p > 0) {
ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 32c23c5d66..81e323c97b 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -1,6 +1,7 @@
#ifndef NVIM_CMDEXPAND_H
#define NVIM_CMDEXPAND_H
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h
new file mode 100644
index 0000000000..a302a32852
--- /dev/null
+++ b/src/nvim/cmdexpand_defs.h
@@ -0,0 +1,113 @@
+#ifndef NVIM_CMDEXPAND_DEFS_H
+#define NVIM_CMDEXPAND_DEFS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "nvim/eval/typval_defs.h"
+#include "nvim/types.h"
+
+typedef enum {
+ XP_PREFIX_NONE, ///< prefix not used
+ XP_PREFIX_NO, ///< "no" prefix for bool option
+ XP_PREFIX_INV, ///< "inv" prefix for bool option
+} xp_prefix_T;
+
+enum { EXPAND_BUF_LEN = 256, };
+
+/// used for completion on the command line
+typedef struct expand {
+ char *xp_pattern; ///< start of item to expand
+ int xp_context; ///< type of expansion
+ size_t xp_pattern_len; ///< bytes in xp_pattern before cursor
+ xp_prefix_T xp_prefix;
+ char *xp_arg; ///< completion function
+ LuaRef xp_luaref; ///< Ref to Lua completion function
+ sctx_T xp_script_ctx; ///< SCTX for completion function
+ int xp_backslash; ///< one of the XP_BS_ values
+#ifndef BACKSLASH_IN_FILENAME
+ bool xp_shell; ///< true for a shell command, more
+ ///< characters need to be escaped
+#endif
+ int xp_numfiles; ///< number of files found by file name completion
+ int xp_col; ///< cursor position in line
+ int xp_selected; ///< selected index in completion
+ char *xp_orig; ///< originally expanded string
+ char **xp_files; ///< list of files
+ char *xp_line; ///< text being completed
+ char xp_buf[EXPAND_BUF_LEN]; ///< buffer for returned match
+} expand_T;
+
+/// values for xp_backslash
+enum {
+ XP_BS_NONE = 0, ///< nothing special for backslashes
+ XP_BS_ONE = 1, ///< uses one backslash before a space
+ XP_BS_THREE = 2, ///< uses three backslashes before a space
+};
+
+/// values for xp_context when doing command line completion
+enum {
+ EXPAND_UNSUCCESSFUL = -2,
+ EXPAND_OK = -1,
+ EXPAND_NOTHING = 0,
+ EXPAND_COMMANDS,
+ EXPAND_FILES,
+ EXPAND_DIRECTORIES,
+ EXPAND_SETTINGS,
+ EXPAND_BOOL_SETTINGS,
+ EXPAND_TAGS,
+ EXPAND_OLD_SETTING,
+ EXPAND_HELP,
+ EXPAND_BUFFERS,
+ EXPAND_EVENTS,
+ EXPAND_MENUS,
+ EXPAND_SYNTAX,
+ EXPAND_HIGHLIGHT,
+ EXPAND_AUGROUP,
+ EXPAND_USER_VARS,
+ EXPAND_MAPPINGS,
+ EXPAND_TAGS_LISTFILES,
+ EXPAND_FUNCTIONS,
+ EXPAND_USER_FUNC,
+ EXPAND_EXPRESSION,
+ EXPAND_MENUNAMES,
+ EXPAND_USER_COMMANDS,
+ EXPAND_USER_CMD_FLAGS,
+ EXPAND_USER_NARGS,
+ EXPAND_USER_COMPLETE,
+ EXPAND_ENV_VARS,
+ EXPAND_LANGUAGE,
+ EXPAND_COLORS,
+ EXPAND_COMPILER,
+ EXPAND_USER_DEFINED,
+ EXPAND_USER_LIST,
+ EXPAND_USER_LUA,
+ EXPAND_SHELLCMD,
+ EXPAND_SIGN,
+ EXPAND_PROFILE,
+ EXPAND_FILETYPE,
+ EXPAND_FILES_IN_PATH,
+ EXPAND_OWNSYNTAX,
+ EXPAND_LOCALES,
+ EXPAND_HISTORY,
+ EXPAND_USER,
+ EXPAND_SYNTIME,
+ EXPAND_USER_ADDR_TYPE,
+ EXPAND_PACKADD,
+ EXPAND_MESSAGES,
+ EXPAND_MAPCLEAR,
+ EXPAND_ARGLIST,
+ EXPAND_DIFF_BUFFERS,
+ EXPAND_BREAKPOINT,
+ EXPAND_SCRIPTNAMES,
+ EXPAND_RUNTIME,
+ EXPAND_STRING_SETTING,
+ EXPAND_SETTING_SUBTRACT,
+ EXPAND_CHECKHEALTH,
+ EXPAND_LUA,
+};
+
+/// Type used by ExpandGeneric()
+typedef char *(*CompleteListItemGetter)(expand_T *, int);
+
+#endif // NVIM_CMDEXPAND_DEFS_H
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 50bdfd892f..b0490670fe 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -12,6 +12,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cmdhist.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index a0f2ab6934..2b7e47e38e 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -1,6 +1,7 @@
#ifndef NVIM_CMDHIST_H
#define NVIM_CMDHIST_H
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/os/time.h"
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index fb2ca9ff8c..ba5f30c20f 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -364,9 +364,9 @@ static void clear_shape_table(void)
{
for (int idx = 0; idx < SHAPE_IDX_COUNT; idx++) {
shape_table[idx].shape = SHAPE_BLOCK;
- shape_table[idx].blinkwait = 0L;
- shape_table[idx].blinkon = 0L;
- shape_table[idx].blinkoff = 0L;
+ shape_table[idx].blinkwait = 0;
+ shape_table[idx].blinkon = 0;
+ shape_table[idx].blinkoff = 0;
shape_table[idx].id = 0;
shape_table[idx].id_lm = 0;
}
diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h
index 93bddd47c7..33d0344c2d 100644
--- a/src/nvim/cursor_shape.h
+++ b/src/nvim/cursor_shape.h
@@ -44,9 +44,9 @@ typedef struct cursor_entry {
CursorShape shape; ///< cursor shape: one of the SHAPE_ defines
int mshape; ///< mouse shape: one of the MSHAPE defines
int percentage; ///< percentage of cell for bar
- long blinkwait; ///< blinking, wait time before blinking starts
- long blinkon; ///< blinking, on time
- long blinkoff; ///< blinking, off time
+ int blinkwait; ///< blinking, wait time before blinking starts
+ int blinkon; ///< blinking, on time
+ int blinkoff; ///< blinking, off time
int id; ///< highlight group ID
int id_lm; ///< highlight group ID for :lmap mode
char *name; ///< mode short name
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 3d5f27e76c..755579a3bc 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -12,6 +12,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/debugger.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 3ab1da76f4..8479675dfa 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -2467,6 +2467,7 @@ int diffopt_changed(void)
char *p = p_dip;
while (*p != NUL) {
+ // Note: Keep this in sync with p_dip_values
if (strncmp(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
@@ -2513,6 +2514,7 @@ int diffopt_changed(void)
p += 8;
diff_flags_new |= DIFF_INTERNAL;
} else if (strncmp(p, "algorithm:", 10) == 0) {
+ // Note: Keep this in sync with p_dip_algorithm_values.
p += 10;
if (strncmp(p, "myers", 5) == 0) {
p += 5;
@@ -3133,7 +3135,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
if (added != 0) {
// Adjust marks. This will change the following entries!
- mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkNOOP);
+ mark_adjust(lnum, lnum + count - 1, MAXLNUM, added, kExtmarkNOOP);
if (curwin->w_cursor.lnum >= lnum) {
// Adjust the cursor position if it's in/after the changed
// lines.
@@ -3144,7 +3146,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
}
}
}
- extmark_adjust(curbuf, lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkUndo);
+ extmark_adjust(curbuf, lnum, lnum + count - 1, MAXLNUM, added, kExtmarkUndo);
changed_lines(curbuf, lnum, 0, lnum + count, added, true);
if (did_free) {
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index cc22accf2b..a0cbc60f25 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1519,16 +1519,12 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
}
- // Force redraw when width of 'number' or 'relativenumber' column
- // changes.
- int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
- if (wp->w_nrwidth != nrwidth) {
+ const int nrwidth_before = wp->w_nrwidth;
+ int nrwidth_new = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
+ // Force redraw when width of 'number' or 'relativenumber' column changes.
+ if (wp->w_nrwidth != nrwidth_new) {
type = UPD_NOT_VALID;
- wp->w_nrwidth = nrwidth;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
- }
+ wp->w_nrwidth = nrwidth_new;
} else if (buf->b_mod_set
&& buf->b_mod_xlines != 0
&& wp->w_redraw_top != 0) {
@@ -2498,6 +2494,10 @@ static void win_update(win_T *wp, DecorProviders *providers)
}
}
+ if (nrwidth_before != wp->w_nrwidth && buf->terminal) {
+ terminal_check_size(buf->terminal);
+ }
+
// restore got_int, unless CTRL-C was hit while redrawing
if (!got_int) {
got_int = save_got_int;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 7f29e615ab..58cface37f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -73,7 +73,7 @@ typedef struct insert_state {
int cmdchar;
int cmdchar_todo; // cmdchar to handle once in init_prompt
int startln;
- long count;
+ int count;
int c;
int lastc;
int i;
@@ -1230,7 +1230,7 @@ static void insert_do_cindent(InsertState *s)
/// @param count repeat count for the command
///
/// @return true if a CTRL-O command caused the return (insert mode pending).
-bool edit(int cmdchar, bool startln, long count)
+bool edit(int cmdchar, bool startln, int count)
{
if (curbuf->terminal) {
if (ex_normal_busy) {
@@ -3394,7 +3394,7 @@ static void ins_ctrl_hat(void)
/// @param nomove when true, don't move the cursor
///
/// @return true when leaving insert mode, false when repeating the insert.
-static bool ins_esc(long *count, int cmdchar, bool nomove)
+static bool ins_esc(int *count, int cmdchar, bool nomove)
FUNC_ATTR_NONNULL_ARG(1)
{
static bool disabled_redraw = false;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0bf26b2451..9a90e430a7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -21,6 +21,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
@@ -1225,7 +1226,7 @@ fail:
///
/// @return [allocated] NULL when calling function fails, allocated string
/// otherwise.
-char *call_func_retstr(const char *const func, int argc, typval_T *argv)
+void *call_func_retstr(const char *const func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
@@ -8093,13 +8094,6 @@ void ex_execute(exarg_T *eap)
}
if (ret != FAIL && ga.ga_data != NULL) {
- if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) {
- // Mark the already saved text as finishing the line, so that what
- // follows is displayed on a new line when scrolling back at the
- // more prompt.
- msg_sb_eol();
- }
-
if (eap->cmdidx == CMD_echomsg) {
msg_ext_set_kind("echomsg");
msg(ga.ga_data, echo_attr);
@@ -8296,7 +8290,7 @@ void option_last_set_msg(LastSet last_set)
msg_puts(p);
if (last_set.script_ctx.sc_lnum > 0) {
msg_puts(_(line_msg));
- msg_outnum((long)last_set.script_ctx.sc_lnum);
+ msg_outnum(last_set.script_ctx.sc_lnum);
}
if (should_free) {
xfree(p);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index be69cd5657..38bcf8f50d 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -6,6 +6,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/event/time.h"
#include "nvim/ex_cmds_defs.h"
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 23ad3d3787..28fb9c6a5c 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -1542,6 +1542,7 @@ M.funcs = {
count = {
args = { 2, 4 },
base = 1,
+ tags = { 'E706' },
desc = [=[
Return the number of times an item with value {expr} appears
in |String|, |List| or |Dictionary| {comp}.
@@ -7381,6 +7382,7 @@ M.funcs = {
using a {n$} positional argument specifier. See |printf-$|.
+ *E1520*
The conversion specifiers and their meanings are:
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
@@ -11121,8 +11123,8 @@ M.funcs = {
user user name
host host name
fname original file name
- pid PID of the Vim process that created the swap
- file
+ pid PID of the Nvim process that created the swap
+ file, or zero if not running.
mtime last modification time in seconds
inode Optional: INODE number of the file
dirty 1 if file was modified, 0 if not
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 5bfce7c272..4dd3f193e6 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -33,6 +33,7 @@
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
@@ -8235,7 +8236,7 @@ static void f_swapfilelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
- get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
+ swapfile_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/// "swapname(expr)" function
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index 5dab12787b..5f6132f68c 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -6,6 +6,7 @@
#include "nvim/api/private/dispatch.h"
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index d5e8cb0109..c0b5416a05 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -115,6 +115,19 @@ typedef enum {
VAR_BLOB, ///< Blob, .v_blob is used.
} VarType;
+/// Type values for type().
+enum {
+ VAR_TYPE_NUMBER = 0,
+ VAR_TYPE_STRING = 1,
+ VAR_TYPE_FUNC = 2,
+ VAR_TYPE_LIST = 3,
+ VAR_TYPE_DICT = 4,
+ VAR_TYPE_FLOAT = 5,
+ VAR_TYPE_BOOL = 6,
+ VAR_TYPE_SPECIAL = 7,
+ VAR_TYPE_BLOB = 10,
+};
+
/// Structure that holds an internal variable value
typedef struct {
VarType v_type; ///< Variable type.
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 8d85c55e15..ca98aad6bc 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -14,6 +14,7 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
@@ -1125,7 +1126,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
msg_puts(", ");
}
if (argvars[i].v_type == VAR_NUMBER) {
- msg_outnum((long)argvars[i].vval.v_number);
+ msg_outnum((int)argvars[i].vval.v_number);
} else {
// Do not want errors such as E724 here.
emsg_off++;
@@ -2262,7 +2263,7 @@ void ex_function(exarg_T *eap)
}
msg_putchar('\n');
if (!eap->forceit) {
- msg_outnum((long)j + 1);
+ msg_outnum(j + 1);
if (j < 9) {
msg_putchar(' ');
}
@@ -2490,7 +2491,7 @@ void ex_function(exarg_T *eap)
} else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
- give_warning2(_("W22: Text found after :endfunction: %s"), p, true);
+ swmsg(true, _("W22: Text found after :endfunction: %s"), p);
}
if (nextcmd != NULL) {
// Another command follows. If the line came from "eap" we
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index 562c549b4b..c3fe56d30c 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 127397d9fa..c57324d5e9 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -27,6 +27,7 @@
#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
@@ -130,7 +131,7 @@ static const char e_non_numeric_argument_to_z[]
= N_("E144: Non-numeric argument to :z");
/// ":ascii" and "ga" implementation
-void do_ascii(const exarg_T *const eap)
+void do_ascii(exarg_T *eap)
{
char *dig;
int cc[MAX_MCO];
@@ -455,7 +456,7 @@ void ex_sort(exarg_T *eap)
regmatch_T regmatch;
int len;
linenr_T lnum;
- long maxlen = 0;
+ int maxlen = 0;
size_t count = (size_t)(eap->line2 - eap->line1) + 1;
size_t i;
char *p;
@@ -692,7 +693,7 @@ void ex_sort(exarg_T *eap)
// Adjust marks for deleted (or added) lines and prepare for displaying.
deleted = (linenr_T)count - (lnum - eap->line2);
if (deleted > 0) {
- mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, kExtmarkNOOP);
+ mark_adjust(eap->line2 - deleted, eap->line2, MAXLNUM, -deleted, kExtmarkNOOP);
msgmore(-deleted);
} else if (deleted < 0) {
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkNOOP);
@@ -921,7 +922,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
check_pos(curbuf, &VIsual);
}
- msgmore((long)count);
+ msgmore(count);
}
static char *prevcmd = NULL; // the previous command
@@ -1280,7 +1281,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
set_keep_msg(msg_buf, 0);
}
} else {
- msgmore((long)linecount);
+ msgmore(linecount);
}
}
} else {
@@ -3290,7 +3291,7 @@ static int check_regexp_delim(int c)
/// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights.
/// If <= 0, preview shouldn't be shown.
/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means.
-static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ns,
+static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_ns,
const handle_T cmdpreview_bufnr)
{
#define ADJUST_SUB_FIRSTLNUM() \
@@ -3317,7 +3318,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
} \
} while (0)
- long i = 0;
+ int i = 0;
regmmatch_T regmatch;
static subflags_T subflags = {
.do_all = false,
@@ -3345,7 +3346,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 };
static int pre_hl_id = 0;
pos_T old_cursor = curwin->w_cursor;
- long start_nsubs;
+ int start_nsubs;
bool did_save = false;
@@ -3441,7 +3442,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
// check for a trailing count
cmd = skipwhite(cmd);
if (ascii_isdigit(*cmd)) {
- i = getdigits_long(&cmd, true, 0);
+ i = getdigits_int(&cmd, true, 0);
if (i <= 0 && !eap->skip && subflags.do_error) {
emsg(_(e_zerocount));
return 0;
@@ -3520,8 +3521,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
&& (cmdpreview_ns <= 0 || preview_lines.lines_needed <= (linenr_T)p_cwh
|| lnum <= curwin->w_botline);
lnum++) {
- long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ int nmatch = (int)vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
+ (colnr_T)0, NULL, NULL);
if (nmatch) {
colnr_T copycol;
colnr_T matchcol;
@@ -3531,7 +3532,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
char *p1;
bool did_sub = false;
int lastone;
- long nmatch_tl = 0; // nr of lines matched below lnum
+ linenr_T nmatch_tl = 0; // nr of lines matched below lnum
int do_again; // do it again after joining lines
bool skip_match = false;
linenr_T sub_firstlnum; // nr of first sub line
@@ -3804,7 +3805,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
// Same highlight as wait_return().
smsg(HL_ATTR(HLF_R), _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
msg_no_more = false;
- msg_scroll = (int)i;
+ msg_scroll = i;
if (!ui_has(kUIMessages)) {
ui_cursor_goto(msg_row, msg_col);
}
@@ -4086,9 +4087,9 @@ skip:
// need to replace the line first (using \zs after \n).
if (lastone
|| nmatch_tl > 0
- || (nmatch = vim_regexec_multi(&regmatch, curwin,
- curbuf, sub_firstlnum,
- matchcol, NULL, NULL)) == 0
+ || (nmatch = (int)vim_regexec_multi(&regmatch, curwin,
+ curbuf, sub_firstlnum,
+ matchcol, NULL, NULL)) == 0
|| regmatch.startpos[0].lnum > 0) {
if (new_start != NULL) {
// Copy the rest of the line, that didn't match.
@@ -4118,13 +4119,12 @@ skip:
for (i = 0; i < nmatch_tl; i++) {
ml_delete(lnum, false);
}
- mark_adjust(lnum, lnum + (linenr_T)nmatch_tl - 1,
- (long)MAXLNUM, (linenr_T)(-nmatch_tl), kExtmarkNOOP);
+ mark_adjust(lnum, lnum + nmatch_tl - 1, MAXLNUM, -nmatch_tl, kExtmarkNOOP);
if (subflags.do_ask) {
- deleted_lines(lnum, (linenr_T)nmatch_tl);
+ deleted_lines(lnum, nmatch_tl);
}
lnum--;
- line2 -= (linenr_T)nmatch_tl; // nr of lines decreases
+ line2 -= nmatch_tl; // nr of lines decreases
nmatch_tl = 0;
}
@@ -4149,8 +4149,8 @@ skip:
copycol = 0;
}
if (nmatch == -1 && !lastone) {
- nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
- sub_firstlnum, matchcol, NULL, NULL);
+ nmatch = (int)vim_regexec_multi(&regmatch, curwin, curbuf,
+ sub_firstlnum, matchcol, NULL, NULL);
}
// 5. break if there isn't another match in this line
@@ -4581,7 +4581,7 @@ bool prepare_tagpreview(bool undo_sync)
///
/// @return 1 if preview window isn't needed, 2 if preview window is needed.
static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id,
- long cmdpreview_ns, handle_T cmdpreview_bufnr)
+ int cmdpreview_ns, handle_T cmdpreview_bufnr)
FUNC_ATTR_NONNULL_ALL
{
char *save_shm_p = xstrdup(p_shm);
@@ -4683,9 +4683,9 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i
}
linenr_origbuf = match.end.lnum;
- bufhl_add_hl_pos_offset(cmdpreview_buf, (int)cmdpreview_ns, hl_id, p_start, p_end, col_width);
+ bufhl_add_hl_pos_offset(cmdpreview_buf, cmdpreview_ns, hl_id, p_start, p_end, col_width);
}
- bufhl_add_hl_pos_offset(orig_buf, (int)cmdpreview_ns, hl_id, match.start, match.end, 0);
+ bufhl_add_hl_pos_offset(orig_buf, cmdpreview_ns, hl_id, match.start, match.end, 0);
}
xfree(str);
@@ -4703,7 +4703,7 @@ void ex_substitute(exarg_T *eap)
}
/// :substitute command preview callback.
-int ex_substitute_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr)
+int ex_substitute_preview(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr)
{
// Only preview once the pattern delimiter has been typed
if (*eap->arg && !ASCII_ISALNUM(*eap->arg)) {
@@ -4772,7 +4772,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
void ex_oldfiles(exarg_T *eap)
{
list_T *l = get_vim_var_list(VV_OLDFILES);
- long nr = 0;
+ int nr = 0;
if (l == NULL) {
msg(_("No old files"), 0);
@@ -4806,7 +4806,7 @@ void ex_oldfiles(exarg_T *eap)
nr = prompt_for_number(false);
msg_starthere();
if (nr > 0 && nr <= tv_list_len(l)) {
- const char *const p = tv_list_find_str(l, (int)nr - 1);
+ const char *const p = tv_list_find_str(l, nr - 1);
if (p == NULL) {
return;
}
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 78f880db56..e15ba673ce 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -5,6 +5,7 @@
#include <stdint.h>
#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_eval_defs.h"
#include "nvim/normal.h"
#include "nvim/pos.h"
#include "nvim/regexp_defs.h"
@@ -69,20 +70,20 @@
#define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file
#define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed
-// values for cmd_addr_type
+/// values for cmd_addr_type
typedef enum {
- ADDR_LINES, // buffer line numbers
- ADDR_WINDOWS, // window number
- ADDR_ARGUMENTS, // argument number
- ADDR_LOADED_BUFFERS, // buffer number of loaded buffer
- ADDR_BUFFERS, // buffer number
- ADDR_TABS, // tab page number
- ADDR_TABS_RELATIVE, // Tab page that only relative
- ADDR_QUICKFIX_VALID, // quickfix list valid entry number
- ADDR_QUICKFIX, // quickfix list entry number
- ADDR_UNSIGNED, // positive count or zero, defaults to 1
- ADDR_OTHER, // something else, use line number for '$', '%', etc.
- ADDR_NONE, // no range used
+ ADDR_LINES, ///< buffer line numbers
+ ADDR_WINDOWS, ///< window number
+ ADDR_ARGUMENTS, ///< argument number
+ ADDR_LOADED_BUFFERS, ///< buffer number of loaded buffer
+ ADDR_BUFFERS, ///< buffer number
+ ADDR_TABS, ///< tab page number
+ ADDR_TABS_RELATIVE, ///< Tab page that only relative
+ ADDR_QUICKFIX_VALID, ///< quickfix list valid entry number
+ ADDR_QUICKFIX, ///< quickfix list entry number
+ ADDR_UNSIGNED, ///< positive count or zero, defaults to 1
+ ADDR_OTHER, ///< something else, use line number for '$', '%', etc.
+ ADDR_NONE, ///< no range used
} cmd_addr_T;
typedef struct exarg exarg_T;
@@ -93,7 +94,7 @@ typedef struct exarg exarg_T;
#define BAD_DROP (-2) // erase it
typedef void (*ex_func_T)(exarg_T *eap);
-typedef int (*ex_preview_func_T)(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr);
+typedef int (*ex_preview_func_T)(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr);
// NOTE: These possible could be removed and changed so that
// Callback could take a "command" style string, and simply
@@ -128,54 +129,13 @@ typedef char *(*LineGetter)(int, void *, int, bool);
/// Structure for command definition.
typedef struct cmdname {
- char *cmd_name; ///< Name of the command.
- ex_func_T cmd_func; ///< Function with implementation of this command.
- ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command.
- uint32_t cmd_argt; ///< Relevant flags from the declared above.
- cmd_addr_T cmd_addr_type; ///< Flag for address type.
+ char *cmd_name; ///< Name of the command.
+ ex_func_T cmd_func; ///< Function with implementation of this command.
+ ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command.
+ uint32_t cmd_argt; ///< Relevant flags from the declared above.
+ cmd_addr_T cmd_addr_type; ///< Flag for address type.
} CommandDefinition;
-// A list used for saving values of "emsg_silent". Used by ex_try() to save the
-// value of "emsg_silent" if it was non-zero. When this is done, the CSF_SILENT
-// flag below is set.
-typedef struct eslist_elem eslist_T;
-struct eslist_elem {
- int saved_emsg_silent; // saved value of "emsg_silent"
- eslist_T *next; // next element on the list
-};
-
-// For conditional commands a stack is kept of nested conditionals.
-// When cs_idx < 0, there is no conditional command.
-enum {
- CSTACK_LEN = 50,
-};
-
-typedef struct {
- int cs_flags[CSTACK_LEN]; // CSF_ flags
- char cs_pending[CSTACK_LEN]; // CSTP_: what's pending in ":finally"
- union {
- void *csp_rv[CSTACK_LEN]; // return typeval for pending return
- void *csp_ex[CSTACK_LEN]; // exception for pending throw
- } cs_pend;
- void *cs_forinfo[CSTACK_LEN]; // info used by ":for"
- int cs_line[CSTACK_LEN]; // line nr of ":while"/":for" line
- int cs_idx; // current entry, or -1 if none
- int cs_looplevel; // nr of nested ":while"s and ":for"s
- int cs_trylevel; // nr of nested ":try"s
- eslist_T *cs_emsg_silent_list; // saved values of "emsg_silent"
- int cs_lflags; // loop flags: CSL_ flags
-} cstack_T;
-#define cs_rettv cs_pend.csp_rv
-#define cs_exception cs_pend.csp_ex
-
-// Flags for the cs_lflags item in cstack_T.
-enum {
- CSL_HAD_LOOP = 1, // just found ":while" or ":for"
- CSL_HAD_ENDLOOP = 2, // just found ":endwhile" or ":endfor"
- CSL_HAD_CONT = 4, // just found ":continue"
- CSL_HAD_FINA = 8, // just found ":finally"
-};
-
/// Arguments used for Ex commands.
struct exarg {
char *arg; ///< argument of the command
@@ -222,41 +182,6 @@ struct exarg {
#define EXFLAG_NR 0x02 // '#': number
#define EXFLAG_PRINT 0x04 // 'p': print
-typedef enum {
- XP_PREFIX_NONE, ///< prefix not used
- XP_PREFIX_NO, ///< "no" prefix for bool option
- XP_PREFIX_INV, ///< "inv" prefix for bool option
-} xp_prefix_T;
-
-// used for completion on the command line
-struct expand {
- char *xp_pattern; // start of item to expand
- int xp_context; // type of expansion
- size_t xp_pattern_len; // bytes in xp_pattern before cursor
- xp_prefix_T xp_prefix;
- char *xp_arg; // completion function
- LuaRef xp_luaref; // Ref to Lua completion function
- sctx_T xp_script_ctx; // SCTX for completion function
- int xp_backslash; // one of the XP_BS_ values
-#ifndef BACKSLASH_IN_FILENAME
- int xp_shell; // true for a shell command, more
- // characters need to be escaped
-#endif
- int xp_numfiles; // number of files found by file name completion
- int xp_col; // cursor position in line
- int xp_selected; // selected index in completion
- char *xp_orig; // originally expanded string
- char **xp_files; // list of files
- char *xp_line; // text being completed
-#define EXPAND_BUF_LEN 256
- char xp_buf[EXPAND_BUF_LEN]; // buffer for returned match
-};
-
-// values for xp_backslash
-#define XP_BS_NONE 0 // nothing special for backslashes
-#define XP_BS_ONE 1 // uses one backslash before a space
-#define XP_BS_THREE 2 // uses three backslashes before a space
-
enum {
CMOD_SANDBOX = 0x0001, ///< ":sandbox"
CMOD_SILENT = 0x0002, ///< ":silent"
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 02783e032b..eb89e0fc9d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -22,6 +22,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/debugger.h"
#include "nvim/digraph.h"
@@ -1629,7 +1630,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool pre
// Call the function to execute the builtin command or the preview callback.
eap->errmsg = NULL;
if (preview) {
- *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(),
+ *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, (int)cmdpreview_get_ns(),
cmdpreview_get_bufnr());
} else {
(cmdnames[eap->cmdidx].cmd_func)(eap);
@@ -5910,7 +5911,7 @@ static void ex_submagic(exarg_T *eap)
}
/// ":smagic" and ":snomagic" preview callback.
-static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr)
+static int ex_submagic_preview(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr)
{
const optmagic_T saved = magic_overruled;
@@ -6043,7 +6044,7 @@ static void ex_redo(exarg_T *eap)
/// ":earlier" and ":later".
static void ex_later(exarg_T *eap)
{
- long count = 0;
+ int count = 0;
bool sec = false;
bool file = false;
char *p = eap->arg;
@@ -6051,7 +6052,7 @@ static void ex_later(exarg_T *eap)
if (*p == NUL) {
count = 1;
} else if (isdigit((uint8_t)(*p))) {
- count = getdigits_long(&p, false, 0);
+ count = getdigits_int(&p, false, 0);
switch (*p) {
case 's':
p++; sec = true; break;
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 0fd14c81d3..59f45fce88 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/getchar_defs.h"
#include "nvim/globals.h"
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 3ad3368900..6713cdb549 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -5,6 +5,37 @@
#include "nvim/pos.h"
+/// A list used for saving values of "emsg_silent". Used by ex_try() to save the
+/// value of "emsg_silent" if it was non-zero. When this is done, the CSF_SILENT
+/// flag below is set.
+typedef struct eslist_elem eslist_T;
+struct eslist_elem {
+ int saved_emsg_silent; ///< saved value of "emsg_silent"
+ eslist_T *next; ///< next element on the list
+};
+
+/// For conditional commands a stack is kept of nested conditionals.
+/// When cs_idx < 0, there is no conditional command.
+enum { CSTACK_LEN = 50, };
+
+typedef struct {
+ int cs_flags[CSTACK_LEN]; ///< CSF_ flags
+ char cs_pending[CSTACK_LEN]; ///< CSTP_: what's pending in ":finally"
+ union {
+ void *csp_rv[CSTACK_LEN]; ///< return typeval for pending return
+ void *csp_ex[CSTACK_LEN]; ///< exception for pending throw
+ } cs_pend;
+ void *cs_forinfo[CSTACK_LEN]; ///< info used by ":for"
+ int cs_line[CSTACK_LEN]; ///< line nr of ":while"/":for" line
+ int cs_idx; ///< current entry, or -1 if none
+ int cs_looplevel; ///< nr of nested ":while"s and ":for"s
+ int cs_trylevel; ///< nr of nested ":try"s
+ eslist_T *cs_emsg_silent_list; ///< saved values of "emsg_silent"
+ int cs_lflags; ///< loop flags: CSL_ flags
+} cstack_T;
+#define cs_rettv cs_pend.csp_rv
+#define cs_exception cs_pend.csp_ex
+
/// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
/// was used.
enum {
@@ -37,6 +68,14 @@ enum {
CSTP_FINISH = 32, ///< ":finish" is pending
};
+/// Flags for the cs_lflags item in cstack_T.
+enum {
+ CSL_HAD_LOOP = 1, ///< just found ":while" or ":for"
+ CSL_HAD_ENDLOOP = 2, ///< just found ":endwhile" or ":endfor"
+ CSL_HAD_CONT = 4, ///< just found ":continue"
+ CSL_HAD_FINA = 8, ///< just found ":finally"
+};
+
/// A list of error messages that can be converted to an exception. "throw_msg"
/// is only set in the first element of the list. Usually, it points to the
/// original message stored in that element, but sometimes it points to a later
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6fa607d569..2a1dffacb7 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -71,6 +71,7 @@
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
@@ -141,11 +142,11 @@ typedef struct cmdpreview_undo_info {
u_header_T *save_b_u_curhead;
int save_b_u_numhead;
bool save_b_u_synced;
- long save_b_u_seq_last;
- long save_b_u_save_nr_last;
- long save_b_u_seq_cur;
+ int save_b_u_seq_last;
+ int save_b_u_save_nr_last;
+ int save_b_u_seq_cur;
time_t save_b_u_time_cur;
- long save_b_u_save_nr_cur;
+ int save_b_u_save_nr_cur;
char *save_b_u_line_ptr;
linenr_T save_b_u_line_lnum;
colnr_T save_b_u_line_colnr;
@@ -206,7 +207,7 @@ static int cedit_key = -1; ///< key value of 'cedit' option
#endif
static handle_T cmdpreview_bufnr = 0;
-static long cmdpreview_ns = 0;
+static int cmdpreview_ns = 0;
static void save_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL
@@ -1328,7 +1329,7 @@ static int command_line_execute(VimState *state, int key)
if (!cmd_silent) {
if (!ui_has(kUICmdline)) {
- cmd_cursor_goto(msg_row, 0);
+ msg_cursor_goto(msg_row, 0);
}
ui_flush();
}
@@ -2766,6 +2767,7 @@ int check_opt_wim(void)
}
for (char *p = p_wim; *p; p++) {
+ // Note: Keep this in sync with p_wim_values.
for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
return FAIL;
@@ -3882,7 +3884,7 @@ void redrawcmd(void)
// when 'incsearch' is set there may be no command line while redrawing
if (ccline.cmdbuff == NULL) {
- cmd_cursor_goto(cmdline_row, 0);
+ msg_cursor_goto(cmdline_row, 0);
msg_clr_eos();
return;
}
@@ -3959,14 +3961,7 @@ void cursorcmd(void)
}
}
- cmd_cursor_goto(msg_row, msg_col);
-}
-
-static void cmd_cursor_goto(int row, int col)
-{
- ScreenGrid *grid = &msg_grid_adj;
- grid_adjust(&grid, &row, &col);
- ui_grid_cursor_goto(grid->handle, row, col);
+ msg_cursor_goto(msg_row, msg_col);
}
void gotocmdline(bool clr)
@@ -3983,7 +3978,7 @@ void gotocmdline(bool clr)
if (clr) { // clear the bottom line(s)
msg_clr_eos(); // will reset clear_cmdline
}
- cmd_cursor_goto(cmdline_row, 0);
+ msg_cursor_goto(cmdline_row, 0);
}
// Check the word in front of the cursor for an abbreviation.
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index aaafedcb4b..61bbd5b9e1 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "klib/kvec.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/option_defs.h"
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 4e8b7be4aa..05b48966ff 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -28,7 +28,7 @@
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
@@ -1753,7 +1753,7 @@ failed:
c = true;
}
- msg_add_lines(c, (long)linecnt, filesize);
+ msg_add_lines(c, linecnt, filesize);
XFREE_CLEAR(keep_msg);
p = NULL;
@@ -2156,7 +2156,7 @@ bool msg_add_fileformat(int eol_type)
}
/// Append line and character count to IObuff.
-void msg_add_lines(int insert_space, long lnum, off_T nchars)
+void msg_add_lines(int insert_space, linenr_T lnum, off_T nchars)
{
char *p = IObuff + strlen(IObuff);
@@ -2700,7 +2700,7 @@ int vim_rename(const char *from, const char *to)
}
// Rename() failed, try copying the file.
- long perm = os_getperm(from);
+ int perm = os_getperm(from);
// For systems that support ACL: get the ACL from the original file.
vim_acl_T acl = os_get_acl(from);
int fd_in = os_open(from, O_RDONLY, 0);
@@ -2710,7 +2710,7 @@ int vim_rename(const char *from, const char *to)
}
// Create the new file with same permissions as the original.
- int fd_out = os_open(to, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, (int)perm);
+ int fd_out = os_open(to, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, perm);
if (fd_out < 0) {
close(fd_in);
os_free_acl(acl);
@@ -2905,7 +2905,7 @@ int buf_check_timestamp(buf_T *buf)
&& (!(file_info_ok = os_fileinfo(buf->b_ffname, &file_info))
|| time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns)
|| (int)file_info.stat.st_mode != buf->b_orig_mode)) {
- const long prev_b_mtime = buf->b_mtime;
+ const int prev_b_mtime = (int)buf->b_mtime;
retval = 1;
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 5eb2689233..92ea866239 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -3,7 +3,10 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/os/fs_defs.h"
#include "nvim/os/os.h"
// Values for readfile() flags
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index b9fae7d0fe..61767583ec 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -102,7 +102,7 @@ for _, cmd in ipairs(defs) do
end
local preview_func
if cmd.preview_func then
- preview_func = string.format("(ex_preview_func_T)&%s", cmd.preview_func)
+ preview_func = string.format("&%s", cmd.preview_func)
else
preview_func = "NULL"
end
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 0932a1357f..05def71caa 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -35,6 +35,8 @@ local redraw_flags={
local list_flags={
comma='P_COMMA',
onecomma='P_ONECOMMA',
+ commacolon='P_COMMA|P_COLON',
+ onecommacolon='P_ONECOMMA|P_COLON',
flags='P_FLAGLIST',
flagscomma='P_COMMA|P_FLAGLIST',
}
@@ -166,6 +168,9 @@ local function dump_option(i, o)
if o.cb then
w(' .opt_did_set_cb=' .. o.cb)
end
+ if o.expand_cb then
+ w(' .opt_expand_cb=' .. o.expand_cb)
+ end
if o.enable_if then
w('#else')
w(' .var=NULL')
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index b696f8ab37..7c5d39bd70 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -291,10 +291,10 @@ static void delete_buff_tail(buffheader_T *buf, int slen)
}
/// Add number "n" to buffer "buf".
-static void add_num_buff(buffheader_T *buf, long n)
+static void add_num_buff(buffheader_T *buf, int n)
{
char number[32];
- snprintf(number, sizeof(number), "%ld", n);
+ snprintf(number, sizeof(number), "%d", n);
add_buff(buf, number, -1L);
}
@@ -589,7 +589,7 @@ void AppendCharToRedobuff(int c)
}
// Append a number to the redo buffer.
-void AppendNumberToRedobuff(long n)
+void AppendNumberToRedobuff(int n)
{
if (!block_redo) {
add_num_buff(&redobuff, n);
@@ -643,7 +643,7 @@ void stuffcharReadbuff(int c)
}
// Append a number to the stuff buffer.
-void stuffnumReadbuff(long n)
+void stuffnumReadbuff(int n)
{
add_num_buff(&readbuf1, n);
}
@@ -753,7 +753,7 @@ static void copy_redo(bool old_redo)
/// CTRL-O <.> in insert mode
///
/// @return FAIL for failure, OK otherwise
-int start_redo(long count, bool old_redo)
+int start_redo(int count, bool old_redo)
{
// init the pointers; return if nothing to redo
if (read_redo(true, old_redo) == FAIL) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 1a7a62174d..d462c83710 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -832,7 +832,7 @@ EXTERN char no_lines_msg[] INIT(= N_("--No lines in buffer--"));
// When ":global" is used to number of substitutions and changed lines is
// accumulated until it's finished.
// Also used for ":spellrepall".
-EXTERN long sub_nsubs; // total number of substitutions
+EXTERN int sub_nsubs; // total number of substitutions
EXTERN linenr_T sub_nlines; // total number of lines changed
// table to store parsed 'wildmode'
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 712688368b..7a707407d2 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -455,6 +455,22 @@ void grid_line_flush(void)
false, 0, false, invalid_row);
}
+/// flush grid line but only if on a valid row
+///
+/// This is a stopgap until message.c has been refactored to behave
+void grid_line_flush_if_valid_row(void)
+{
+ if (grid_line_row < 0 || grid_line_row >= grid_line_grid->rows) {
+ if (rdb_flags & RDB_INVALID) {
+ abort();
+ } else {
+ grid_line_grid = NULL;
+ return;
+ }
+ }
+ grid_line_flush();
+}
+
/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
/// to "end_col" (exclusive) with character "c1" in first column followed by
/// "c2" in the other columns. Use attributes "attr".
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 09a28989e4..5cbff6e3dd 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -18,13 +18,13 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
-#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
@@ -2291,10 +2291,10 @@ static void highlight_list_two(int cnt, int attr)
}
/// Function given to ExpandGeneric() to obtain the list of group names.
-const char *get_highlight_name(expand_T *const xp, int idx)
+char *get_highlight_name(expand_T *const xp, int idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
- return get_highlight_name_ext(xp, idx, true);
+ return (char *)get_highlight_name_ext(xp, idx, true);
}
/// Obtain a highlight group name.
diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h
index 77a3684067..369864552c 100644
--- a/src/nvim/highlight_group.h
+++ b/src/nvim/highlight_group.h
@@ -3,6 +3,7 @@
#include "nvim/api/keysets.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index b7bc23cda5..d19164b24f 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -55,7 +55,7 @@
/// @return false for an error.
bool tabstop_set(char *var, colnr_T **array)
{
- long valcount = 1;
+ int valcount = 1;
int t;
char *cp;
@@ -89,7 +89,7 @@ bool tabstop_set(char *var, colnr_T **array)
return false;
}
- *array = (colnr_T *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
+ *array = (colnr_T *)xmalloc((unsigned)(valcount + 1) * sizeof(int));
(*array)[0] = (colnr_T)valcount;
t = 1;
@@ -122,13 +122,13 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts)
OptInt ts = ts_arg == 0 ? 8 : ts_arg;
colnr_T tabcol = 0;
int t;
- long padding = 0;
+ int padding = 0;
if (vts == NULL || vts[0] == 0) {
return (int)(ts - (col % ts));
}
- const long tabcount = vts[0];
+ const int tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
@@ -141,7 +141,7 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts)
padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
}
- return (int)padding;
+ return padding;
}
/// Find the size of the tab that covers a particular column.
@@ -149,13 +149,13 @@ int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts)
{
colnr_T tabcol = 0;
int t;
- long tab_size = 0;
+ int tab_size = 0;
if (vts == NULL || vts[0] == 0) {
return (int)ts;
}
- const long tabcount = vts[0];
+ const int tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
@@ -167,20 +167,20 @@ int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts)
tab_size = vts[tabcount];
}
- return (int)tab_size;
+ return tab_size;
}
/// Find the column on which a tab starts.
-colnr_T tabstop_start(colnr_T col, long ts, colnr_T *vts)
+colnr_T tabstop_start(colnr_T col, int ts, colnr_T *vts)
{
colnr_T tabcol = 0;
int t;
if (vts == NULL || vts[0] == 0) {
- return (int)((col / ts) * ts);
+ return ((col / ts) * ts);
}
- const long tabcount = vts[0];
+ const int tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
@@ -194,26 +194,26 @@ colnr_T tabstop_start(colnr_T col, long ts, colnr_T *vts)
/// Find the number of tabs and spaces necessary to get from one column
/// to another.
-void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr_T *vts, int *ntabs,
+void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, const colnr_T *vts, int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
- long padding = 0;
+ int padding = 0;
int t;
- long ts = ts_arg == 0 ? (long)curbuf->b_p_ts : ts_arg;
+ int ts = ts_arg == 0 ? (int)curbuf->b_p_ts : ts_arg;
assert(ts != 0); // suppress clang "Division by zero"
if (vts == NULL || vts[0] == 0) {
int tabs = 0;
- const int initspc = (int)(ts - (start_col % ts));
+ const int initspc = (ts - (start_col % ts));
if (spaces >= initspc) {
spaces -= initspc;
tabs++;
}
- tabs += (int)(spaces / ts);
- spaces -= (int)((spaces / ts) * ts);
+ tabs += (spaces / ts);
+ spaces -= ((spaces / ts) * ts);
*ntabs = tabs;
*nspcs = spaces;
@@ -221,7 +221,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr
}
// Find the padding needed to reach the next tabstop.
- const long tabcount = vts[0];
+ const int tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > start_col) {
@@ -241,7 +241,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr
}
*ntabs = 1;
- spaces -= (int)padding;
+ spaces -= padding;
// At least one tab has been used. See if any more will fit.
while (spaces != 0 && ++t <= tabcount) {
@@ -251,7 +251,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr
return;
}
*ntabs += 1;
- spaces -= (int)padding;
+ spaces -= padding;
}
*ntabs += spaces / (int)vts[tabcount];
@@ -283,21 +283,21 @@ bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2)
}
/// Copy a tabstop array, allocating space for the new array.
-int *tabstop_copy(const long *oldts)
+int *tabstop_copy(const int *oldts)
{
- long *newts;
+ int *newts;
int t;
if (oldts == 0) {
return 0;
}
- newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
+ newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(int));
for (t = 0; t <= oldts[0]; t++) {
newts[t] = oldts[t];
}
- return (int *)newts;
+ return newts;
}
/// Return a count of the number of tabstops.
@@ -316,25 +316,23 @@ int tabstop_first(colnr_T *ts)
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)
{
- long result = get_sw_value_col(buf, 0);
- assert(result >= 0 && result <= INT_MAX);
- return (int)result;
+ int result = get_sw_value_col(buf, 0);
+ return result;
}
/// Idem, using "pos".
-long get_sw_value_pos(buf_T *buf, pos_T *pos)
+int get_sw_value_pos(buf_T *buf, pos_T *pos)
{
pos_T save_cursor = curwin->w_cursor;
- long sw_value;
curwin->w_cursor = *pos;
- sw_value = get_sw_value_col(buf, get_nolist_virtcol());
+ int sw_value = get_sw_value_col(buf, get_nolist_virtcol());
curwin->w_cursor = save_cursor;
return sw_value;
}
/// Idem, using the first non-black in the current line.
-long get_sw_value_indent(buf_T *buf)
+int get_sw_value_indent(buf_T *buf)
{
pos_T pos = curwin->w_cursor;
@@ -343,9 +341,9 @@ long get_sw_value_indent(buf_T *buf)
}
/// Idem, using virtual column "col".
-long get_sw_value_col(buf_T *buf, colnr_T col)
+int get_sw_value_col(buf_T *buf, colnr_T col)
{
- return buf->b_p_sw ? (long)buf->b_p_sw
+ return buf->b_p_sw ? (int)buf->b_p_sw
: tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
}
@@ -760,6 +758,7 @@ bool briopt_check(win_T *wp)
char *p = wp->w_p_briopt;
while (*p != NUL) {
+ // Note: Keep this in sync with p_briopt_values
if (strncmp(p, "shift:", 6) == 0
&& ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
@@ -939,12 +938,12 @@ void ex_retab(exarg_T *eap)
{
linenr_T lnum;
bool got_tab = false;
- long num_spaces = 0;
- long num_tabs;
- long len;
- long start_col = 0; // For start of white-space string
- long start_vcol = 0; // For start of white-space string
- long old_len;
+ int num_spaces = 0;
+ int num_tabs;
+ int len;
+ int start_col = 0; // For start of white-space string
+ int64_t start_vcol = 0; // For start of white-space string
+ int old_len;
char *new_line = (char *)1; // init to non-NULL
colnr_T *new_vts_array = NULL;
char *new_ts_str; // string value of tab argument
@@ -975,8 +974,8 @@ void ex_retab(exarg_T *eap)
}
for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
char *ptr = ml_get(lnum);
- long col = 0;
- long vcol = 0;
+ int col = 0;
+ int64_t vcol = 0;
bool did_undo = false; // called u_save for current line
while (true) {
if (ascii_iswhite(ptr[col])) {
@@ -995,13 +994,13 @@ void ex_retab(exarg_T *eap)
// Retabulate this string of white-space
// len is virtual length of white string
- len = num_spaces = vcol - start_vcol;
+ len = num_spaces = (int)(vcol - start_vcol);
num_tabs = 0;
if (!curbuf->b_p_et) {
int t, s;
tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
- (long)curbuf->b_p_ts, new_vts_array, &t, &s);
+ (int)curbuf->b_p_ts, new_vts_array, &t, &s);
num_tabs = t;
num_spaces = s;
}
@@ -1018,7 +1017,7 @@ void ex_retab(exarg_T *eap)
// len is actual number of white characters used
len = num_spaces + num_tabs;
- old_len = (long)strlen(ptr);
+ old_len = (int)strlen(ptr);
const long new_len = old_len - col + start_col + len + 1;
if (new_len <= 0 || new_len >= MAXCOL) {
emsg_text_too_long();
@@ -1030,7 +1029,7 @@ void ex_retab(exarg_T *eap)
memmove(new_line, ptr, (size_t)start_col);
}
memmove(new_line + start_col + len,
- ptr + col, (size_t)(old_len - col + 1));
+ ptr + col, (size_t)old_len - (size_t)col + 1);
ptr = new_line + start_col;
for (col = 0; col < len; col++) {
ptr[col] = (col < num_tabs) ? '\t' : ' ';
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 4a6c819015..3ada39c800 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -24,7 +24,6 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 18da7af416..d6fd14cc10 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -20,6 +20,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/change.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index b132a0d147..f3e2b6d1d0 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -8,6 +8,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/assert.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/func_attr.h"
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 1fb91d85b4..2cfa264754 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -21,6 +21,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index 405234dbd5..860bbad272 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -7,6 +7,7 @@
#include "nvim/api/keysets.h"
#include "nvim/api/private/defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/mapping_defs.h"
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 7b7c822b3b..a3cd569846 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -43,6 +43,7 @@
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/eval/typval.h"
@@ -77,8 +78,8 @@ typedef struct {
} convertStruct;
struct interval {
- long first;
- long last;
+ int first;
+ int last;
};
// uncrustify:off
@@ -2830,3 +2831,14 @@ void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// encoding options.
+char *get_encoding_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= (int)ARRAY_SIZE(enc_canon_table)) {
+ return NULL;
+ }
+
+ return (char *)enc_canon_table[idx].name;
+}
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 724a16014d..01a7d0d6ae 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/func_attr.h"
#include "nvim/mbyte_defs.h"
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 7bfa6db4ef..716d05e27a 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -168,17 +168,15 @@ enum {
B0_MAGIC_CHAR = 0x55,
};
-// Block zero holds all info about the swap file. This is the first block in
-// the file.
+// Block zero holds all info about the swapfile. This is the first block in the file.
//
-// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
-// swap files unusable!
+// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing swapfiles unusable!
//
// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
//
// This block is built up of single bytes, to make it portable across
// different machines. b0_magic_* is used to check the byte order and size of
-// variables, because the rest of the swap file is not portable.
+// variables, because the rest of the swapfile is not portable.
typedef struct {
char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
char b0_version[10]; // Vim version string
@@ -210,8 +208,7 @@ typedef struct {
// EOL_MAC + 1.
#define B0_FF_MASK 3
-// Swap file is in directory of edited file. Used to find the file from
-// different mount points.
+// Swapfile is in directory of edited file. Used to find the file from different mount points.
#define B0_SAME_DIR 4
// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
@@ -289,14 +286,14 @@ int ml_open(buf_T *buf)
buf->b_p_swf = false;
}
- // When 'updatecount' is non-zero swap file may be opened later.
+ // When 'updatecount' is non-zero swapfile may be opened later.
if (!buf->terminal && p_uc && buf->b_p_swf) {
buf->b_may_swap = true;
} else {
buf->b_may_swap = false;
}
- // Open the memfile. No swap file is created yet.
+ // Open the memfile. No swapfile is created yet.
memfile_T *mfp = mf_open(NULL, 0);
if (mfp == NULL) {
goto error;
@@ -335,7 +332,7 @@ int ml_open(buf_T *buf)
}
// Always sync block number 0 to disk, so we can check the file name in
- // the swap file in findswapname(). Don't do this for a help files or
+ // the swapfile in findswapname(). Don't do this for a help files or
// a spell buffer though.
// Only works when there's a swapfile, otherwise it's done when the file
// is created.
@@ -386,17 +383,17 @@ error:
}
/// ml_setname() is called when the file name of "buf" has been changed.
-/// It may rename the swap file.
+/// It may rename the swapfile.
void ml_setname(buf_T *buf)
{
bool success = false;
memfile_T *mfp = buf->b_ml.ml_mfp;
- if (mfp->mf_fd < 0) { // there is no swap file yet
- // When 'updatecount' is 0 and 'noswapfile' there is no swap file.
- // For help files we will make a swap file now.
+ if (mfp->mf_fd < 0) { // there is no swapfile yet
+ // When 'updatecount' is 0 and 'noswapfile' there is no swapfile.
+ // For help files we will make a swapfile now.
if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) {
- ml_open_file(buf); // create a swap file
+ ml_open_file(buf); // create a swapfile
}
return;
}
@@ -423,13 +420,13 @@ void ml_setname(buf_T *buf)
success = true;
break;
}
- // need to close the swap file before renaming
+ // need to close the swapfile before renaming
if (mfp->mf_fd >= 0) {
close(mfp->mf_fd);
mfp->mf_fd = -1;
}
- // try to rename the swap file
+ // try to rename the swapfile
if (vim_rename(mfp->mf_fname, fname) == 0) {
success = true;
mf_free_fnames(mfp);
@@ -440,10 +437,10 @@ void ml_setname(buf_T *buf)
xfree(fname); // this fname didn't work, try another
}
- if (mfp->mf_fd == -1) { // need to (re)open the swap file
+ if (mfp->mf_fd == -1) { // need to (re)open the swapfile
mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0);
if (mfp->mf_fd < 0) {
- // could not (re)open the swap file, what can we do????
+ // could not (re)open the swapfile, what can we do????
emsg(_("E301: Oops, lost the swap file!!!"));
return;
}
@@ -466,7 +463,7 @@ void ml_open_files(void)
}
}
-/// Open a swap file for an existing memfile, if there is no swap file yet.
+/// Open a swapfile for an existing memfile, if there is no swapfile yet.
/// If we are unable to find a file name, mf_fname will be NULL
/// and the memfile will be in memory only (no recovery possible).
void ml_open_file(buf_T *buf)
@@ -495,7 +492,7 @@ void ml_open_file(buf_T *buf)
if (*dirp == NUL) {
break;
}
- // There is a small chance that between choosing the swap file name
+ // There is a small chance that between choosing the swapfile name
// and creating it, another Vim creates the file. In that case the
// creation will fail and we will use another directory.
char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir);
@@ -514,7 +511,7 @@ void ml_open_file(buf_T *buf)
if (mf_sync(mfp, MFS_ZERO) == OK) {
// Mark all blocks that should be in the swapfile as dirty.
// Needed for when the 'swapfile' option was reset, so that
- // the swap file was deleted, and then on again.
+ // the swapfile was deleted, and then on again.
mf_set_dirty(mfp);
break;
}
@@ -531,12 +528,12 @@ void ml_open_file(buf_T *buf)
no_wait_return--;
}
- // don't try to open a swap file again
+ // don't try to open a swapfile again
buf->b_may_swap = false;
}
-/// If still need to create a swap file, and starting to edit a not-readonly
-/// file, or reading into an existing buffer, create a swap file now.
+/// If still need to create a swapfile, and starting to edit a not-readonly
+/// file, or reading into an existing buffer, create a swapfile now.
///
/// @param newfile reading file into new buffer
void check_need_swap(bool newfile)
@@ -553,7 +550,7 @@ void check_need_swap(bool newfile)
/// Close memline for buffer 'buf'.
///
-/// @param del_file if true, delete the swap file
+/// @param del_file if true, delete the swapfile
void ml_close(buf_T *buf, int del_file)
{
if (buf->b_ml.ml_mfp == NULL) { // not open
@@ -643,7 +640,7 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what)
mf_put(mfp, hp, true, false);
}
-/// Write file name and timestamp into block 0 of a swap file.
+/// Write file name and timestamp into block 0 of a swapfile.
/// Also set buf->b_mtime.
/// Don't use NameBuff[]!!!
static void set_b0_fname(ZeroBlock *b0p, buf_T *buf)
@@ -695,7 +692,7 @@ static void set_b0_fname(ZeroBlock *b0p, buf_T *buf)
add_b0_fenc(b0p, curbuf);
}
-/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
+/// Update the B0_SAME_DIR flag of the swapfile. It's set if the file and the
/// swapfile for "buf" are in the same directory.
/// This is fail safe: if we are not sure the directories are equal the flag is
/// not set.
@@ -724,27 +721,30 @@ static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf)
}
}
-/// Return true if the process with number "b0p->b0_pid" is still running.
-/// "swap_fname" is the name of the swap file, if it's from before a reboot then
-/// the result is false;
-static bool swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
+/// Returns the PID of the process that owns the swapfile, if it is running.
+///
+/// @param b0p swapfile data
+/// @param swap_fname Name of the swapfile. If it's from before a reboot, the result is 0.
+///
+/// @return PID, or 0 if process is not running or the swapfile is from before a reboot.
+static int swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
{
FileInfo st;
double uptime;
- // If the system rebooted after when the swap file was written then the
+ // If the system rebooted after when the swapfile was written then the
// process can't be running now.
if (os_fileinfo(swap_fname, &st)
&& uv_uptime(&uptime) == 0
&& (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) {
- return false;
+ return 0;
}
- return os_proc_running((int)char_to_long(b0p->b0_pid));
+ int pid = (int)char_to_long(b0p->b0_pid);
+ return os_proc_running(pid) ? pid : 0;
}
/// Try to recover curbuf from the .swp file.
///
-/// @param checkext if true, check the extension and detect whether it is a
-/// swap file.
+/// @param checkext if true, check the extension and detect whether it is a swapfile.
void ml_recover(bool checkext)
{
buf_T *buf = NULL;
@@ -766,8 +766,8 @@ void ml_recover(bool checkext)
int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
int attr = HL_ATTR(HLF_E);
- // If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
- // Otherwise a search is done to find the swap file(s).
+ // If the file name ends in ".s[a-w][a-z]" we assume this is the swapfile.
+ // Otherwise a search is done to find the swapfile(s).
char *fname = curbuf->b_fname;
if (fname == NULL) { // When there is no file name
fname = "";
@@ -782,17 +782,17 @@ void ml_recover(bool checkext)
} else {
directly = false;
- // count the number of matching swap files
+ // count the number of matching swapfiles
len = recover_names(fname, false, NULL, 0, NULL);
- if (len == 0) { // no swap files found
+ if (len == 0) { // no swapfiles found
semsg(_("E305: No swap file found for %s"), fname);
goto theend;
}
int i;
- if (len == 1) { // one swap file found, use it
+ if (len == 1) { // one swapfile found, use it
i = 1;
- } else { // several swap files found, choose
- // list the names of the swap files
+ } else { // several swapfiles found, choose
+ // list the names of the swapfiles
(void)recover_names(fname, true, NULL, 0, NULL);
msg_putchar('\n');
msg_puts(_("Enter number of swap file to use (0 to quit): "));
@@ -801,7 +801,7 @@ void ml_recover(bool checkext)
goto theend;
}
}
- // get the swap file name that will be used
+ // get the swapfile name that will be used
(void)recover_names(fname, false, NULL, i, &fname_used);
}
if (fname_used == NULL) {
@@ -812,7 +812,7 @@ void ml_recover(bool checkext)
getout(1);
}
- // Allocate a buffer structure for the swap file that is used for recovery.
+ // Allocate a buffer structure for the swapfile that is used for recovery.
// Only the memline in it is really used.
buf = xmalloc(sizeof(buf_T));
@@ -825,7 +825,7 @@ void ml_recover(bool checkext)
buf->b_ml.ml_locked = NULL; // no locked block
buf->b_ml.ml_flags = 0;
- // open the memfile from the old swap file
+ // open the memfile from the old swapfile
p = xstrdup(fname_used); // save "fname_used" for the message:
// mf_open() will consume "fname_used"!
mfp = mf_open(fname_used, O_RDONLY);
@@ -837,7 +837,7 @@ void ml_recover(bool checkext)
buf->b_ml.ml_mfp = mfp;
// The page size set in mf_open() might be different from the page size
- // used in the swap file, we must get it from block 0. But to read block
+ // used in the swapfile, we must get it from block 0. But to read block
// 0 we need a page size. Use the minimal size for block 0 here, it will
// be set to the real value below.
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
@@ -910,7 +910,7 @@ void ml_recover(bool checkext)
b0p = hp->bh_data;
}
- // If .swp file name given directly, use name from swap file for buffer.
+ // If .swp file name given directly, use name from swapfile for buffer.
if (directly) {
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
@@ -929,7 +929,7 @@ void ml_recover(bool checkext)
smsg(0, _("Original file \"%s\""), NameBuff);
msg_putchar('\n');
- // check date of swap file and original file
+ // check date of swapfile and original file
FileInfo org_file_info;
FileInfo swp_file_info;
long mtime = char_to_long(b0p->b0_mtime);
@@ -968,7 +968,7 @@ void ml_recover(bool checkext)
0, MAXLNUM, NULL, READ_NEW, false);
}
- // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
+ // Use the 'fileformat' and 'fileencoding' as stored in the swapfile.
if (b0_ff != 0) {
set_fileformat(b0_ff - 1, OPT_LOCAL);
}
@@ -1215,7 +1215,7 @@ void ml_recover(bool checkext)
// Warn there could be an active Vim on the same file, the user may
// want to kill it.
msg_puts(_("\nNote: process STILL RUNNING: "));
- msg_outnum(char_to_long(b0p->b0_pid));
+ msg_outnum((int)char_to_long(b0p->b0_pid));
}
msg_puts("\n\n");
cmdline_row = msg_row;
@@ -1231,7 +1231,7 @@ theend:
}
mf_close(mfp, false); // will also xfree(mfp->mf_fname)
}
- if (buf != NULL) { // may be NULL if swap file not found.
+ if (buf != NULL) { // may be NULL if swapfile not found.
xfree(buf->b_ml.ml_stack);
xfree(buf);
}
@@ -1243,20 +1243,20 @@ theend:
}
}
-/// Find the names of swap files in current directory and the directory given
+/// Find the names of swapfiles in current directory and the directory given
/// with the 'directory' option.
///
/// Used to:
-/// - list the swap files for "vim -r"
-/// - count the number of swap files when recovering
-/// - list the swap files when recovering
-/// - list the swap files for swapfilelist()
-/// - find the name of the n'th swap file when recovering
+/// - list the swapfiles for "vim -r"
+/// - count the number of swapfiles when recovering
+/// - list the swapfiles when recovering
+/// - list the swapfiles for swapfilelist()
+/// - find the name of the n'th swapfile when recovering
///
-/// @param fname base for swap file name
-/// @param do_list when true, list the swap file names
+/// @param fname base for swapfile name
+/// @param do_list when true, list the swapfile names
/// @param ret_list when not NULL add file names to it
-/// @param nr when non-zero, return nr'th swap file name
+/// @param nr when non-zero, return nr'th swapfile name
/// @param fname_out result when "nr" > 0
int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fname_out)
{
@@ -1273,7 +1273,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
if (fname != NULL) {
#ifdef HAVE_READLINK
- // Expand symlink in the file name, because the swap file is created
+ // Expand symlink in the file name, because the swapfile is created
// with the actual file instead of with the symlink.
if (resolve_symlink(fname, fname_buf) == OK) {
fname_res = fname_buf;
@@ -1342,9 +1342,9 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
num_files = 0;
}
- // When no swap file found, wildcard expansion might have failed (e.g.
+ // When no swapfile found, wildcard expansion might have failed (e.g.
// not able to execute the shell).
- // Try finding a swap file by simply adding ".swp" to the file name.
+ // Try finding a swapfile by simply adding ".swp" to the file name.
if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) {
char *swapname = modname(fname_res, ".swp", true);
if (swapname != NULL) {
@@ -1402,8 +1402,8 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
if (num_files) {
for (int i = 0; i < num_files; i++) {
- // print the swap file name
- msg_outnum((long)++file_count);
+ // print the swapfile name
+ msg_outnum(++file_count);
msg_puts(". ");
msg_puts(path_tail(files[i]));
msg_putchar('\n');
@@ -1456,12 +1456,13 @@ char *make_percent_swname(const char *dir, const char *name)
return d;
}
-static bool process_still_running;
+// PID of swapfile owner, or zero if not running.
+static int process_running;
-/// This is used by the swapinfo() function.
+/// For Vimscript "swapinfo()".
///
/// @return information found in swapfile "fname" in dictionary "d".
-void get_b0_dict(const char *fname, dict_T *d)
+void swapfile_dict(const char *fname, dict_T *d)
{
int fd;
ZeroBlock b0;
@@ -1482,7 +1483,7 @@ void get_b0_dict(const char *fname, dict_T *d)
tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname,
B0_FNAME_SIZE_ORG);
- tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid));
+ tv_dict_add_nr(d, S_LEN("pid"), swapfile_process_running(&b0, fname));
tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime));
tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0);
tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
@@ -1496,7 +1497,7 @@ void get_b0_dict(const char *fname, dict_T *d)
}
}
-/// Give information about an existing swap file.
+/// Loads info from swapfile `fname`, and displays it to the user.
///
/// @return timestamp (0 when unknown).
static time_t swapfile_info(char *fname)
@@ -1509,7 +1510,7 @@ static time_t swapfile_info(char *fname)
char uname[B0_UNAME_SIZE];
#endif
- // print the swap file date
+ // print the swapfile date
FileInfo file_info;
if (os_fileinfo(fname, &file_info)) {
#ifdef UNIX
@@ -1566,10 +1567,9 @@ static time_t swapfile_info(char *fname)
if (char_to_long(b0.b0_pid) != 0L) {
msg_puts(_("\n process ID: "));
- msg_outnum(char_to_long(b0.b0_pid));
- if (swapfile_process_running(&b0, fname)) {
+ msg_outnum((int)char_to_long(b0.b0_pid));
+ if ((process_running = swapfile_process_running(&b0, fname))) {
msg_puts(_(" (STILL RUNNING)"));
- process_still_running = true;
}
}
@@ -1589,13 +1589,12 @@ static time_t swapfile_info(char *fname)
return x;
}
-/// @return true if the swap file looks OK and there are no changes, thus it
-/// can be safely deleted.
+/// @return true if the swapfile looks OK and there are no changes, thus it can be safely deleted.
static bool swapfile_unchanged(char *fname)
{
ZeroBlock b0;
- // Swap file must exist.
+ // Swapfile must exist.
if (!os_path_exists(fname)) {
return false;
}
@@ -1653,7 +1652,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot)
{
int num_names = 0;
- // May also add the file name with a dot prepended, for swap file in same
+ // May also add the file name with a dot prepended, for swapfile in same
// dir as original file.
if (prepend_dot) {
names[num_names] = modname(path, ".sw?", true);
@@ -1663,7 +1662,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot)
num_names++;
}
- // Form the normal swap file name pattern by appending ".sw?".
+ // Form the normal swapfile name pattern by appending ".sw?".
names[num_names] = concat_fnames(path, ".sw?", false);
if (num_names >= 1) { // check if we have the same name twice
char *p = names[num_names - 1];
@@ -1724,7 +1723,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
/// sync one buffer, including negative blocks
///
-/// after this all the blocks are in the swap file
+/// after this all the blocks are in the swapfile
///
/// Used for the :preserve command and when the original file has been
/// changed or deleted.
@@ -3132,7 +3131,7 @@ int resolve_symlink(const char *fname, char *buf)
}
#endif
-/// Make swap file name out of the file name and a directory name.
+/// Make swapfile name out of the file name and a directory name.
///
/// @return pointer to allocated memory or NULL.
char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
@@ -3141,7 +3140,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
#ifdef HAVE_READLINK
char fname_buf[MAXPATHL];
- // Expand symlink in the file name, so that we put the swap file with the
+ // Expand symlink in the file name, so that we put the swapfile with the
// actual file instead of with the symlink.
if (resolve_symlink(fname, fname_buf) == OK) {
fname_res = fname_buf;
@@ -3162,7 +3161,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
return r;
}
- // Prepend a '.' to the swap file name for the current directory.
+ // Prepend a '.' to the swapfile name for the current directory.
char *r = modname(fname_res, ".swp",
dir_name[0] == '.' && dir_name[1] == NUL);
if (r == NULL) { // out of memory
@@ -3174,14 +3173,11 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
return s;
}
-/// Get file name to use for swap file or backup file.
-/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
-/// option "dname".
-/// - If "dname" is ".", return "fname" (swap file in dir of file).
-/// - If "dname" starts with "./", insert "dname" in "fname" (swap file
-/// relative to dir of file).
-/// - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
-/// dir).
+/// Get file name to use for swapfile or backup file.
+/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' option "dname".
+/// - If "dname" is ".", return "fname" (swapfile in dir of file).
+/// - If "dname" starts with "./", insert "dname" in "fname" (swapfile relative to dir of file).
+/// - Otherwise, prepend "dname" to the tail of "fname" (swapfile in specific dir).
///
/// The return value is an allocated string and can be NULL.
///
@@ -3212,10 +3208,10 @@ char *get_file_in_dir(char *fname, char *dname)
return retval;
}
-/// Print the ATTENTION message: info about an existing swap file.
+/// Print the ATTENTION message: info about an existing swapfile.
///
/// @param buf buffer being edited
-/// @param fname swap file name
+/// @param fname swapfile name
static void attention_message(buf_T *buf, char *fname)
{
assert(buf->b_fname != NULL);
@@ -3299,7 +3295,7 @@ static int do_swapexists(buf_T *buf, char *fname)
return 0;
}
-/// Find out what name to use for the swap file for buffer 'buf'.
+/// Find out what name to use for the swapfile for buffer 'buf'.
///
/// Several names are tried to find one that does not exist. Last directory in
/// option is automatically created.
@@ -3308,20 +3304,20 @@ static int do_swapexists(buf_T *buf, char *fname)
/// not being able to open the swap or undo file.
/// @note May trigger SwapExists autocmd, pointers may change!
///
-/// @param[in] buf Buffer for which swap file names needs to be found.
+/// @param[in] buf Buffer for which swapfile names needs to be found.
/// @param[in,out] dirp Pointer to a list of directories. When out of memory,
/// is set to NULL. Is advanced to the next directory in
/// the list otherwise.
-/// @param[in] old_fname Allowed existing swap file name. Except for this
+/// @param[in] old_fname Allowed existing swapfile name. Except for this
/// case, name of the non-existing file is used.
/// @param[in,out] found_existing_dir If points to true, then new directory
-/// for swap file is not created. At first
+/// for swapfile is not created. At first
/// findswapname() call this argument must
/// point to false. This parameter may only
/// be set to true by this function, it is
/// never set to false.
///
-/// @return [allocated] Name of the swap file.
+/// @return [allocated] Name of the swapfile.
static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
@@ -3333,7 +3329,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
char *dir_name = xmalloc(dir_len);
(void)copy_option_part(dirp, dir_name, dir_len, ",");
- // we try different names until we find one that does not exist yet
+ // We try different swapfile names until we find one that does not exist yet.
char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
while (true) {
@@ -3346,7 +3342,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
break;
}
// check if the swapfile already exists
- // Extra security check: When a swap file is a symbolic link, this
+ // Extra security check: When a swapfile is a symbolic link, this
// is most likely a symlink attack.
FileInfo file_info;
bool file_or_link_found = os_fileinfo_link(fname, &file_info);
@@ -3365,17 +3361,17 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// Give an error message, unless recovering, no file name, we are
// viewing a help file or when the path of the file is different
// (happens when all .swp files are in one directory).
- if (!recoverymode && buf_fname != NULL
- && !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
+ if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
int fd;
ZeroBlock b0;
int differ = false;
- // Try to read block 0 from the swap file to get the original
- // file name (and inode number).
+ // Try to read block 0 from the swapfile to get the original file name (and inode number).
fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ process_running = swapfile_process_running(&b0, fname);
+
// If the swapfile has the same directory as the
// buffer don't compare the directory names, they can
// have a different mountpoint.
@@ -3393,8 +3389,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
}
} else {
- // The name in the swap file may be
- // "~user/path/file". Expand it first.
+ // The name in the swapfile may be "~user/path/file". Expand it first.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
@@ -3405,16 +3400,16 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
close(fd);
}
- // give the ATTENTION message when there is an old swap file
- // for the current file, and the buffer was not recovered.
+ // Show the ATTENTION message when:
+ // - there is an old swapfile for the current file
+ // - the buffer was not recovered
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
- process_still_running = false;
- // It's safe to delete the swap file if all these are true:
+ // It's safe to delete the swapfile if all these are true:
// - the edited file exists
- // - the swap file has no changes and looks OK
+ // - the swapfile has no changes and looks OK
if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
choice = 4;
if (p_verbose > 0) {
@@ -3430,8 +3425,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
choice = do_swapexists(buf, fname);
}
+ process_running = 0; // Set by attention_message..swapfile_info.
if (choice == 0) {
- // Show info about the existing swap file.
+ // Show info about the existing swapfile.
attention_message(buf, fname);
// We don't want a 'q' typed at the more-prompt
@@ -3459,15 +3455,15 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"),
name,
- process_still_running
+ process_running
? _("&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Quit\n&Abort") :
_("&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
- if (process_still_running && choice >= 4) {
- choice++; // Skip missing "Delete it" button.
+ if (process_running && choice >= 4) {
+ choice++; // Skip missing "Delete it" button.
}
xfree(name);
@@ -3477,27 +3473,27 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (choice > 0) {
switch (choice) {
- case 1:
+ case 1: // "Open Read-Only"
buf->b_p_ro = true;
break;
- case 2:
+ case 2: // "Edit anyway"
break;
- case 3:
+ case 3: // "Recover"
swap_exists_action = SEA_RECOVER;
break;
- case 4:
+ case 4: // "Delete it"
os_remove(fname);
break;
- case 5:
+ case 5: // "Quit"
swap_exists_action = SEA_QUIT;
break;
- case 6:
+ case 6: // "Abort"
swap_exists_action = SEA_QUIT;
got_int = true;
break;
}
- // If the file was deleted this fname can be used.
+ // If the swapfile was deleted this `fname` can be used.
if (!os_path_exists(fname)) {
break;
}
@@ -3512,10 +3508,10 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
}
- // Change the ".swp" extension to find another file that can be used.
+ // Permute the ".swp" extension to find a unique swapfile name.
// First decrement the last char: ".swo", ".swn", etc.
// If that still isn't enough decrement the last but one char: ".svz"
- // Can happen when editing many "No Name" buffers.
+ // Can happen when many Nvim instances are editing the same file (including "No Name" buffers).
if (fname[n - 1] == 'a') { // ".s?a"
if (fname[n - 2] == 'a') { // ".saa": tried enough, give up
emsg(_("E326: Too many swap files found"));
@@ -3553,7 +3549,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
}
-/// Compare current file name with file name from swap file.
+/// Compare current file name with file name from swapfile.
/// Try to use inode numbers when possible.
/// Return non-zero when files are different.
///
@@ -3563,7 +3559,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
/// because the device number cannot be used over a network.
/// - When a file does not exist yet (editing a new file) there is no inode
/// number.
-/// - The file name in a swap file may not be valid on the current host. The
+/// - The file name in a swapfile may not be valid on the current host. The
/// "~user" form is used whenever possible to avoid this.
///
/// This is getting complicated, let's make a table:
@@ -3577,7 +3573,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
/// == 0 X OK OK fname_c != fname_s
/// X == 0 OK OK fname_c != fname_s
///
-/// current file doesn't exist, file for swap file exist, file name(s) not
+/// current file doesn't exist, file for swapfile exist, file name(s) not
/// available -> probably different
/// == 0 != 0 FAIL X true
/// == 0 != 0 X FAIL true
@@ -3600,11 +3596,11 @@ static int b0_magic_wrong(ZeroBlock *b0p)
/// without making the block 0 incompatible with 32 bit versions.
///
/// @param fname_c current file name
-/// @param fname_s file name from swap file
+/// @param fname_s file name from swapfile
static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
{
uint64_t ino_c = 0; // ino of current file
- uint64_t ino_s; // ino of file from swap file
+ uint64_t ino_s; // ino of file from swapfile
char buf_c[MAXPATHL]; // full path of fname_c
char buf_s[MAXPATHL]; // full path of fname_s
int retval_c; // flag: buf_c valid
@@ -3616,7 +3612,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
}
// First we try to get the inode from the file name, because the inode in
- // the swap file may be outdated. If that fails (e.g. this path is not
+ // the swapfile may be outdated. If that fails (e.g. this path is not
// valid on this machine), use the inode from block 0.
if (os_fileinfo(fname_s, &file_info)) {
ino_s = os_fileinfo_inode(&file_info);
@@ -3638,7 +3634,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
// Can't compare inodes or file names, guess that the files are different,
// unless both appear not to exist at all, then compare with the file name
- // in the swap file.
+ // in the swapfile.
if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) {
return strcmp(fname_c, fname_s) != 0;
}
@@ -3675,7 +3671,7 @@ static long char_to_long(const char *s_in)
return retval;
}
-/// Set the flags in the first block of the swap file:
+/// Set the flags in the first block of the swapfile:
/// - file is modified or not: buf->b_changed
/// - 'fileformat'
/// - 'fileencoding'
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 2204a0a291..4a72464527 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -12,6 +12,7 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -365,7 +366,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
menu->en_name = NULL;
menu->en_dname = NULL;
}
- menu->priority = pri_tab[pri_idx];
+ menu->priority = (int)pri_tab[pri_idx];
menu->parent = parent;
// Add after menu that has lower priority.
@@ -658,7 +659,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
dict_T *dict = tv_dict_alloc();
tv_dict_add_str(dict, S_LEN("name"), menu->dname);
- tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority);
+ tv_dict_add_nr(dict, S_LEN("priority"), menu->priority);
tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname));
if (menu->mnemonic) {
@@ -1846,7 +1847,7 @@ static void menuitem_getinfo(const char *menu_name, const vimmenu_T *menu, int m
if (menu->actext != NULL) {
tv_dict_add_str(dict, S_LEN("accel"), menu->actext);
}
- tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority);
+ tv_dict_add_nr(dict, S_LEN("priority"), menu->priority);
tv_dict_add_str(dict, S_LEN("modes"), get_menu_mode_str(menu->modes));
char buf[NUMBUFLEN];
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index 32959cf35f..8d7a98d270 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/menu_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/menu_defs.h b/src/nvim/menu_defs.h
index 79b267ae49..1e010c07ba 100644
--- a/src/nvim/menu_defs.h
+++ b/src/nvim/menu_defs.h
@@ -52,7 +52,7 @@ struct VimMenu {
char *en_dname; ///< NULL when "dname" untranslated
int mnemonic; ///< mnemonic key (after '&')
char *actext; ///< accelerator text (after TAB)
- long priority; ///< Menu order priority
+ int priority; ///< Menu order priority
char *strings[MENU_MODES]; ///< Mapped string for each mode
int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
bool silent[MENU_MODES]; ///< A silent flag for each mode
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 68ea49d53b..97402276b2 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -475,7 +475,14 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen)
}
}
-// Note: Caller of smsg() must check the resulting string is shorter than IOSIZE!!!
+/// Shows a printf-style message with attributes.
+///
+/// Note: Caller must check the resulting string is shorter than IOSIZE!!!
+///
+/// @see semsg
+/// @see swmsg
+///
+/// @param s printf-style format message
int smsg(int attr, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
@@ -757,6 +764,8 @@ void emsg_invreg(int name)
}
/// Print an error message with unknown number of arguments
+///
+/// @return whether the message was displayed
bool semsg(const char *const fmt, ...)
FUNC_ATTR_PRINTF(1, 2)
{
@@ -1025,10 +1034,9 @@ int delete_first_msg(void)
}
/// :messages command implementation
-void ex_messages(void *const eap_p)
+void ex_messages(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
{
- const exarg_T *const eap = (const exarg_T *)eap_p;
struct msg_hist *p;
if (strcmp(eap->arg, "clear") == 0) {
@@ -1347,7 +1355,7 @@ bool messaging(void)
return !(p_lz && char_avail() && !KeyTyped) && (p_ch > 0 || ui_has(kUIMessages));
}
-void msgmore(long n)
+void msgmore(int n)
{
long pn;
@@ -1481,11 +1489,11 @@ void msg_putchar_attr(int c, int attr)
msg_puts_attr(buf, attr);
}
-void msg_outnum(long n)
+void msg_outnum(int n)
{
char buf[20];
- snprintf(buf, sizeof(buf), "%ld", n);
+ snprintf(buf, sizeof(buf), "%d", n);
msg_puts(buf);
}
@@ -1962,40 +1970,6 @@ void msg_prt_line(const char *s, int list)
msg_clr_eos();
}
-/// Use grid_puts() to output one multi-byte character.
-///
-/// @return the pointer "s" advanced to the next character.
-static const char *screen_puts_mbyte(const char *s, int l, int attr)
-{
- int cw;
- attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
-
- msg_didout = true; // remember that line is not empty
- cw = utf_ptr2cells(s);
- if (cw > 1
- && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) {
- // Doesn't fit, print a highlighted '>' to fill it up.
- msg_screen_putchar('>', HL_ATTR(HLF_AT));
- return s;
- }
-
- grid_puts(&msg_grid_adj, s, l, msg_row, msg_col, attr);
- if (cmdmsg_rl) {
- msg_col -= cw;
- if (msg_col == 0) {
- msg_col = Columns;
- msg_row++;
- }
- } else {
- msg_col += cw;
- if (msg_col >= Columns) {
- msg_col = 0;
- msg_row++;
- }
- }
- return s + l;
-}
-
/// Output a string to the screen at position msg_row, msg_col.
/// Update msg_row and msg_col for the next message.
void msg_puts(const char *s)
@@ -2133,14 +2107,8 @@ static void msg_ext_emit_chunk(void)
static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
{
const char *s = str;
- const char *t_s = str; // String from "t_s" to "s" is still todo.
- int t_col = 0; // Screen cells todo, 0 when "t_s" not used.
- int l;
- int cw;
const char *sb_str = str;
int sb_col = msg_col;
- int wrap;
- int did_last_char;
did_wait_return = false;
@@ -2156,175 +2124,143 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
return;
}
+ int print_attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
msg_grid_validate();
cmdline_was_last_drawn = redrawing_cmdline;
- while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
- // We are at the end of the screen line when:
- // - When outputting a newline.
- // - When outputting a character in the last column.
- if (!recurse && msg_row >= Rows - 1
- && (*s == '\n' || (cmdmsg_rl
- ? (msg_col <= 1
- || (*s == TAB && msg_col <= 7)
- || (utf_ptr2cells(s) > 1
- && msg_col <= 2))
- : ((*s != '\r' && msg_col + t_col >= Columns - 1)
- || (*s == TAB
- && msg_col + t_col >= ((Columns - 1) & ~7))
- || (utf_ptr2cells(s) > 1
- && msg_col + t_col >= Columns - 2))))) {
- // The screen is scrolled up when at the last row (some terminals
- // scroll automatically, some don't. To avoid problems we scroll
- // ourselves).
- if (t_col > 0) {
- // output postponed text
- t_puts(&t_col, t_s, s, attr);
- }
+ int msg_row_pending = -1;
- // When no more prompt and no more room, truncate here
+ while (true) {
+ if (cmdmsg_rl ? msg_col <= 0 : msg_col >= Columns) {
+ if (p_more && !recurse) {
+ // Store text for scrolling back.
+ store_sb_text(&sb_str, s, attr, &sb_col, true);
+ }
if (msg_no_more && lines_left == 0) {
break;
}
- // Scroll the screen up one line.
- bool has_last_char = ((uint8_t)(*s) >= ' ' && !cmdmsg_rl);
- msg_scroll_up(!has_last_char, false);
+ msg_col = cmdmsg_rl ? Columns - 1 : 0;
+ msg_row++;
+ msg_didout = false;
+ }
- msg_row = Rows - 2;
- if (msg_col >= Columns) { // can happen after screen resize
- msg_col = Columns - 1;
- }
+ if (msg_row >= Rows) {
+ msg_row = Rows - 1;
- // Display char in last column before showing more-prompt.
- if (has_last_char) {
- if (maxlen >= 0) {
- // Avoid including composing chars after the end.
- l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
- } else {
- l = utfc_ptr2len(s);
- }
- s = screen_puts_mbyte(s, l, attr);
- did_last_char = true;
- } else {
- did_last_char = false;
+ // When no more prompt and no more room, truncate here
+ if (msg_no_more && lines_left == 0) {
+ break;
}
- // Tricky: if last cell will be written, delay the throttle until
- // after the first scroll. Otherwise we would need to keep track of it.
- if (has_last_char && msg_do_throttle()) {
- if (!msg_grid.throttled) {
- msg_grid_scroll_discount++;
+ if (!recurse) {
+ if (msg_row_pending >= 0) {
+ grid_line_flush_if_valid_row();
+ msg_row_pending = -1;
}
- msg_grid.throttled = true;
- }
- if (p_more) {
- // Store text for scrolling back.
- store_sb_text(&sb_str, s, attr, &sb_col, true);
- }
-
- inc_msg_scrolled();
- need_wait_return = true; // may need wait_return() in main()
- redraw_cmdline = true;
- if (cmdline_row > 0 && !exmode_active) {
- cmdline_row--;
- }
+ // Scroll the screen up one line.
+ msg_scroll_up(true, false);
- // If screen is completely filled and 'more' is set then wait
- // for a character.
- if (lines_left > 0) {
- lines_left--;
- }
- if (p_more && lines_left == 0 && State != MODE_HITRETURN
- && !msg_no_more && !exmode_active) {
- if (do_more_prompt(NUL)) {
- s = confirm_msg_tail;
+ inc_msg_scrolled();
+ need_wait_return = true; // may need wait_return() in main()
+ redraw_cmdline = true;
+ if (cmdline_row > 0 && !exmode_active) {
+ cmdline_row--;
}
- if (quit_more) {
- return;
+
+ // If screen is completely filled and 'more' is set then wait
+ // for a character.
+ if (lines_left > 0) {
+ lines_left--;
}
- }
- // When we displayed a char in last column need to check if there
- // is still more.
- if (did_last_char) {
- continue;
+ if (p_more && lines_left == 0 && State != MODE_HITRETURN
+ && !msg_no_more && !exmode_active) {
+ if (do_more_prompt(NUL)) {
+ s = confirm_msg_tail;
+ }
+ if (quit_more) {
+ return;
+ }
+ }
}
}
- wrap = *s == '\n'
- || msg_col + t_col >= Columns
- || (utf_ptr2cells(s) > 1
- && msg_col + t_col >= Columns - 1)
- ;
- if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
- || *s == '\t' || *s == BELL)) {
- // Output any postponed text.
- t_puts(&t_col, t_s, s, attr);
+ if (!((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)) {
+ break;
}
- if (wrap && p_more && !recurse) {
- // Store text for scrolling back.
- store_sb_text(&sb_str, s, attr, &sb_col, true);
+ if (msg_row != msg_row_pending && ((uint8_t)(*s) >= 0x20 || *s == TAB)) {
+ // TODO(bfredl): this logic is messier that it has to be. What
+ // messages really want is its own private linebuf_char buffer.
+ if (msg_row_pending >= 0) {
+ grid_line_flush_if_valid_row();
+ }
+ grid_line_start(&msg_grid_adj, msg_row);
+ msg_row_pending = msg_row;
}
- if (*s == '\n') { // go to next line
- msg_didout = false; // remember that line is empty
- if (cmdmsg_rl) {
- msg_col = Columns - 1;
+ if ((uint8_t)(*s) >= 0x20) { // printable char
+ int cw = utf_ptr2cells(s);
+ // avoid including composing chars after the end
+ int l = (maxlen >= 0) ? utfc_ptr2len_len(s, (int)((str + maxlen) - s)) : utfc_ptr2len(s);
+
+ if (cw > 1 && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) {
+ // Doesn't fit, print a highlighted '>' to fill it up.
+ grid_line_puts(msg_col, ">", 1, HL_ATTR(HLF_AT));
+ cw = 1;
} else {
- msg_col = 0;
- }
- if (++msg_row >= Rows) { // safety check
- msg_row = Rows - 1;
+ grid_line_puts(msg_col, s, l, print_attr);
+ s += l;
}
- } else if (*s == '\r') { // go to column 0
- msg_col = 0;
- } else if (*s == '\b') { // go to previous char
- if (msg_col) {
- msg_col--;
- }
- } else if (*s == TAB) { // translate Tab into spaces
- do {
- msg_screen_putchar(' ', attr);
- } while (msg_col & 7);
- } else if (*s == BELL) { // beep (from ":sh")
- vim_beep(BO_SH);
- } else if ((uint8_t)(*s) >= 0x20) { // printable char
- cw = utf_ptr2cells(s);
- if (maxlen >= 0) {
- // avoid including composing chars after the end
- l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
+ msg_didout = true; // remember that line is not empty
+ if (cmdmsg_rl) {
+ msg_col -= cw;
} else {
- l = utfc_ptr2len(s);
+ msg_col += cw;
}
- // When drawing from right to left or when a double-wide character
- // doesn't fit, draw a single character here. Otherwise collect
- // characters and draw them all at once later.
- if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) {
- if (l > 1) {
- s = screen_puts_mbyte(s, l, attr) - 1;
+ } else {
+ char c = *s++;
+ if (c == '\n') { // go to next line
+ msg_didout = false; // remember that line is empty
+ if (cmdmsg_rl) {
+ msg_col = Columns - 1;
} else {
- msg_screen_putchar(*s, attr);
+ msg_col = 0;
}
- } else {
- // postpone this character until later
- if (t_col == 0) {
- t_s = s;
+ msg_row++;
+ if (p_more && !recurse) {
+ // Store text for scrolling back.
+ store_sb_text(&sb_str, s, attr, &sb_col, true);
+ }
+ } else if (c == '\r') { // go to column 0
+ msg_col = 0;
+ } else if (c == '\b') { // go to previous char
+ if (msg_col) {
+ msg_col--;
}
- t_col += cw;
- s += l - 1;
+ } else if (c == TAB) { // translate Tab into spaces
+ do {
+ grid_line_puts(msg_col, " ", 1, print_attr);
+ msg_col += cmdmsg_rl ? -1 : 1;
+
+ if (msg_col == (cmdmsg_rl ? 0 : Columns)) {
+ break;
+ }
+ } while (msg_col & 7);
+ } else if (c == BELL) { // beep (from ":sh")
+ vim_beep(BO_SH);
}
}
- s++;
}
- // Output any postponed text.
- if (t_col > 0) {
- t_puts(&t_col, t_s, s, attr);
+ if (msg_row_pending >= 0) {
+ grid_line_flush_if_valid_row();
}
+ msg_cursor_goto(msg_row, msg_col);
+
if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n')) {
store_sb_text(&sb_str, s, attr, &sb_col, false);
}
@@ -2332,6 +2268,13 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
msg_check();
}
+void msg_cursor_goto(int row, int col)
+{
+ ScreenGrid *grid = &msg_grid_adj;
+ grid_adjust(&grid, &row, &col);
+ ui_grid_cursor_goto(grid->handle, row, col);
+}
+
/// @return true when ":filter pattern" was used and "msg" does not match
/// "pattern".
bool message_filtered(const char *msg)
@@ -2508,6 +2451,9 @@ static void store_sb_text(const char **sb_str, const char *s, int attr, int *sb_
|| do_clear_sb_text == SB_CLEAR_CMDLINE_DONE) {
clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL);
msg_sb_eol(); // prevent messages from overlapping
+ if (do_clear_sb_text == SB_CLEAR_CMDLINE_DONE && s > *sb_str && **sb_str == '\n') {
+ (*sb_str)++;
+ }
do_clear_sb_text = SB_CLEAR_NONE;
}
@@ -2655,9 +2601,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
msg_row = row;
msg_col = mp->sb_msg_col;
char *p = mp->sb_text;
- if (*p == '\n') { // don't display the line break
- p++;
- }
msg_puts_display(p, -1, mp->sb_attr, true);
if (mp->sb_eol || mp->sb_next == NULL) {
break;
@@ -2668,26 +2611,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
return mp->sb_next;
}
-/// Output any postponed text for msg_puts_len().
-static void t_puts(int *t_col, const char *t_s, const char *s, int attr)
-{
- attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
- // Output postponed text.
- msg_didout = true; // Remember that line is not empty.
- grid_puts(&msg_grid_adj, t_s, (int)(s - t_s), msg_row, msg_col, attr);
- msg_col += *t_col;
- *t_col = 0;
- // If the string starts with a composing character don't increment the
- // column position for it.
- if (utf_iscomposing(utf_ptr2char(t_s))) {
- msg_col--;
- }
- if (msg_col >= Columns) {
- msg_col = 0;
- msg_row++;
- }
-}
-
/// @return true when messages should be printed to stdout/stderr:
/// - "batch mode" ("silent mode", -es/-Es)
/// - no UI and not embedded
@@ -3034,26 +2957,6 @@ void os_msg(const char *str)
}
#endif // MSWIN
-/// Put a character on the screen at the current message position and advance
-/// to the next position. Only for printable ASCII!
-static void msg_screen_putchar(int c, int attr)
-{
- attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
- msg_didout = true; // remember that line is not empty
- grid_putchar(&msg_grid_adj, c, msg_row, msg_col, attr);
- if (cmdmsg_rl) {
- if (--msg_col == 0) {
- msg_col = Columns;
- msg_row++;
- }
- } else {
- if (++msg_col >= Columns) {
- msg_col = 0;
- msg_row++;
- }
- }
-}
-
void msg_moremsg(int full)
{
int attr;
@@ -3443,9 +3346,22 @@ void give_warning(const char *message, bool hl)
no_wait_return--;
}
-void give_warning2(const char *const message, const char *const a1, bool hl)
+/// Shows a warning, with optional highlighting.
+///
+/// @param hl enable highlighting
+/// @param fmt printf-style format message
+///
+/// @see smsg
+/// @see semsg
+void swmsg(bool hl, const char *const fmt, ...)
+ FUNC_ATTR_PRINTF(2, 3)
{
- vim_snprintf(IObuff, IOSIZE, message, a1);
+ va_list args;
+
+ va_start(args, fmt);
+ vim_vsnprintf(IObuff, IOSIZE, fmt, args);
+ va_end(args);
+
give_warning(IObuff, hl);
}
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 78cd5b7d0e..6fc6674cc2 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -7,6 +7,7 @@
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/grid_defs.h"
#include "nvim/macros.h"
#include "nvim/types.h"
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 8efb038a55..dd9737ede2 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -2699,7 +2699,7 @@ void do_check_cursorbind(void)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
curwin = wp;
curbuf = curwin->w_buffer;
- // skip original window and windows with 'noscrollbind'
+ // skip original window and windows with 'nocursorbind'
if (curwin != old_curwin && curwin->w_p_crb) {
if (curwin->w_p_diff) {
curwin->w_cursor.lnum =
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 715b98377a..1a212661c5 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1735,13 +1735,13 @@ static void prep_redo_cmd(cmdarg_T *cap)
/// Prepare for redo of any command.
/// Note that only the last argument can be a multi-byte char.
-void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
+void prep_redo(int regname, int num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
{
prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
}
/// Prepare for redo of any command with extra count after "cmd2".
-void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4,
+void prep_redo_num2(int regname, int num1, int cmd1, int cmd2, int num2, int cmd3, int cmd4,
int cmd5)
{
ResetRedobuff();
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index b9fdd21652..3d7782c05c 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -39,7 +39,7 @@ typedef struct oparg_S {
pos_T end; // end of the operator
pos_T cursor_start; // cursor position before motion for "gw"
- long line_count; // number of lines from op_start to op_end
+ linenr_T line_count; // number of lines from op_start to op_end
// (inclusive)
bool empty; // op_start and op_end the same (only used by
// op_change())
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 5d3e285e3b..348a86a0f6 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -217,7 +217,7 @@ int get_extra_op_char(int optype)
/// handle a shift operation
void op_shift(oparg_T *oap, int curs_top, int amount)
{
- long i;
+ int i;
int block_col = 0;
if (u_save((linenr_T)(oap->start.lnum - 1),
@@ -292,7 +292,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
/// @param call_changed_bytes call changed_bytes()
void shift_line(int left, int round, int amount, int call_changed_bytes)
{
- const int sw_val = (int)get_sw_value_indent(curbuf);
+ const int sw_val = get_sw_value_indent(curbuf);
int count = get_indent(); // get current indent
@@ -338,7 +338,7 @@ static void shift_block(oparg_T *oap, int amount)
const int oldstate = State;
char *newp;
const int oldcol = curwin->w_cursor.col;
- const int sw_val = (int)get_sw_value_indent(curbuf);
+ const int sw_val = get_sw_value_indent(curbuf);
const int ts_val = (int)curbuf->b_p_ts;
struct block_def bd;
int incr;
@@ -634,7 +634,7 @@ static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def *
/// Handle reindenting a block of lines.
void op_reindent(oparg_T *oap, Indenter how)
{
- long i = 0;
+ int i = 0;
linenr_T first_changed = 0;
linenr_T last_changed = 0;
linenr_T start_lnum = curwin->w_cursor.lnum;
@@ -647,8 +647,8 @@ void op_reindent(oparg_T *oap, Indenter how)
// Save for undo. Do this once for all lines, much faster than doing this
// for each line separately, especially when undoing.
- if (u_savecommon(curbuf, start_lnum - 1, start_lnum + (linenr_T)oap->line_count,
- start_lnum + (linenr_T)oap->line_count, false) == OK) {
+ if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count,
+ start_lnum + oap->line_count, false) == OK) {
char *l;
int amount;
for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
@@ -693,7 +693,7 @@ void op_reindent(oparg_T *oap, Indenter how)
// there is no change still need to remove the Visual highlighting.
if (last_changed != 0) {
changed_lines(curbuf, first_changed, 0,
- oap->is_VIsual ? start_lnum + (linenr_T)oap->line_count :
+ oap->is_VIsual ? start_lnum + oap->line_count :
last_changed + 1, 0L, true);
} else if (oap->is_VIsual) {
redraw_curbuf_later(UPD_INVERTED);
@@ -1717,8 +1717,8 @@ int op_delete(oparg_T *oap)
pos_T curpos;
// save deleted and changed lines for undo
- if (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
- (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) {
+ if (u_save(curwin->w_cursor.lnum - 1,
+ curwin->w_cursor.lnum + oap->line_count) == FAIL) {
return FAIL;
}
@@ -2204,7 +2204,7 @@ bool swapchar(int op_type, pos_T *pos)
/// Insert and append operators for Visual mode.
void op_insert(oparg_T *oap, long count1)
{
- long pre_textlen = 0;
+ int pre_textlen = 0;
char *firstline;
colnr_T ind_pre_col = 0;
int ind_pre_vcol = 0;
@@ -2251,7 +2251,7 @@ void op_insert(oparg_T *oap, long count1)
if (oap->op_type == OP_APPEND) {
firstline += bd.textlen;
}
- pre_textlen = (long)strlen(firstline);
+ pre_textlen = (int)strlen(firstline);
}
if (oap->op_type == OP_APPEND) {
@@ -2400,7 +2400,7 @@ void op_insert(oparg_T *oap, long count1)
} else {
firstline += add;
}
- long ins_len = (long)strlen(firstline) - pre_textlen - offset;
+ int ins_len = (int)strlen(firstline) - pre_textlen - offset;
if (pre_textlen >= 0 && ins_len > 0) {
char *ins_text = xstrnsave(firstline, (size_t)ins_len);
// block handled here
@@ -2421,8 +2421,8 @@ void op_insert(oparg_T *oap, long count1)
int op_change(oparg_T *oap)
{
int retval;
- long pre_textlen = 0;
- long pre_indent = 0;
+ int pre_textlen = 0;
+ int pre_indent = 0;
char *firstline;
struct block_def bd;
@@ -2456,8 +2456,8 @@ int op_change(oparg_T *oap)
coladvance_force(getviscol());
}
firstline = ml_get(oap->start.lnum);
- pre_textlen = (long)strlen(firstline);
- pre_indent = (long)getwhitecols(firstline);
+ pre_textlen = (int)strlen(firstline);
+ pre_indent = (int)getwhitecols(firstline);
bd.textcol = curwin->w_cursor.col;
}
@@ -2478,25 +2478,25 @@ int op_change(oparg_T *oap)
// Don't repeat the insert when Insert mode ended with CTRL-C.
if (oap->motion_type == kMTBlockWise
&& oap->start.lnum != oap->end.lnum && !got_int) {
- long ins_len;
+ int ins_len;
// Auto-indenting may have changed the indent. If the cursor was past
// the indent, exclude that indent change from the inserted text.
firstline = ml_get(oap->start.lnum);
if (bd.textcol > (colnr_T)pre_indent) {
- long new_indent = (long)getwhitecols(firstline);
+ int new_indent = (int)getwhitecols(firstline);
pre_textlen += new_indent - pre_indent;
bd.textcol += (colnr_T)(new_indent - pre_indent);
}
- ins_len = (long)strlen(firstline) - pre_textlen;
+ ins_len = (int)strlen(firstline) - pre_textlen;
if (ins_len > 0) {
long offset;
char *newp;
char *oldp;
// Subsequent calls to ml_get() flush the firstline data - take a
// copy of the inserted text.
- char *ins_text = xmalloc((size_t)(ins_len + 1));
+ char *ins_text = xmalloc((size_t)ins_len + 1);
xstrlcpy(ins_text, firstline + bd.textcol, (size_t)ins_len + 1);
for (linenr_T linenr = oap->start.lnum + 1; linenr <= oap->end.lnum;
linenr++) {
@@ -2526,7 +2526,7 @@ int op_change(oparg_T *oap)
STRMOVE(newp + offset, oldp);
ml_replace(linenr, newp, false);
extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol,
- 0, vpos.coladd + (int)ins_len, kExtmarkUndo);
+ 0, vpos.coladd + ins_len, kExtmarkUndo);
}
}
check_cursor();
@@ -2903,7 +2903,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
/// PUT_LINE force linewise put (":put")
/// PUT_BLOCK_INNER in block mode, do not add trailing spaces
/// @param dir BACKWARD for 'P', FORWARD for 'p'
-void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
+void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
{
char *ptr;
char *newp;
@@ -3320,7 +3320,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
break;
}
- totlen = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces);
+ totlen = (size_t)count * (size_t)(yanklen + spaces) + (size_t)bd.startspaces +
+ (size_t)bd.endspaces;
newp = xmalloc(totlen + oldlen + 1);
// copy part up to cursor to new line
@@ -3446,7 +3447,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// multiplication overflow
emsg(_(e_resulting_text_too_long));
} else {
- totlen = (size_t)(count * yanklen);
+ totlen = (size_t)count * (size_t)yanklen;
do {
oldp = ml_get(lnum);
oldlen = strlen(oldp);
@@ -4166,7 +4167,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
// have moved up (last line deleted), so the current lnum is kept in t.
t = curwin->w_cursor.lnum;
curwin->w_cursor.lnum++;
- del_lines((long)count - 1, false);
+ del_lines((int)count - 1, false);
curwin->w_cursor.lnum = t;
curbuf_splice_pending--;
curbuf->deleted_bytes2 = 0;
@@ -5569,7 +5570,7 @@ static void op_colon(oparg_T *oap)
if (oap->start.lnum == curwin->w_cursor.lnum) {
stuffcharReadbuff('.');
} else {
- stuffnumReadbuff((long)oap->start.lnum);
+ stuffnumReadbuff(oap->start.lnum);
}
// When using !! on a closed fold the range ".!" works best to operate
@@ -5590,7 +5591,7 @@ static void op_colon(oparg_T *oap)
stuffReadbuff(".+");
stuffnumReadbuff(oap->line_count - 1);
} else {
- stuffnumReadbuff((long)oap->end.lnum);
+ stuffnumReadbuff(oap->end.lnum);
}
}
}
@@ -5996,7 +5997,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
resel_VIsual_vcol = oap->end_vcol;
}
}
- resel_VIsual_line_count = (linenr_T)oap->line_count;
+ resel_VIsual_line_count = oap->line_count;
}
// can't redo yank (unless 'y' is in 'cpoptions') and ":"
diff --git a/src/nvim/option.c b/src/nvim/option.c
index eef5e66aeb..c0353e52be 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -40,6 +40,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
@@ -136,12 +137,12 @@ static char *p_vsts_nopaste;
#define OPTION_COUNT ARRAY_SIZE(options)
+/// :set boolean option prefix
typedef enum {
- OP_NONE = 0,
- OP_ADDING, ///< "opt+=arg"
- OP_PREPENDING, ///< "opt^=arg"
- OP_REMOVING, ///< "opt-=arg"
-} set_op_T;
+ PREFIX_NO = 0, ///< "no" prefix
+ PREFIX_NONE, ///< no prefix
+ PREFIX_INV, ///< "inv" prefix
+} set_prefix_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -557,7 +558,7 @@ static char *find_dup_item(char *origval, const char *newval, uint32_t flags)
/// Set the Vi-default value of a number option.
/// Used for 'lines' and 'columns'.
-void set_number_default(char *name, long val)
+void set_number_default(char *name, OptInt val)
{
int opt_idx = findoption(name);
if (opt_idx >= 0) {
@@ -752,8 +753,8 @@ void ex_set(exarg_T *eap)
(void)do_set(eap->arg, flags);
}
-static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const void *varp,
- const char **errmsg)
+static void do_set_bool(int opt_idx, int opt_flags, set_prefix_T prefix, int nextchar,
+ const void *varp, const char **errmsg)
{
varnumber_T value;
@@ -772,10 +773,12 @@ static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, co
value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
}
} else {
- if (prefix == 2) {
- value = *(int *)varp ^ 1; // ":set invopt": invert
+ // ":set invopt": invert
+ // ":set opt" or ":set noopt": set or reset
+ if (prefix == PREFIX_INV) {
+ value = *(int *)varp ^ 1;
} else {
- value = prefix; // ":set opt" or ":set noopt": set or reset
+ value = prefix == PREFIX_NO ? 0 : 1;
}
}
@@ -797,7 +800,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co
// other error
arg++;
if (nextchar == '&') {
- value = (long)(intptr_t)options[opt_idx].def_val;
+ value = (varnumber_T)options[opt_idx].def_val;
} else if (nextchar == '<') {
if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
// for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global value
@@ -843,7 +846,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co
if (op == OP_REMOVING) {
value = *(OptInt *)varp - value;
}
- *errmsg = set_num_option(opt_idx, (void *)varp, (long)value,
+ *errmsg = set_num_option(opt_idx, (void *)varp, value,
errbuf, errbuflen, opt_flags);
}
@@ -888,7 +891,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
// For MS-Windows backslashes before normal file name characters
// are not removed, and keep backslash at start, for "\\machine\path",
// but do remove it for "\\\\machine\\path".
- // The reverse is found in ExpandOldSetting().
+ // The reverse is found in escape_option_str_cmdline().
while (*arg != NUL && !ascii_iswhite(*arg)) {
if (*arg == '\\' && arg[1] != NUL
#ifdef BACKSLASH_IN_FILENAME
@@ -1168,7 +1171,7 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
// be triggered that can cause havoc.
*errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval,
errbuf, errbuflen,
- opt_flags, value_checked);
+ opt_flags, op, value_checked);
secure = secure_saved;
@@ -1204,17 +1207,17 @@ static set_op_T get_op(const char *arg)
return op;
}
-static int get_option_prefix(char **argp)
+static set_prefix_T get_option_prefix(char **argp)
{
if (strncmp(*argp, "no", 2) == 0) {
*argp += 2;
- return 0;
+ return PREFIX_NO;
} else if (strncmp(*argp, "inv", 3) == 0) {
*argp += 3;
- return 2;
+ return PREFIX_INV;
}
- return 1;
+ return PREFIX_NONE;
}
/// @param[in] arg Pointer to start option name
@@ -1273,11 +1276,11 @@ static int parse_option_name(char *arg, int *keyp, int *lenp, int *opt_idxp)
return OK;
}
-static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, int prefix,
- const char **errmsg)
+static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags,
+ set_prefix_T prefix, const char **errmsg)
{
// Only bools can have a prefix of 'inv' or 'no'
- if (!(flags & P_BOOL) && prefix != 1) {
+ if (!(flags & P_BOOL) && prefix != PREFIX_NONE) {
*errmsg = e_invarg;
return FAIL;
}
@@ -1325,8 +1328,8 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla
return OK;
}
-static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int prefix, int nextchar,
- set_op_T op, uint32_t flags, void *varp, char *errbuf,
+static void do_set_option_value(int opt_idx, int opt_flags, char **argp, set_prefix_T prefix,
+ int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
size_t errbuflen, const char **errmsg)
{
bool value_checked = false;
@@ -1355,7 +1358,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb
size_t errbuflen, const char **errmsg)
{
// 1: nothing, 0: "no", 2: "inv" in front of name
- int prefix = get_option_prefix(argp);
+ set_prefix_T prefix = get_option_prefix(argp);
char *arg = *argp;
@@ -1434,7 +1437,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb
// '=' character per "set" command line. grrr. (jw)
//
if (nextchar == '?'
- || (prefix == 1
+ || (prefix == PREFIX_NONE
&& vim_strchr("=:&<", nextchar) == NULL
&& !(flags & P_BOOL))) {
// print value
@@ -1976,8 +1979,8 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
}
/// Apply the OptionSet autocommand.
-static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval, OptInt oldval_g,
- long newval, const char *errmsg)
+static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptInt oldval, OptInt oldval_g,
+ OptInt newval, const char *errmsg)
{
// Don't do this while starting up, failure or recursively.
if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
@@ -1988,7 +1991,7 @@ static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval,
vim_snprintf(buf_old, sizeof(buf_old), "%" PRId64, oldval);
vim_snprintf(buf_old_global, sizeof(buf_old_global), "%" PRId64, oldval_g);
- vim_snprintf(buf_new, sizeof(buf_new), "%ld", newval);
+ vim_snprintf(buf_new, sizeof(buf_new), "%" PRId64, newval);
vim_snprintf(buf_type, sizeof(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
@@ -2899,9 +2902,9 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in
options[opt_idx].flags |= P_WAS_SET;
apply_optionset_autocmd(opt_idx, opt_flags,
- (long)(old_value ? true : false),
- (long)(old_global_value ? true : false),
- (long)(value ? true : false), NULL);
+ (old_value ? true : false),
+ (old_global_value ? true : false),
+ (value ? true : false), NULL);
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -2921,8 +2924,8 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in
}
/// Check the bounds of numeric options.
-static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long old_Rows,
- char *errbuf, size_t errbuflen, const char *errmsg)
+static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, int old_Rows, char *errbuf,
+ size_t errbuflen, const char *errmsg)
{
// Check the (new) bounds for Rows and Columns here.
if (p_lines < min_rows() && full_screen) {
@@ -2997,9 +3000,9 @@ static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long ol
}
/// Options that need some validation.
-static const char *validate_num_option(const OptInt *pp, long *valuep)
+static const char *validate_num_option(const OptInt *pp, OptInt *valuep)
{
- long value = *valuep;
+ OptInt value = *valuep;
// Many number options assume their value is in the signed int range.
if (value < INT_MIN || value > INT_MAX) {
@@ -3157,12 +3160,12 @@ static const char *validate_num_option(const OptInt *pp, long *valuep)
/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
///
/// @return NULL on success, error message on error.
-static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
+static const char *set_num_option(int opt_idx, void *varp, OptInt value, char *errbuf,
size_t errbuflen, int opt_flags)
{
OptInt old_value = *(OptInt *)varp;
OptInt old_global_value = 0; // only used when setting a local and global option
- long old_Rows = Rows; // remember old Rows
+ int old_Rows = Rows; // remember old Rows
OptInt *pp = (OptInt *)varp;
// Disallow changing some options from secure mode.
@@ -3184,7 +3187,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
return errmsg;
}
- *pp = (OptInt)value;
+ *pp = value;
// Remember where the option was set.
set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
@@ -3195,7 +3198,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
.os_varp = varp,
.os_flags = opt_flags,
.os_oldval.number = old_value,
- .os_newval.number = (OptInt)value,
+ .os_newval.number = value,
.os_errbuf = NULL,
.os_errbuflen = 0,
.os_buf = curbuf,
@@ -3215,7 +3218,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
options[opt_idx].flags |= P_WAS_SET;
apply_optionset_autocmd(opt_idx, opt_flags, old_value, old_global_value,
- value, errmsg);
+ (int)value, errmsg);
if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -3786,7 +3789,7 @@ static const char *set_option(int opt_idx, void *varp, OptVal *v, int opt_flags,
if (v->type == kOptValTypeBoolean) {
errmsg = set_bool_option(opt_idx, varp, (int)v->data.boolean, opt_flags);
} else if (v->type == kOptValTypeNumber) {
- errmsg = set_num_option(opt_idx, varp, (long)v->data.number, errbuf, errbuflen, opt_flags);
+ errmsg = set_num_option(opt_idx, varp, v->data.number, errbuf, errbuflen, opt_flags);
} else if (v->type == kOptValTypeString) {
errmsg = set_string_option(opt_idx, varp, v->data.string.data, opt_flags, &value_checked,
errbuf, errbuflen);
@@ -5305,8 +5308,10 @@ void set_imsearch_global(buf_T *buf)
}
static int expand_option_idx = -1;
+static int expand_option_start_col = 0;
static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
static int expand_option_flags = 0;
+static bool expand_option_append = false;
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
@@ -5405,7 +5410,15 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
}
// handle "-=" and "+="
+ expand_option_append = false;
+ bool expand_option_subtract = false;
if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') {
+ if (nextchar == '-') {
+ expand_option_subtract = true;
+ }
+ if (nextchar == '+' || nextchar == '^') {
+ expand_option_append = true;
+ }
p++;
nextchar = '=';
}
@@ -5414,28 +5427,51 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
xp->xp_context = EXPAND_UNSUCCESSFUL;
return;
}
- if (p[1] == NUL) {
- xp->xp_context = EXPAND_OLD_SETTING;
- if (is_term_option) {
- expand_option_idx = -1;
- } else {
- expand_option_idx = opt_idx;
- }
- xp->xp_pattern = p + 1;
- return;
- }
- xp->xp_context = EXPAND_NOTHING;
- if (is_term_option || (flags & P_NUM)) {
- return;
+
+ // Below are for handling expanding a specific option's value after the '=' or ':'
+
+ if (is_term_option) {
+ expand_option_idx = -1;
+ } else {
+ expand_option_idx = opt_idx;
}
xp->xp_pattern = p + 1;
+ expand_option_start_col = (int)(p + 1 - xp->xp_line);
+ // Certain options currently have special case handling to reuse the
+ // expansion logic with other commands.
if (options[opt_idx].var == &p_syn) {
xp->xp_context = EXPAND_OWNSYNTAX;
return;
}
+ if (options[opt_idx].var == &p_ft) {
+ xp->xp_context = EXPAND_FILETYPE;
+ return;
+ }
+ // Now pick. If the option has a custom expander, use that. Otherwise, just
+ // fill with the existing option value.
+ if (expand_option_subtract) {
+ xp->xp_context = EXPAND_SETTING_SUBTRACT;
+ return;
+ } else if (expand_option_idx >= 0
+ && options[expand_option_idx].opt_expand_cb != NULL) {
+ xp->xp_context = EXPAND_STRING_SETTING;
+ } else if (*xp->xp_pattern == NUL) {
+ xp->xp_context = EXPAND_OLD_SETTING;
+ return;
+ } else {
+ xp->xp_context = EXPAND_NOTHING;
+ }
+
+ if (is_term_option || (flags & P_NUM)) {
+ return;
+ }
+
+ // Only string options below
+
+ // Options that have P_EXPAND are considered to all use file/dir expansion.
if (flags & P_EXPAND) {
p = options[opt_idx].var;
if (p == (char *)&p_bdir
@@ -5451,8 +5487,6 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
} else {
xp->xp_backslash = XP_BS_ONE;
}
- } else if (p == (char *)&p_ft) {
- xp->xp_context = EXPAND_FILETYPE;
} else {
xp->xp_context = EXPAND_FILES;
// for 'tags' need three backslashes for a space
@@ -5464,27 +5498,45 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
}
- // For an option that is a list of file names, find the start of the
- // last file name.
- for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) {
- // count number of backslashes before ' ' or ','
- if (*p == ' ' || *p == ',') {
- char *s = p;
- while (s > xp->xp_pattern && *(s - 1) == '\\') {
- s--;
- }
- if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
- || (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) {
- xp->xp_pattern = p + 1;
- break;
+ // For an option that is a list of file names, or comma/colon-separated
+ // values, split it by the delimiter and find the start of the current
+ // pattern, while accounting for backslash-escaped space/commas/colons.
+ // Triple-backslashed escaped file names (e.g. 'path') can also be
+ // delimited by space.
+ if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON)) {
+ for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) {
+ // count number of backslashes before ' ' or ','
+ if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) {
+ char *s = p;
+ while (s > xp->xp_pattern && *(s - 1) == '\\') {
+ s--;
+ }
+ if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
+ || (*p == ',' && (flags & P_COMMA) && ((p - s) % 1) == 0)
+ || (*p == ':' && (flags & P_COLON))) {
+ xp->xp_pattern = p + 1;
+ break;
+ }
}
}
+ }
- // for 'spellsuggest' start at "file:"
- if (options[opt_idx].var == &p_sps
- && strncmp(p, "file:", 5) == 0) {
- xp->xp_pattern = p + 5;
- break;
+ // An option that is a list of single-character flags should always start
+ // at the end as we don't complete words.
+ if (flags & P_FLAGLIST) {
+ xp->xp_pattern = arg + strlen(arg);
+ }
+
+ // Some options can either be using file/dir expansions, or custom value
+ // expansion depending on what the user typed. Unfortunately we have to
+ // manually handle it here to make sure we have the correct xp_context set.
+ // for 'spellsuggest' start at "file:"
+ if (options[opt_idx].var == &p_sps) {
+ if (strncmp(xp->xp_pattern, "file:", 5) == 0) {
+ xp->xp_pattern += 5;
+ return;
+ } else if (options[expand_option_idx].opt_expand_cb != NULL) {
+ xp->xp_context = EXPAND_STRING_SETTING;
}
}
}
@@ -5609,7 +5661,33 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM
return OK;
}
-void ExpandOldSetting(int *numMatches, char ***matches)
+/// Escape an option value that can be used on the command-line with :set.
+/// Caller needs to free the returned string, unless NULL is returned.
+static char *escape_option_str_cmdline(char *var)
+{
+ // A backslash is required before some characters. This is the reverse of
+ // what happens in do_set().
+ char *buf = vim_strsave_escaped(var, escape_chars);
+
+#ifdef BACKSLASH_IN_FILENAME
+ // For MS-Windows et al. we don't double backslashes at the start and
+ // before a file name character.
+ // The reverse is found at stropt_copy_value().
+ for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
+ if (var[0] == '\\' && var[1] == '\\'
+ && expand_option_idx >= 0
+ && (options[expand_option_idx].flags & P_EXPAND)
+ && vim_isfilec((uint8_t)var[2])
+ && (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
+ STRMOVE(var, var + 1);
+ }
+ }
+#endif
+ return buf;
+}
+
+/// Expansion handler for :set= when we just want to fill in with the existing value.
+int ExpandOldSetting(int *numMatches, char ***matches)
{
char *var = NULL;
@@ -5629,26 +5707,149 @@ void ExpandOldSetting(int *numMatches, char ***matches)
var = "";
}
- // A backslash is required before some characters. This is the reverse of
- // what happens in do_set().
- char *buf = vim_strsave_escaped(var, escape_chars);
+ char *buf = escape_option_str_cmdline(var);
-#ifdef BACKSLASH_IN_FILENAME
- // For MS-Windows et al. we don't double backslashes at the start and
- // before a file name character.
- for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
- if (var[0] == '\\' && var[1] == '\\'
- && expand_option_idx >= 0
- && (options[expand_option_idx].flags & P_EXPAND)
- && vim_isfilec((uint8_t)var[2])
- && (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
- STRMOVE(var, var + 1);
+ (*matches)[0] = buf;
+ *numMatches = 1;
+ return OK;
+}
+
+/// Expansion handler for :set=/:set+= when the option has a custom expansion handler.
+int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches)
+{
+ if (expand_option_idx < 0
+ || options[expand_option_idx].opt_expand_cb == NULL) {
+ // Not supposed to reach this. This function is only for options with
+ // custom expansion callbacks.
+ return FAIL;
+ }
+
+ optexpand_T args = {
+ .oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
+ .oe_append = expand_option_append,
+ .oe_regmatch = regmatch,
+ .oe_xp = xp,
+ .oe_set_arg = xp->xp_line + expand_option_start_col,
+ };
+ args.oe_include_orig_val = !expand_option_append && (*args.oe_set_arg == NUL);
+
+ // Retrieve the existing value, but escape it as a reverse of setting it.
+ // We technically only need to do this when oe_append or
+ // oe_include_orig_val is true.
+ option_value2string(&options[expand_option_idx], expand_option_flags);
+ char *var = NameBuff;
+ char *buf = escape_option_str_cmdline(var);
+ args.oe_opt_value = buf;
+
+ int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches);
+
+ xfree(buf);
+ return num_ret;
+}
+
+/// Expansion handler for :set-=
+int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches)
+{
+ if (expand_option_idx < 0) {
+ // term option
+ return ExpandOldSetting(numMatches, matches);
+ }
+
+ char *option_val = *(char **)get_option_varp_scope_from(expand_option_idx,
+ expand_option_flags,
+ curbuf, curwin);
+
+ uint32_t option_flags = options[expand_option_idx].flags;
+
+ if (option_flags & P_NUM) {
+ return ExpandOldSetting(numMatches, matches);
+ } else if (option_flags & P_COMMA) {
+ // Split the option by comma, then present each option to the user if
+ // it matches the pattern.
+ // This condition needs to go first, because 'whichwrap' has both
+ // P_COMMA and P_FLAGLIST.
+
+ if (*option_val == NUL) {
+ return FAIL;
}
+
+ // Make a copy as we need to inject null characters destructively.
+ char *option_copy = xstrdup(option_val);
+ char *next_val = option_copy;
+
+ garray_T ga;
+ ga_init(&ga, sizeof(char *), 10);
+
+ do {
+ char *item = next_val;
+ char *comma = vim_strchr(next_val, ',');
+ while (comma != NULL && comma != next_val && *(comma - 1) == '\\') {
+ // "\," is interpreted as a literal comma rather than option
+ // separator when reading options in copy_option_part(). Skip
+ // it.
+ comma = vim_strchr(comma + 1, ',');
+ }
+ if (comma != NULL) {
+ *comma = NUL; // null-terminate this value, required by later functions
+ next_val = comma + 1;
+ } else {
+ next_val = NULL;
+ }
+
+ if (*item == NUL) {
+ // empty value, don't add to list
+ continue;
+ }
+
+ if (!vim_regexec(regmatch, item, (colnr_T)0)) {
+ continue;
+ }
+
+ char *buf = escape_option_str_cmdline(item);
+ GA_APPEND(char *, &ga, buf);
+ } while (next_val != NULL);
+
+ xfree(option_copy);
+
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ return OK;
+ } else if (option_flags & P_FLAGLIST) {
+ // Only present the flags that are set on the option as the other flags
+ // are not meaningful to do set-= on.
+
+ if (*xp->xp_pattern != NUL) {
+ // Don't suggest anything if cmdline is non-empty. Vim's set-=
+ // behavior requires consecutive strings and it's usually
+ // unintuitive to users if ther try to subtract multiple flags at
+ // once.
+ return FAIL;
+ }
+
+ size_t num_flags = strlen(option_val);
+ if (num_flags == 0) {
+ return FAIL;
+ }
+
+ *matches = xmalloc(sizeof(char *) * (num_flags + 1));
+
+ int count = 0;
+
+ (*matches)[count++] = xstrdup(option_val);
+
+ if (num_flags > 1) {
+ // If more than one flags, split the flags up and expose each
+ // character as individual choice.
+ for (char *flag = option_val; *flag != NUL; flag++) {
+ (*matches)[count++] = xstrnsave(flag, 1);
+ }
+ }
+
+ *numMatches = count;
+ return OK;
}
-#endif
- *matches[0] = buf;
- *numMatches = 1;
+ return ExpandOldSetting(numMatches, matches);
}
/// Get the value for the numeric or string option///opp in a nice format into
@@ -5772,6 +5973,7 @@ int fill_culopt_flags(char *val, win_T *wp)
p = val;
}
while (*p != NUL) {
+ // Note: Keep this in sync with p_culopt_values.
if (strncmp(p, "line", 4) == 0) {
p += 4;
culopt_flags_new |= CULOPT_LINE;
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 9e3bf25bc3..593917407a 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -4,9 +4,11 @@
#include <stdint.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/option_defs.h"
+#include "nvim/search.h"
/// The options that are local to a window or buffer have "indir" set to one of
/// these values. Special values:
@@ -45,7 +47,15 @@ typedef struct vimoption {
///< local option: indirect option index
///< callback function to invoke after an option is modified to validate and
///< apply the new value.
+
+ /// callback function to invoke after an option is modified to validate and
+ /// apply the new value.
opt_did_set_cb_T opt_did_set_cb;
+
+ /// callback function to invoke when expanding possible values on the
+ /// cmdline. Only useful for string options.
+ opt_expand_cb_T opt_expand_cb;
+
void *def_val; ///< default values for variable (neovim!!)
LastSet last_set; ///< script in which the option was last set
} vimoption_T;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index f078f6073c..a11ed9188c 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -1,8 +1,12 @@
#ifndef NVIM_OPTION_DEFS_H
#define NVIM_OPTION_DEFS_H
+#include <stdbool.h>
+#include <stddef.h>
+
#include "nvim/api/private/defs.h"
-#include "nvim/eval/typval_defs.h"
+#include "nvim/cmdexpand_defs.h"
+#include "nvim/regexp_defs.h"
#include "nvim/types.h"
/// Option value type
@@ -25,6 +29,14 @@ typedef struct {
} data;
} OptVal;
+/// :set operator types
+typedef enum {
+ OP_NONE = 0,
+ OP_ADDING, ///< "opt+=arg"
+ OP_PREPENDING, ///< "opt^=arg"
+ OP_REMOVING, ///< "opt-=arg"
+} set_op_T;
+
/// Argument for the callback function (opt_did_set_cb_T) invoked after an
/// option value is modified.
typedef struct {
@@ -33,6 +45,7 @@ typedef struct {
void *os_varp;
int os_idx;
int os_flags;
+ set_op_T os_op;
/// old value of the option (can be a string, number or a boolean)
union {
@@ -80,4 +93,40 @@ typedef struct {
/// Otherwise returns an error message.
typedef const char *(*opt_did_set_cb_T)(optset_T *args);
+/// Argument for the callback function (opt_expand_cb_T) invoked after a string
+/// option value is expanded for cmdline completion.
+typedef struct {
+ /// Pointer to the option variable. It's always a string.
+ char *oe_varp;
+ /// The original option value, escaped.
+ char *oe_opt_value;
+
+ /// true if using set+= instead of set=
+ bool oe_append;
+ /// true if we would like to add the original option value as the first choice.
+ bool oe_include_orig_val;
+
+ /// Regex from the cmdline, for matching potential options against.
+ regmatch_T *oe_regmatch;
+ /// The expansion context.
+ expand_T *oe_xp;
+
+ /// The full argument passed to :set. For example, if the user inputs
+ /// ":set dip=icase,algorithm:my<Tab>", oe_xp->xp_pattern will only have
+ /// "my", but oe_set_arg will contain the whole "icase,algorithm:my".
+ char *oe_set_arg;
+} optexpand_T;
+
+/// Type for the callback function that is invoked when expanding possible
+/// string option values during cmdline completion.
+///
+/// Strings in returned matches will be managed and freed by caller.
+///
+/// Returns OK if the expansion succeeded (numMatches and matches have to be
+/// set). Otherwise returns FAIL.
+///
+/// Note: If returned FAIL or *numMatches is 0, *matches will NOT be freed by
+/// caller.
+typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches);
+
#endif // NVIM_OPTION_DEFS_H
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index d8bbce21b3..86e7f4cee8 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -16,6 +16,7 @@
///< the same.
#define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can
///< never be used for local or hidden options
+#define P_NO_DEF_EXP 0x20U ///< do not expand default value
#define P_NODEFAULT 0x40U ///< don't set to default value
#define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must
///< use free() when assigning new value
@@ -51,8 +52,10 @@
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_MLE 0x20000000U ///< under control of 'modelineexpr'
#define P_FUNC 0x40000000U ///< accept a function reference or a lambda
-
-#define P_NO_DEF_EXP 0x80000000U ///< Do not expand default value.
+#define P_COLON 0x80000000U ///< values use colons to create sublists
+// Warning: Currently we have used all 32 bits for option flags, and adding more
+// flags will overflow it. Adding another flag will need to change how
+// it's stored first.
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
@@ -183,7 +186,7 @@
#define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_"
// characters for p_ww option:
-#define WW_ALL "bshl<>[],~"
+#define WW_ALL "bshl<>[]~"
// characters for p_mouse option:
#define MOUSE_NORMAL 'n' // use mouse in Normal mode
@@ -757,9 +760,9 @@ extern char *p_vfile; ///< 'verbosefile'
EXTERN int p_warn; ///< 'warn'
EXTERN char *p_wop; ///< 'wildoptions'
EXTERN unsigned wop_flags;
-#define WOP_TAGFILE 0x01
-#define WOP_PUM 0x02
-#define WOP_FUZZY 0x04
+#define WOP_FUZZY 0x01
+#define WOP_TAGFILE 0x02
+#define WOP_PUM 0x04
EXTERN OptInt p_window; ///< 'window'
EXTERN char *p_wak; ///< 'winaltkeys'
EXTERN char *p_wig; ///< 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index cd1d760836..a0b9306908 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -6,7 +6,7 @@
--- @field varname? string
--- @field pv_name? string
--- @field type 'bool'|'number'|'string'
---- @field list? 'comma'|'onecomma'|'flags'|'flagscomma'
+--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
--- @field enable_if? string|false
@@ -25,6 +25,7 @@
--- @field alloced? true
--- @field redraw? vim.option_redraw[]
--- @field cb? string
+--- @field expand_cb? string
--- @field tags? string[]
--- @class vim.option_defaults
@@ -192,6 +193,7 @@ return {
set to one of CJK locales. See Unicode Standard Annex #11
(https://www.unicode.org/reports/tr11).
]=],
+ expand_cb = 'expand_set_ambiwidth',
full_name = 'ambiwidth',
redraw = { 'all_windows', 'ui_option' },
scope = { 'global' },
@@ -331,6 +333,7 @@ return {
option, you must load syntax.vim again to see the result. This can be
done with ":syntax on".
]=],
+ expand_cb = 'expand_set_background',
full_name = 'background',
scope = { 'global' },
short_desc = N_('"dark" or "light", used for highlight colors'),
@@ -357,6 +360,7 @@ return {
When the value is empty, Vi compatible backspacing is used, none of
the ways mentioned for the items above are possible.
]=],
+ expand_cb = 'expand_set_backspace',
full_name = 'backspace',
list = 'onecomma',
scope = { 'global' },
@@ -453,6 +457,7 @@ return {
the system may refuse to do this. In that case the "auto" value will
again not rename the file.
]=],
+ expand_cb = 'expand_set_backupcopy',
full_name = 'backupcopy',
list = 'onecomma',
scope = { 'global', 'buffer' },
@@ -621,6 +626,7 @@ return {
indicate that an error occurred. It can be silenced by adding the
"error" keyword.
]=],
+ expand_cb = 'expand_set_belloff',
full_name = 'belloff',
list = 'comma',
scope = { 'global' },
@@ -763,6 +769,7 @@ return {
added for the 'showbreak' setting.
(default: off)
]=],
+ expand_cb = 'expand_set_breakindentopt',
full_name = 'breakindentopt',
list = 'onecomma',
redraw = { 'current_buffer' },
@@ -816,6 +823,7 @@ return {
This option is used together with 'buftype' and 'swapfile' to specify
special kinds of buffers. See |special-buffers|.
]=],
+ expand_cb = 'expand_set_bufhidden',
full_name = 'bufhidden',
noglob = true,
scope = { 'buffer' },
@@ -893,6 +901,7 @@ return {
without saving. For writing there must be matching |BufWriteCmd|,
|FileWriteCmd| or |FileAppendCmd| autocommands.
]=],
+ expand_cb = 'expand_set_buftype',
full_name = 'buftype',
noglob = true,
scope = { 'buffer' },
@@ -917,6 +926,7 @@ return {
case mapping, the current locale is not effective.
This probably only matters for Turkish.
]=],
+ expand_cb = 'expand_set_casemap',
full_name = 'casemap',
list = 'onecomma',
scope = { 'global' },
@@ -1183,6 +1193,7 @@ return {
"*". See |clipboard|.
]=],
deny_duplicates = true,
+ expand_cb = 'expand_set_clipboard',
full_name = 'clipboard',
list = 'onecomma',
scope = { 'global' },
@@ -1369,6 +1380,7 @@ return {
based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns
|i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions).
]=],
+ expand_cb = 'expand_set_complete',
full_name = 'complete',
list = 'onecomma',
scope = { 'buffer' },
@@ -1399,7 +1411,9 @@ return {
Keep in mind that the cursor position is not always where it's
displayed. E.g., when moving vertically it may change column.
]=],
+ expand_cb = 'expand_set_concealcursor',
full_name = 'concealcursor',
+ list = 'flags',
redraw = { 'current_window' },
scope = { 'window' },
short_desc = N_('whether concealable text is hidden in cursor line'),
@@ -1492,6 +1506,7 @@ return {
select one from the menu. Only works in combination with
"menu" or "menuone".
]=],
+ expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',
list = 'onecomma',
scope = { 'global' },
@@ -1517,6 +1532,7 @@ return {
command line completion the global value is used.
]=],
enable_if = 'BACKSLASH_IN_FILENAME',
+ expand_cb = 'expand_set_completeslash',
full_name = 'completeslash',
scope = { 'buffer' },
type = 'string',
@@ -1797,6 +1813,7 @@ return {
_ When using |cw| on a word, do not include the
whitespace following the word in the motion.
]=],
+ expand_cb = 'expand_set_cpoptions',
full_name = 'cpoptions',
list = 'flags',
redraw = { 'all_windows' },
@@ -1878,6 +1895,7 @@ return {
"line" and "screenline" cannot be used together.
]=],
+ expand_cb = 'expand_set_cursorlineopt',
full_name = 'cursorlineopt',
list = 'onecomma',
redraw = { 'current_window_only' },
@@ -1900,6 +1918,7 @@ return {
"msg" and "throw" are useful for debugging 'foldexpr', 'formatexpr' or
'indentexpr'.
]=],
+ expand_cb = 'expand_set_debug',
full_name = 'debug',
scope = { 'global' },
short_desc = N_('to "msg" to see all error messages'),
@@ -2048,7 +2067,8 @@ return {
When omitted a context of six lines is used.
When using zero the context is actually one,
since folds require a line in between, also
- for a deleted line.
+ for a deleted line. Set it to a very large
+ value (999999) to disable folding completely.
See |fold-diff|.
iblank Ignore changes where lines are all blank. Adds
@@ -2140,8 +2160,9 @@ return {
:set diffopt-=internal " do NOT use the internal diff parser
<
]=],
+ expand_cb = 'expand_set_diffopt',
full_name = 'diffopt',
- list = 'onecomma',
+ list = 'onecommacolon',
redraw = { 'current_window' },
scope = { 'global' },
short_desc = N_('options for using diff mode'),
@@ -2242,6 +2263,7 @@ return {
The "@" character can be changed by setting the "lastline" item in
'fillchars'. The character is highlighted with |hl-NonText|.
]=],
+ expand_cb = 'expand_set_display',
full_name = 'display',
list = 'onecomma',
redraw = { 'all_windows' },
@@ -2260,6 +2282,7 @@ return {
hor horizontally, height of windows is not affected
both width and height of windows is affected
]=],
+ expand_cb = 'expand_set_eadirection',
full_name = 'eadirection',
scope = { 'global' },
short_desc = N_("in which direction 'equalalways' works"),
@@ -2470,6 +2493,7 @@ return {
:set ei=WinEnter,WinLeave
<
]=],
+ expand_cb = 'expand_set_eventignore',
full_name = 'eventignore',
list = 'onecomma',
scope = { 'global' },
@@ -2558,6 +2582,7 @@ return {
This option cannot be changed when 'modifiable' is off.
]=],
+ expand_cb = 'expand_set_encoding',
full_name = 'fileencoding',
no_mkrc = true,
redraw = { 'statuslines', 'current_buffer' },
@@ -2619,6 +2644,7 @@ return {
Setting this option does not have an effect until the next time a file
is read.
]=],
+ expand_cb = 'expand_set_encoding',
full_name = 'fileencodings',
list = 'onecomma',
scope = { 'global' },
@@ -2651,6 +2677,7 @@ return {
option is set, because the file would be different when written.
This option cannot be changed when 'modifiable' is off.
]=],
+ expand_cb = 'expand_set_fileformat',
full_name = 'fileformat',
no_mkrc = true,
redraw = { 'curswant', 'statuslines' },
@@ -2714,6 +2741,7 @@ return {
used.
Also see |file-formats|.
]=],
+ expand_cb = 'expand_set_fileformat',
full_name = 'fileformats',
list = 'onecomma',
scope = { 'global' },
@@ -2846,6 +2874,7 @@ return {
eob EndOfBuffer |hl-EndOfBuffer|
lastline NonText |hl-NonText|
]=],
+ expand_cb = 'expand_set_chars_option',
full_name = 'fillchars',
list = 'onecomma',
redraw = { 'current_window' },
@@ -2884,6 +2913,7 @@ return {
its level is higher than 'foldlevel'. Useful if you want folds to
automatically close when moving out of them.
]=],
+ expand_cb = 'expand_set_foldclose',
full_name = 'foldclose',
list = 'onecomma',
redraw = { 'current_window' },
@@ -2906,6 +2936,7 @@ return {
"[1-9]": to display a fixed number of columns
See |folding|.
]=],
+ expand_cb = 'expand_set_foldcolumn',
full_name = 'foldcolumn',
redraw = { 'current_window' },
scope = { 'window' },
@@ -3045,6 +3076,7 @@ return {
|fold-syntax| syntax Syntax highlighting items specify folds.
|fold-diff| diff Fold text that is not changed.
]=],
+ expand_cb = 'expand_set_foldmethod',
full_name = 'foldmethod',
redraw = { 'current_window' },
scope = { 'window' },
@@ -3122,6 +3154,7 @@ return {
To close folds you can re-apply 'foldlevel' with the |zx| command or
set the 'foldclose' option to "all".
]=],
+ expand_cb = 'expand_set_foldopen',
full_name = 'foldopen',
list = 'onecomma',
redraw = { 'curswant' },
@@ -3218,6 +3251,7 @@ return {
To avoid problems with flags that are added in the future, use the
"+=" and "-=" feature of ":set" |add-option-flags|.
]=],
+ expand_cb = 'expand_set_formatoptions',
full_name = 'formatoptions',
list = 'flags',
scope = { 'buffer' },
@@ -4050,6 +4084,7 @@ return {
'redrawtime') then 'inccommand' is automatically disabled until
|Command-line-mode| is done.
]=],
+ expand_cb = 'expand_set_inccommand',
full_name = 'inccommand',
scope = { 'global' },
short_desc = N_('Live preview of substitution'),
@@ -4451,6 +4486,7 @@ return {
|alternate-file| or using |mark-motions| try to
restore the |mark-view| in which the action occurred.
]=],
+ expand_cb = 'expand_set_jumpoptions',
full_name = 'jumpoptions',
list = 'onecomma',
scope = { 'global' },
@@ -4495,6 +4531,7 @@ return {
Special keys in this context are the cursor keys, <End>, <Home>,
<PageUp> and <PageDown>.
]=],
+ expand_cb = 'expand_set_keymodel',
full_name = 'keymodel',
list = 'onecomma',
scope = { 'global' },
@@ -4779,6 +4816,7 @@ return {
Note that when using 'indentexpr' the `=` operator indents all the
lines, otherwise the first line is not indented (Vi-compatible).
]=],
+ expand_cb = 'expand_set_lispoptions',
full_name = 'lispoptions',
list = 'onecomma',
pv_name = 'p_lop',
@@ -4933,6 +4971,7 @@ return {
"precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace",
"lead" and "trail".
]=],
+ expand_cb = 'expand_set_chars_option',
full_name = 'listchars',
list = 'onecomma',
redraw = { 'current_window' },
@@ -5015,6 +5054,7 @@ return {
:set makeencoding=char " system locale is used
<
]=],
+ expand_cb = 'expand_set_encoding',
full_name = 'makeencoding',
scope = { 'global', 'buffer' },
short_desc = N_('Converts the output of external commands'),
@@ -5377,6 +5417,7 @@ return {
'mousehide' hide mouse pointer while typing text
'selectmode' whether to start Select mode or Visual mode
]=],
+ expand_cb = 'expand_set_mouse',
full_name = 'mouse',
list = 'flags',
scope = { 'global' },
@@ -5468,6 +5509,7 @@ return {
"g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
"g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
]=],
+ expand_cb = 'expand_set_mousemodel',
full_name = 'mousemodel',
scope = { 'global' },
short_desc = N_('changes meaning of mouse buttons'),
@@ -5513,6 +5555,7 @@ return {
< Will make Nvim scroll 5 lines at a time when scrolling vertically, and
scroll 2 columns at a time when scrolling horizontally.
]=],
+ expand_cb = 'expand_set_mousescroll',
full_name = 'mousescroll',
list = 'comma',
scope = { 'global' },
@@ -5645,6 +5688,7 @@ return {
considered decimal. This also happens for numbers that are not
recognized as octal or hex.
]=],
+ expand_cb = 'expand_set_nrformats',
full_name = 'nrformats',
list = 'onecomma',
scope = { 'buffer' },
@@ -6307,6 +6351,7 @@ return {
This is useful for languages such as Hebrew, Arabic and Farsi.
The 'rightleft' option must be set for 'rightleftcmd' to take effect.
]=],
+ expand_cb = 'expand_set_rightleftcmd',
full_name = 'rightleftcmd',
redraw = { 'current_window' },
scope = { 'window' },
@@ -6634,6 +6679,7 @@ return {
When 'diff' mode is active there always is vertical scroll binding,
even when "ver" isn't there.
]=],
+ expand_cb = 'expand_set_scrollopt',
full_name = 'scrollopt',
list = 'onecomma',
scope = { 'global' },
@@ -6687,6 +6733,7 @@ return {
backwards, you cannot include the last character of a line, when
starting in Normal mode and 'virtualedit' empty.
]=],
+ expand_cb = 'expand_set_selection',
full_name = 'selection',
scope = { 'global' },
short_desc = N_('what type of selection to use'),
@@ -6707,6 +6754,7 @@ return {
cmd when using "v", "V" or CTRL-V
See |Select-mode|.
]=],
+ expand_cb = 'expand_set_selectmode',
full_name = 'selectmode',
list = 'onecomma',
scope = { 'global' },
@@ -6758,6 +6806,7 @@ return {
If you leave out "options" many things won't work well after restoring
the session.
]=],
+ expand_cb = 'expand_set_sessionoptions',
full_name = 'sessionoptions',
list = 'onecomma',
scope = { 'global' },
@@ -7292,6 +7341,7 @@ return {
shm=a Abbreviation, but no loss of information.
shm=at Abbreviation, and truncate message when necessary.
]=],
+ expand_cb = 'expand_set_shortmess',
full_name = 'shortmess',
list = 'flags',
scope = { 'global' },
@@ -7368,6 +7418,7 @@ return {
place the text. Without a custom 'statusline' or 'tabline' it will be
displayed in a convenient location.
]=],
+ expand_cb = 'expand_set_showcmdloc',
full_name = 'showcmdloc',
scope = { 'global' },
short_desc = N_('change location of partial command'),
@@ -7530,6 +7581,7 @@ return {
This is done in order for the signcolumn appearance not appear weird
during line deletion.
]=],
+ expand_cb = 'expand_set_signcolumn',
full_name = 'signcolumn',
redraw = { 'current_window' },
scope = { 'window' },
@@ -7828,6 +7880,7 @@ return {
security reasons.
]=],
expand = true,
+ expand_cb = 'expand_set_spellsuggest',
full_name = 'spellsuggest',
list = 'onecomma',
scope = { 'global' },
@@ -7852,7 +7905,7 @@ return {
designated regions of the buffer are spellchecked in
this case.
]=],
- expand = true,
+ expand_cb = 'expand_set_spelloptions',
full_name = 'spelloptions',
list = 'onecomma',
redraw = { 'current_buffer' },
@@ -7892,6 +7945,7 @@ return {
with the previous cursor position. For "screen", the text cannot always
be kept on the same screen line when 'wrap' is enabled.
]=],
+ expand_cb = 'expand_set_splitkeep',
full_name = 'splitkeep',
scope = { 'global' },
short_desc = N_('determines scroll behavior for split windows'),
@@ -8326,6 +8380,7 @@ return {
uselast If included, jump to the previously used window when
jumping to errors with |quickfix| commands.
]=],
+ expand_cb = 'expand_set_switchbuf',
full_name = 'switchbuf',
list = 'onecomma',
scope = { 'global' },
@@ -8580,6 +8635,7 @@ return {
match Match case
smart Ignore case unless an upper case letter is used
]=],
+ expand_cb = 'expand_set_tagcase',
full_name = 'tagcase',
scope = { 'global', 'buffer' },
short_desc = N_('how to handle case when searching in tags files'),
@@ -8728,6 +8784,7 @@ return {
C1 Control characters 0x80...0x9F
]=],
+ expand_cb = 'expand_set_termpastefilter',
full_name = 'termpastefilter',
list = 'onecomma',
scope = { 'global' },
@@ -9275,6 +9332,7 @@ return {
slash |deprecated| Always enabled. Uses "/" in filenames.
unix |deprecated| Always enabled. Uses "\n" line endings.
]=],
+ expand_cb = 'expand_set_sessionoptions',
full_name = 'viewoptions',
list = 'onecomma',
scope = { 'global' },
@@ -9331,6 +9389,7 @@ return {
not get a warning for it.
When combined with other words, "none" is ignored.
]=],
+ expand_cb = 'expand_set_virtualedit',
full_name = 'virtualedit',
list = 'onecomma',
redraw = { 'curswant' },
@@ -9395,6 +9454,7 @@ return {
line (not an empty line) then it will not move to the next line. This
makes "dl", "cl", "yl" etc. work normally.
]=],
+ expand_cb = 'expand_set_whichwrap',
full_name = 'whichwrap',
list = 'flagscomma',
scope = { 'global' },
@@ -9578,8 +9638,9 @@ return {
< Complete longest common string, then list alternatives.
More info here: |cmdline-completion|.
]=],
+ expand_cb = 'expand_set_wildmode',
full_name = 'wildmode',
- list = 'onecomma',
+ list = 'onecommacolon',
scope = { 'global' },
short_desc = N_("mode for 'wildchar' command-line expansion"),
type = 'string',
@@ -9609,6 +9670,7 @@ return {
d #define
f function
]=],
+ expand_cb = 'expand_set_wildoptions',
full_name = 'wildoptions',
list = 'onecomma',
scope = { 'global' },
@@ -9637,6 +9699,7 @@ return {
key is never used for the menu.
This option is not used for <F10>; on Win32.
]=],
+ expand_cb = 'expand_set_winaltkeys',
full_name = 'winaltkeys',
scope = { 'global' },
short_desc = N_('when the windows system handles ALT keys'),
@@ -9691,7 +9754,7 @@ return {
{
abbreviation = 'winhl',
alloced = true,
- cb = 'did_set_winhl',
+ cb = 'did_set_winhighlight',
defaults = { if_true = '' },
deny_duplicates = true,
desc = [=[
@@ -9713,8 +9776,9 @@ return {
set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC
<
]=],
+ expand_cb = 'expand_set_winhighlight',
full_name = 'winhighlight',
- list = 'onecomma',
+ list = 'onecommacolon',
redraw = { 'current_window' },
scope = { 'window' },
short_desc = N_('Setup window-local highlights'),
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 750941da07..6f41bba99b 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -11,6 +11,8 @@
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/diff.h"
@@ -41,6 +43,7 @@
#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/pos.h"
+#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
@@ -72,12 +75,25 @@ static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhar
static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error",
"esc", "ex", "hangul", "lang", "mess", "showmatch", "operator",
"register", "shell", "spell", "wildmode", NULL };
+// Note: Keep this in sync with briopt_check()
+static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL };
+// Note: Keep this in sync with diffopt_changed()
+static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase",
+ "iwhite", "iwhiteall", "iwhiteeol", "horizontal", "vertical",
+ "closeoff", "hiddenoff", "foldcolumn:", "followwrap", "internal",
+ "indent-heuristic", "linematch:", "algorithm:", NULL };
+static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL };
static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", NULL };
static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
+static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
+// Note: Keep this in sync with fill_culopt_flags()
+static char *(p_culopt_values[]) = { "line", "screenline", "number", "both", NULL };
static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", NULL };
static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent", "quickfix", "search",
"tag", "insert", "undo", "jump", NULL };
+// Note: Keep this in sync with spell_check_sps()
+static char *(p_sps_values[]) = { "best", "fast", "double", "expr:", "file:", "timeout:", NULL };
/// Also used for 'viewoptions'! Keep in sync with SSOP_ flags.
static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "localoptions",
"options", "help", "blank", "globals", "slash", "unix", "sesdir",
@@ -89,7 +105,9 @@ static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vspli
static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL };
static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL };
static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
-static char *(p_wop_values[]) = { "tagfile", "pum", "fuzzy", NULL };
+// Note: Keep this in sync with check_opt_wim()
+static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL };
+static char *(p_wop_values[]) = { "fuzzy", "tagfile", "pum", NULL };
static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL };
static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
@@ -118,7 +136,6 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto
static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5",
"auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4",
"5", "6", "7", "8", "9", NULL };
-static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
static char *(p_jop_values[]) = { "stack", "view", NULL };
@@ -487,8 +504,9 @@ const char *set_string_option(const int opt_idx, void *varp_arg, const char *val
secure = 1;
}
- const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval, errbuf,
- errbuflen, opt_flags, value_checked);
+ const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval,
+ errbuf, errbuflen,
+ opt_flags, OP_NONE, value_checked);
secure = secure_saved;
@@ -659,6 +677,116 @@ static const char *did_set_option_listflag(char *val, char *flags, char *errbuf,
return NULL;
}
+/// Expand an option that accepts a list of string values.
+static int expand_set_opt_string(optexpand_T *args, char **values, size_t numValues,
+ int *numMatches, char ***matches)
+{
+ regmatch_T *regmatch = args->oe_regmatch;
+ bool include_orig_val = args->oe_include_orig_val;
+ char *option_val = args->oe_opt_value;
+
+ // Assume numValues is small since they are fixed enums, so just allocate
+ // upfront instead of needing two passes to calculate output size.
+ *matches = xmalloc(sizeof(char *) * (numValues + 1));
+
+ int count = 0;
+
+ if (include_orig_val && *option_val != NUL) {
+ (*matches)[count++] = xstrdup(option_val);
+ }
+
+ for (char **val = values; *val != NULL; val++) {
+ if (include_orig_val && *option_val != NUL) {
+ if (strcmp(*val, option_val) == 0) {
+ continue;
+ }
+ }
+ if (vim_regexec(regmatch, *val, (colnr_T)0)) {
+ (*matches)[count++] = xstrdup(*val);
+ }
+ }
+ if (count == 0) {
+ XFREE_CLEAR(*matches);
+ return FAIL;
+ }
+ *numMatches = count;
+ return OK;
+}
+
+static char *set_opt_callback_orig_option = NULL;
+static char *((*set_opt_callback_func)(expand_T *, int));
+
+/// Callback used by expand_set_opt_generic to also include the original value.
+static char *expand_set_opt_callback(expand_T *xp, int idx)
+{
+ if (idx == 0) {
+ if (set_opt_callback_orig_option != NULL) {
+ return set_opt_callback_orig_option;
+ } else {
+ return ""; // empty strings are ignored
+ }
+ }
+ return set_opt_callback_func(xp, idx - 1);
+}
+
+/// Expand an option with a callback that iterates through a list of possible names.
+static int expand_set_opt_generic(optexpand_T *args, CompleteListItemGetter func, int *numMatches,
+ char ***matches)
+{
+ set_opt_callback_orig_option = args->oe_include_orig_val ? args->oe_opt_value : NULL;
+ set_opt_callback_func = func;
+
+ // not using fuzzy as currently EXPAND_STRING_SETTING doesn't use it
+ ExpandGeneric("", args->oe_xp, args->oe_regmatch, matches, numMatches,
+ expand_set_opt_callback, false);
+
+ set_opt_callback_orig_option = NULL;
+ set_opt_callback_func = NULL;
+ return OK;
+}
+
+/// Expand an option which is a list of flags.
+static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatches, char ***matches)
+{
+ char *option_val = args->oe_opt_value;
+ char *cmdline_val = args->oe_set_arg;
+ bool append = args->oe_append;
+ bool include_orig_val = args->oe_include_orig_val && (*option_val != NUL);
+
+ size_t num_flags = strlen(flags);
+
+ // Assume we only have small number of flags, so just allocate max size.
+ *matches = xmalloc(sizeof(char *) * (num_flags + 1));
+
+ int count = 0;
+
+ if (include_orig_val) {
+ (*matches)[count++] = xstrdup(option_val);
+ }
+
+ for (char *flag = flags; *flag != NUL; flag++) {
+ if (append && vim_strchr(option_val, *flag) != NULL) {
+ continue;
+ }
+
+ if (vim_strchr(cmdline_val, *flag) == NULL) {
+ if (include_orig_val && option_val[1] == NUL && *flag == option_val[0]) {
+ // This value is already used as the first choice as it's the
+ // existing flag. Just skip it to avoid duplicate.
+ continue;
+ }
+ (*matches)[count++] = xstrnsave(flag, 1);
+ }
+ }
+
+ if (count == 0) {
+ XFREE_CLEAR(*matches);
+ return FAIL;
+ }
+ *numMatches = count;
+ return OK;
+}
+
/// The 'ambiwidth' option is changed.
const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -668,6 +796,15 @@ const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED)
return check_chars_options();
}
+int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_ambw_values,
+ ARRAY_SIZE(p_ambw_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'background' option is changed.
const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -692,6 +829,15 @@ const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_background(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_bg_values,
+ ARRAY_SIZE(p_bg_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'backspace' option is changed.
const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -705,6 +851,15 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_backspace(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_bs_values,
+ ARRAY_SIZE(p_bs_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'backupcopy' option is changed.
const char *did_set_backupcopy(optset_T *args)
{
@@ -739,6 +894,15 @@ const char *did_set_backupcopy(optset_T *args)
return NULL;
}
+int expand_set_backupcopy(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_bkc_values,
+ ARRAY_SIZE(p_bkc_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'backupext' or the 'patchmode' option is changed.
const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -756,6 +920,15 @@ const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true);
}
+int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_bo_values,
+ ARRAY_SIZE(p_bo_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'breakindentopt' option is changed.
const char *did_set_breakindentopt(optset_T *args)
{
@@ -771,6 +944,15 @@ const char *did_set_breakindentopt(optset_T *args)
return NULL;
}
+int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_briopt_values,
+ ARRAY_SIZE(p_briopt_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'bufhidden' option is changed.
const char *did_set_bufhidden(optset_T *args)
{
@@ -778,6 +960,15 @@ const char *did_set_bufhidden(optset_T *args)
return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false);
}
+int expand_set_bufhidden(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_bufhidden_values,
+ ARRAY_SIZE(p_bufhidden_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'buftype' option is changed.
const char *did_set_buftype(optset_T *args)
{
@@ -798,12 +989,30 @@ const char *did_set_buftype(optset_T *args)
return NULL;
}
+int expand_set_buftype(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_buftype_values,
+ ARRAY_SIZE(p_buftype_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'casemap' option is changed.
const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true);
}
+int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_cmp_values,
+ ARRAY_SIZE(p_cmp_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The global 'listchars' or 'fillchars' option is changed.
static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags)
{
@@ -867,6 +1076,17 @@ const char *did_set_chars_option(optset_T *args)
return errmsg;
}
+/// Expand 'fillchars' or 'listchars' option value.
+int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches)
+{
+ char **varp = (char **)args->oe_varp;
+ bool is_lcs = (varp == &p_lcs || varp == &curwin->w_p_lcs);
+ return expand_set_opt_generic(args,
+ is_lcs ? get_listchars_name : get_fillchars_name,
+ numMatches,
+ matches);
+}
+
/// The 'cinoptions' option is changed.
const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -882,6 +1102,15 @@ const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true);
}
+int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_cb_values,
+ ARRAY_SIZE(p_cb_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'colorcolumn' option is changed.
const char *did_set_colorcolumn(optset_T *args)
{
@@ -972,6 +1201,18 @@ const char *did_set_complete(optset_T *args)
return NULL;
}
+int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches)
+{
+ static char *(p_cpt_values[]) = {
+ ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", NULL
+ };
+ return expand_set_opt_string(args,
+ p_cpt_values,
+ ARRAY_SIZE(p_cpt_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'completeopt' option is changed.
const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -982,6 +1223,15 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_cot_values,
+ ARRAY_SIZE(p_cot_values) - 1,
+ numMatches,
+ matches);
+}
+
#ifdef BACKSLASH_IN_FILENAME
/// The 'completeslash' option is changed.
const char *did_set_completeslash(optset_T *args)
@@ -993,6 +1243,15 @@ const char *did_set_completeslash(optset_T *args)
}
return NULL;
}
+
+int expand_set_completeslash(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_csl_values,
+ ARRAY_SIZE(p_csl_values) - 1,
+ numMatches,
+ matches);
+}
#endif
/// The 'concealcursor' option is changed.
@@ -1003,6 +1262,11 @@ const char *did_set_concealcursor(optset_T *args)
return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen);
}
+int expand_set_concealcursor(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, COCU_ALL, numMatches, matches);
+}
+
/// The 'cpoptions' option is changed.
const char *did_set_cpoptions(optset_T *args)
{
@@ -1011,12 +1275,18 @@ const char *did_set_cpoptions(optset_T *args)
return did_set_option_listflag(*varp, CPO_VI, args->os_errbuf, args->os_errbuflen);
}
+int expand_set_cpoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, CPO_VI, numMatches, matches);
+}
+
/// The 'cursorlineopt' option is changed.
const char *did_set_cursorlineopt(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
char **varp = (char **)args->os_varp;
+ // This could be changed to use opt_strings_flags() instead.
if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) {
return e_invarg;
}
@@ -1024,12 +1294,30 @@ const char *did_set_cursorlineopt(optset_T *args)
return NULL;
}
+int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_culopt_values,
+ ARRAY_SIZE(p_culopt_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'debug' option is changed.
const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_strings(p_debug, p_debug_values, false);
}
+int expand_set_debug(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_debug_values,
+ ARRAY_SIZE(p_debug_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'diffopt' option is changed.
const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1039,6 +1327,31 @@ const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ expand_T *xp = args->oe_xp;
+
+ if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern - 1) == ':') {
+ // Within "algorithm:", we have a subgroup of possible options.
+ const size_t algo_len = strlen("algorithm:");
+ if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len
+ && strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) {
+ return expand_set_opt_string(args,
+ p_dip_algorithm_values,
+ ARRAY_SIZE(p_dip_algorithm_values) - 1,
+ numMatches,
+ matches);
+ }
+ return FAIL;
+ }
+
+ return expand_set_opt_string(args,
+ p_dip_values,
+ ARRAY_SIZE(p_dip_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'display' option is changed.
const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1050,12 +1363,30 @@ const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_display(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_dy_values,
+ ARRAY_SIZE(p_dy_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'eadirection' option is changed.
const char *did_set_eadirection(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_strings(p_ead, p_ead_values, false);
}
+int expand_set_eadirection(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_ead_values,
+ ARRAY_SIZE(p_ead_values) - 1,
+ numMatches,
+ matches);
+}
+
/// One of the 'encoding', 'fileencoding' or 'makeencoding'
/// options is changed.
const char *did_set_encoding(optset_T *args)
@@ -1098,6 +1429,11 @@ const char *did_set_encoding(optset_T *args)
return NULL;
}
+int expand_set_encoding(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_generic(args, get_encoding_name, numMatches, matches);
+}
+
/// The 'eventignore' option is changed.
const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1107,6 +1443,21 @@ const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+static char *get_eventignore_name(expand_T *xp, int idx)
+{
+ // 'eventignore' allows special keyword "all" in addition to
+ // all event names.
+ if (idx == 0) {
+ return "all";
+ }
+ return get_event_name_no_group(xp, idx - 1);
+}
+
+int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches);
+}
+
/// The 'fileformat' option is changed.
const char *did_set_fileformat(optset_T *args)
{
@@ -1130,6 +1481,15 @@ const char *did_set_fileformat(optset_T *args)
return NULL;
}
+int expand_set_fileformat(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_ff_values,
+ ARRAY_SIZE(p_ff_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'fileformats' option is changed.
const char *did_set_fileformats(optset_T *args)
{
@@ -1160,6 +1520,15 @@ const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_strings(p_fcl, p_fcl_values, true);
}
+int expand_set_foldclose(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_fcl_values,
+ ARRAY_SIZE(p_fcl_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'foldcolumn' option is changed.
const char *did_set_foldcolumn(optset_T *args)
{
@@ -1170,6 +1539,15 @@ const char *did_set_foldcolumn(optset_T *args)
return NULL;
}
+int expand_set_foldcolumn(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_fdc_values,
+ ARRAY_SIZE(p_fdc_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'foldexpr' option is changed.
const char *did_set_foldexpr(optset_T *args)
{
@@ -1229,12 +1607,30 @@ const char *did_set_foldmethod(optset_T *args)
return NULL;
}
+int expand_set_foldmethod(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_fdm_values,
+ ARRAY_SIZE(p_fdm_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'foldopen' option is changed.
const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true);
}
+int expand_set_foldopen(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_fdo_values,
+ ARRAY_SIZE(p_fdo_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'formatoptions' option is changed.
const char *did_set_formatoptions(optset_T *args)
{
@@ -1243,6 +1639,11 @@ const char *did_set_formatoptions(optset_T *args)
return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen);
}
+int expand_set_formatoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, FO_ALL, numMatches, matches);
+}
+
/// The 'guicursor' option is changed.
const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1300,6 +1701,15 @@ const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_strings(p_icm, p_icm_values, false);
}
+int expand_set_inccommand(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_icm_values,
+ ARRAY_SIZE(p_icm_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is
/// changed.
const char *did_set_isopt(optset_T *args)
@@ -1321,6 +1731,15 @@ const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true);
}
+int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_jop_values,
+ ARRAY_SIZE(p_jop_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'keymap' option has changed.
const char *did_set_keymap(optset_T *args)
{
@@ -1384,6 +1803,15 @@ const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_keymodel(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_km_values,
+ ARRAY_SIZE(p_km_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'lispoptions' option is changed.
const char *did_set_lispoptions(optset_T *args)
{
@@ -1395,6 +1823,16 @@ const char *did_set_lispoptions(optset_T *args)
return NULL;
}
+int expand_set_lispoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ static char *(p_lop_values[]) = { "expr:0", "expr:1", NULL };
+ return expand_set_opt_string(args,
+ p_lop_values,
+ ARRAY_SIZE(p_lop_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'matchpairs' option is changed.
const char *did_set_matchpairs(optset_T *args)
{
@@ -1439,12 +1877,26 @@ const char *did_set_mouse(optset_T *args)
return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen);
}
+int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches);
+}
+
/// The 'mousemodel' option is changed.
const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_strings(p_mousem, p_mousem_values, false);
}
+int expand_set_mousemodel(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_mousem_values,
+ ARRAY_SIZE(p_mousem_values) - 1,
+ numMatches,
+ matches);
+}
+
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
@@ -1510,6 +1962,16 @@ const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_mousescroll(optexpand_T *args, int *numMatches, char ***matches)
+{
+ static char *(p_mousescroll_values[]) = { "hor:", "ver:", NULL };
+ return expand_set_opt_string(args,
+ p_mousescroll_values,
+ ARRAY_SIZE(p_mousescroll_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'nrformats' option is changed.
const char *did_set_nrformats(optset_T *args)
{
@@ -1518,6 +1980,15 @@ const char *did_set_nrformats(optset_T *args)
return did_set_opt_strings(*varp, p_nf_values, true);
}
+int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_nf_values,
+ ARRAY_SIZE(p_nf_values) - 1,
+ numMatches,
+ matches);
+}
+
/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
const char *did_set_optexpr(optset_T *args)
@@ -1553,6 +2024,16 @@ const char *did_set_rightleftcmd(optset_T *args)
return NULL;
}
+int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char ***matches)
+{
+ static char *(p_rlc_values[]) = { "search", NULL };
+ return expand_set_opt_string(args,
+ p_rlc_values,
+ ARRAY_SIZE(p_rlc_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'rulerformat' option is changed.
const char *did_set_rulerformat(optset_T *args)
{
@@ -1565,6 +2046,15 @@ const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_strings(p_sbo, p_scbopt_values, true);
}
+int expand_set_scrollopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_scbopt_values,
+ ARRAY_SIZE(p_scbopt_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'selection' option is changed.
const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1574,12 +2064,30 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_selection(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_sel_values,
+ ARRAY_SIZE(p_sel_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'selectmode' option is changed.
const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_strings(p_slm, p_slm_values, true);
}
+int expand_set_selectmode(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_slm_values,
+ ARRAY_SIZE(p_slm_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'sessionoptions' option is changed.
const char *did_set_sessionoptions(optset_T *args)
{
@@ -1595,6 +2103,15 @@ const char *did_set_sessionoptions(optset_T *args)
return NULL;
}
+int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_ssop_values,
+ ARRAY_SIZE(p_ssop_values) - 1,
+ numMatches,
+ matches);
+}
+
static const char *did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf,
size_t errbuflen)
{
@@ -1661,6 +2178,11 @@ const char *did_set_shortmess(optset_T *args)
return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen);
}
+int expand_set_shortmess(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, SHM_ALL, numMatches, matches);
+}
+
/// The 'showbreak' option is changed.
const char *did_set_showbreak(optset_T *args)
{
@@ -1681,6 +2203,15 @@ const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_strings(p_sloc, p_sloc_values, true);
}
+int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_sloc_values,
+ ARRAY_SIZE(p_sloc_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'signcolumn' option is changed.
const char *did_set_signcolumn(optset_T *args)
{
@@ -1700,6 +2231,15 @@ const char *did_set_signcolumn(optset_T *args)
return NULL;
}
+int expand_set_signcolumn(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_scl_values,
+ ARRAY_SIZE(p_scl_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'spellcapcheck' option is changed.
const char *did_set_spellcapcheck(optset_T *args)
{
@@ -1745,6 +2285,15 @@ const char *did_set_spelloptions(optset_T *args)
return NULL;
}
+int expand_set_spelloptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_spo_values,
+ ARRAY_SIZE(p_spo_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'spellsuggest' option is changed.
const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1754,12 +2303,30 @@ const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_sps_values,
+ ARRAY_SIZE(p_sps_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'splitkeep' option is changed.
const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_strings(p_spk, p_spk_values, false);
}
+int expand_set_splitkeep(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_spk_values,
+ ARRAY_SIZE(p_spk_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'statuscolumn' option is changed.
const char *did_set_statuscolumn(optset_T *args)
{
@@ -1817,6 +2384,15 @@ const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED)
return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true);
}
+int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_swb_values,
+ ARRAY_SIZE(p_swb_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'tabline' option is changed.
const char *did_set_tabline(optset_T *args)
{
@@ -1850,12 +2426,30 @@ const char *did_set_tagcase(optset_T *args)
return NULL;
}
+int expand_set_tagcase(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_tc_values,
+ ARRAY_SIZE(p_tc_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'termpastefilter' option is changed.
const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true);
}
+int expand_set_termpastefilter(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_tpf_values,
+ ARRAY_SIZE(p_tpf_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'titlestring' or the 'iconstring' option is changed.
static const char *did_set_titleiconstring(optset_T *args, int flagval)
{
@@ -1988,12 +2582,28 @@ const char *did_set_virtualedit(optset_T *args)
return NULL;
}
+int expand_set_virtualedit(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_ve_values,
+ ARRAY_SIZE(p_ve_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'whichwrap' option is changed.
const char *did_set_whichwrap(optset_T *args)
{
char **varp = (char **)args->os_varp;
- return did_set_option_listflag(*varp, WW_ALL, args->os_errbuf, args->os_errbuflen);
+ // Add ',' to the list flags because 'whichwrap' is a flag
+ // list that is comma-separated.
+ return did_set_option_listflag(*varp, WW_ALL ",", args->os_errbuf, args->os_errbuflen);
+}
+
+int expand_set_whichwrap(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_listflag(args, WW_ALL, numMatches, matches);
}
/// The 'wildmode' option is changed.
@@ -2005,12 +2615,30 @@ const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_wildmode(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_wim_values,
+ ARRAY_SIZE(p_wim_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'wildoptions' option is changed.
const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED)
{
return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true);
}
+int expand_set_wildoptions(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_wop_values,
+ ARRAY_SIZE(p_wop_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'winaltkeys' option is changed.
const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -2020,13 +2648,23 @@ const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_wak_values,
+ ARRAY_SIZE(p_wak_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'winbar' option is changed.
const char *did_set_winbar(optset_T *args)
{
return did_set_statustabline_rulerformat(args, false, false);
}
-const char *did_set_winhl(optset_T *args)
+/// The 'winhighlight' option is changed.
+const char *did_set_winhighlight(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
if (!parse_winhl_opt(win)) {
@@ -2035,6 +2673,11 @@ const char *did_set_winhl(optset_T *args)
return NULL;
}
+int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_generic(args, get_highlight_name, numMatches, matches);
+}
+
// When 'syntax' is set, load the syntax of that name
static void do_syntax_autocmd(buf_T *buf, bool value_changed)
{
@@ -2084,11 +2727,12 @@ static void do_spelllang_source(win_T *win)
/// @param errbuf buffer for errors, or NULL
/// @param errbuflen length of errors buffer
/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param op OP_ADDING/OP_PREPENDING/OP_REMOVING
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
///
/// @return NULL for success, or an untranslated error message for an error
const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **varp, char *oldval,
- char *errbuf, size_t errbuflen, int opt_flags,
+ char *errbuf, size_t errbuflen, int opt_flags, set_op_T op,
bool *value_checked)
{
const char *errmsg = NULL;
@@ -2102,6 +2746,7 @@ const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **va
.os_varp = varp,
.os_idx = opt_idx,
.os_flags = opt_flags,
+ .os_op = op,
.os_oldval.string = oldval,
.os_newval.string = *varp,
.os_value_checked = false,
@@ -2324,15 +2969,17 @@ static const struct chars_tab fcs_tab[] = {
static lcs_chars_T lcs_chars;
static const struct chars_tab lcs_tab[] = {
- { &lcs_chars.eol, "eol", NUL, NUL },
- { &lcs_chars.ext, "extends", NUL, NUL },
- { &lcs_chars.nbsp, "nbsp", NUL, NUL },
- { &lcs_chars.prec, "precedes", NUL, NUL },
- { &lcs_chars.space, "space", NUL, NUL },
- { &lcs_chars.tab2, "tab", NUL, NUL },
- { &lcs_chars.lead, "lead", NUL, NUL },
- { &lcs_chars.trail, "trail", NUL, NUL },
- { &lcs_chars.conceal, "conceal", NUL, NUL },
+ { &lcs_chars.eol, "eol", NUL, NUL },
+ { &lcs_chars.ext, "extends", NUL, NUL },
+ { &lcs_chars.nbsp, "nbsp", NUL, NUL },
+ { &lcs_chars.prec, "precedes", NUL, NUL },
+ { &lcs_chars.space, "space", NUL, NUL },
+ { &lcs_chars.tab2, "tab", NUL, NUL },
+ { &lcs_chars.lead, "lead", NUL, NUL },
+ { &lcs_chars.trail, "trail", NUL, NUL },
+ { &lcs_chars.conceal, "conceal", NUL, NUL },
+ { NULL, "multispace", NUL, NUL },
+ { NULL, "leadmultispace", NUL, NUL },
};
/// Handle setting 'listchars' or 'fillchars'.
@@ -2403,54 +3050,13 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
int i;
for (i = 0; i < entries; i++) {
const size_t len = strlen(tab[i].name);
- if (strncmp(p, tab[i].name, len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
- const char *s = p + len + 1;
- int c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- int c2 = 0, c3 = 0;
- if (tab[i].cp == &lcs_chars.tab2) {
- if (*s == NUL) {
- return e_invarg;
- }
- c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || char2cells(c2) > 1) {
- return e_invarg;
- }
- if (!(*s == ',' || *s == NUL)) {
- c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || char2cells(c3) > 1) {
- return e_invarg;
- }
- }
- }
-
- if (*s == ',' || *s == NUL) {
- if (round > 0) {
- if (tab[i].cp == &lcs_chars.tab2) {
- lcs_chars.tab1 = c1;
- lcs_chars.tab2 = c2;
- lcs_chars.tab3 = c3;
- } else if (tab[i].cp != NULL) {
- *(tab[i].cp) = c1;
- }
- }
- p = s;
- break;
- }
+ if (!(strncmp(p, tab[i].name, len) == 0
+ && p[len] == ':'
+ && p[len + 1] != NUL)) {
+ continue;
}
- }
- if (i == entries) {
- const size_t len = strlen("multispace");
- const size_t len2 = strlen("leadmultispace");
- if (is_listchars
- && strncmp(p, "multispace", len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
+ if (is_listchars && strcmp(tab[i].name, "multispace") == 0) {
const char *s = p + len + 1;
if (round == 0) {
// Get length of lcs-multispace string in the first round
@@ -2478,11 +3084,11 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
}
p = s;
}
- } else if (is_listchars
- && strncmp(p, "leadmultispace", len2) == 0
- && p[len2] == ':'
- && p[len2 + 1] != NUL) {
- const char *s = p + len2 + 1;
+ break;
+ }
+
+ if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0) {
+ const char *s = p + len + 1;
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
@@ -2509,9 +3115,48 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
}
p = s;
}
- } else {
+ break;
+ }
+
+ const char *s = p + len + 1;
+ int c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
+ int c2 = 0, c3 = 0;
+ if (tab[i].cp == &lcs_chars.tab2) {
+ if (*s == NUL) {
+ return e_invarg;
+ }
+ c2 = get_encoded_char_adv(&s);
+ if (c2 == 0 || char2cells(c2) > 1) {
+ return e_invarg;
+ }
+ if (!(*s == ',' || *s == NUL)) {
+ c3 = get_encoded_char_adv(&s);
+ if (c3 == 0 || char2cells(c3) > 1) {
+ return e_invarg;
+ }
+ }
+ }
+
+ if (*s == ',' || *s == NUL) {
+ if (round > 0) {
+ if (tab[i].cp == &lcs_chars.tab2) {
+ lcs_chars.tab1 = c1;
+ lcs_chars.tab2 = c2;
+ lcs_chars.tab3 = c3;
+ } else if (tab[i].cp != NULL) {
+ *(tab[i].cp) = c1;
+ }
+ }
+ p = s;
+ break;
+ }
+ }
+
+ if (i == entries) {
+ return e_invarg;
}
if (*p == ',') {
@@ -2536,6 +3181,40 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
return NULL; // no error
}
+/// Handle the new value of 'fillchars'.
+const char *set_fillchars_option(win_T *wp, char *val, bool apply)
+{
+ return set_chars_option(wp, val, false, apply);
+}
+
+/// Handle the new value of 'listchars'.
+const char *set_listchars_option(win_T *wp, char *val, bool apply)
+{
+ return set_chars_option(wp, val, true, apply);
+}
+
+/// Function given to ExpandGeneric() to obtain possible arguments of the
+/// 'fillchars' option.
+char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= (int)ARRAY_SIZE(fcs_tab)) {
+ return NULL;
+ }
+
+ return (char *)fcs_tab[idx].name;
+}
+
+/// Function given to ExpandGeneric() to obtain possible arguments of the
+/// 'listchars' option.
+char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= (int)ARRAY_SIZE(lcs_tab)) {
+ return NULL;
+ }
+
+ return (char *)lcs_tab[idx].name;
+}
+
/// Check all global and local values of 'listchars' and 'fillchars'.
/// May set different defaults in case character widths change.
///
@@ -2558,15 +3237,3 @@ const char *check_chars_options(void)
}
return NULL;
}
-
-/// Handle the new value of 'fillchars'.
-const char *set_fillchars_option(win_T *wp, char *val, bool apply)
-{
- return set_chars_option(wp, val, false, apply);
-}
-
-/// Handle the new value of 'listchars'.
-const char *set_listchars_option(win_T *wp, char *val, bool apply)
-{
- return set_chars_option(wp, val, true, apply);
-}
diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h
index 3520cc2061..a481ed1d07 100644
--- a/src/nvim/optionstr.h
+++ b/src/nvim/optionstr.h
@@ -2,6 +2,7 @@
#define NVIM_OPTIONSTR_H
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/option_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index b03509a313..7de7168d62 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -17,7 +17,6 @@
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds_defs.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/log.h"
@@ -29,7 +28,6 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
#include "nvim/version.h"
#include "nvim/vim.h"
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 846219f720..119a42f074 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -282,9 +282,10 @@ static char writebuf[kRWBufferSize];
///
/// @param[in,out] rv RBuffer instance used.
/// @param[in,out] fp File to work with.
-static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
+static void file_rb_write_full_cb(RBuffer *const rv, void *const fp_in)
FUNC_ATTR_NONNULL_ALL
{
+ FileDescriptor *const fp = fp_in;
assert(fp->wr);
assert(rv->data == (void *)fp);
if (rbuffer_size(rv) == 0) {
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 476ede2046..2712b874bb 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -63,8 +63,6 @@
#ifdef HAVE_XATTR
static const char e_xattr_erange[]
= N_("E1506: Buffer too small to copy xattr value or key");
-static const char e_xattr_enotsup[]
- = N_("E1507: Extended attributes are not supported by the filesystem");
static const char e_xattr_e2big[]
= N_("E1508: Size of the extended attribute value is larger than the maximum size allowed");
static const char e_xattr_other[]
@@ -800,9 +798,9 @@ void os_copy_xattr(const char *from_file, const char *to_file)
case E2BIG:
errmsg = e_xattr_e2big;
goto error_exit;
- case ENOTSUP:
- errmsg = e_xattr_enotsup;
- goto error_exit;
+ case EACCES:
+ case EPERM:
+ break;
case ERANGE:
errmsg = e_xattr_erange;
goto error_exit;
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index 652b851903..c3958cb3f2 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -5,6 +5,7 @@
# define Boolean CFBoolean // Avoid conflict with API's Boolean
# define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo
# include <CoreServices/CoreServices.h>
+
# undef Boolean
# undef FileInfo
#endif
@@ -17,6 +18,7 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/garray.h"
@@ -30,7 +32,6 @@
#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/profile.h"
-#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/os/lang.h b/src/nvim/os/lang.h
index bb1ebfb721..ad64b38916 100644
--- a/src/nvim/os/lang.h
+++ b/src/nvim/os/lang.h
@@ -1,6 +1,7 @@
#ifndef NVIM_OS_LANG_H
#define NVIM_OS_LANG_H
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h
index a7496130cc..006dfbfc04 100644
--- a/src/nvim/os/os.h
+++ b/src/nvim/os/os.h
@@ -4,9 +4,12 @@
#include <stdbool.h>
#include <uv.h>
+#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
+#include "nvim/garray.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/stdpaths_defs.h"
-#include "nvim/vim.h"
+#include "nvim/types.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/env.h.generated.h"
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index 411ba91fa7..b23d2b7b13 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -10,10 +10,10 @@
#include "auto/config.h"
#include "nvim/ascii.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/garray.h"
#include "nvim/memory.h"
#include "nvim/os/os.h"
-#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef HAVE_PWD_FUNCS
# include <pwd.h>
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 21f0543756..5d991ce719 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -648,11 +648,13 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in
}
s = p + 1;
} else if (path_end >= path + wildoff
+#ifdef MSWIN
+ && vim_strchr("*?[~", (uint8_t)(*path_end)) != NULL
+#else
&& (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL
-#ifndef MSWIN
- || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end)))
+ || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end))))
#endif
- )) { // NOLINT(whitespace/parens)
+ ) { // NOLINT(whitespace/parens)
e = p;
}
len = (size_t)(utfc_ptr2len(path_end));
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index 3162a446c0..73ad534de7 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -11,6 +11,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
@@ -30,7 +31,6 @@
#include "nvim/profile.h"
#include "nvim/runtime.h"
#include "nvim/types.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "profile.c.generated.h"
diff --git a/src/nvim/profile.h b/src/nvim/profile.h
index 547d11185f..7450a0dfdc 100644
--- a/src/nvim/profile.h
+++ b/src/nvim/profile.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <time.h>
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/runtime.h"
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 12cf08636f..360088758d 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -20,6 +20,7 @@
#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index 7308ef5f75..8286a11f89 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -5,6 +5,7 @@
#include "klib/kvec.h"
#include "nvim/autocmd.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_eval_defs.h"
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 14d35d9b59..cd1d016c7b 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1564,15 +1564,14 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
break;
}
case kSDItemVariable: {
- if (entry.data.global_var.value.v_type == VAR_TYPE_BLOB) {
+ if (entry.data.global_var.value.v_type == VAR_BLOB) {
// Strings and Blobs both pack as msgpack BINs; differentiate them by
// storing an additional VAR_TYPE_BLOB element alongside Blobs
list_T *const list = tv_list_alloc(1);
tv_list_append_number(list, VAR_TYPE_BLOB);
entry.data.global_var.additional_elements = list;
}
- const size_t arr_size = 2 + (size_t)(
- tv_list_len(entry.data.global_var.additional_elements));
+ const size_t arr_size = 2 + (size_t)(tv_list_len(entry.data.global_var.additional_elements));
msgpack_pack_array(spacker, arr_size);
const String varname = cstr_as_string(entry.data.global_var.name);
PACK_BIN(varname);
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 96af14bfc6..5d18ed393a 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -16,6 +16,7 @@
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
diff --git a/src/nvim/sign.h b/src/nvim/sign.h
index ba84cd71a4..89d765bf38 100644
--- a/src/nvim/sign.h
+++ b/src/nvim/sign.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/sign_defs.h"
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index f1df2e56f3..25e08abb0e 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -452,24 +452,24 @@ struct wordnode_S {
// Info used while reading the spell files.
typedef struct spellinfo_S {
wordnode_T *si_foldroot; // tree with case-folded words
- long si_foldwcount; // nr of words in si_foldroot
+ int si_foldwcount; // nr of words in si_foldroot
wordnode_T *si_keeproot; // tree with keep-case words
- long si_keepwcount; // nr of words in si_keeproot
+ int si_keepwcount; // nr of words in si_keeproot
wordnode_T *si_prefroot; // tree with postponed prefixes
- long si_sugtree; // creating the soundfolding trie
+ int si_sugtree; // creating the soundfolding trie
sblock_T *si_blocks; // memory blocks used
- long si_blocks_cnt; // memory blocks allocated
+ int si_blocks_cnt; // memory blocks allocated
int si_did_emsg; // true when ran out of memory
- long si_compress_cnt; // words to add before lowering
- // compression limit
+ int si_compress_cnt; // words to add before lowering
+ // compression limit
wordnode_T *si_first_free; // List of nodes that have been freed during
// compression, linked by "wn_child" field.
- long si_free_count; // number of nodes in si_first_free
+ int si_free_count; // number of nodes in si_first_free
#ifdef SPELL_PRINTTREE
int si_wordnode_nr; // sequence nr for nodes
#endif
@@ -1874,24 +1874,24 @@ static void spell_reload_one(char *fname, bool added_word)
// Tunable parameters for when the tree is compressed. Filled from the
// 'mkspellmem' option.
-static long compress_start = 30000; // memory / SBLOCKSIZE
-static long compress_inc = 100; // memory / SBLOCKSIZE
-static long compress_added = 500000; // word count
+static int compress_start = 30000; // memory / SBLOCKSIZE
+static int compress_inc = 100; // memory / SBLOCKSIZE
+static int compress_added = 500000; // word count
// Check the 'mkspellmem' option. Return FAIL if it's wrong.
// Sets "sps_flags".
int spell_check_msm(void)
{
char *p = p_msm;
- long start = 0;
- long incr = 0;
- long added = 0;
+ int start = 0;
+ int incr = 0;
+ int added = 0;
if (!ascii_isdigit(*p)) {
return FAIL;
}
// block count = (value * 1024) / SBLOCKSIZE (but avoid overflow)
- start = (getdigits_long(&p, true, 0) * 10) / (SBLOCKSIZE / 102);
+ start = (getdigits_int(&p, true, 0) * 10) / (SBLOCKSIZE / 102);
if (*p != ',') {
return FAIL;
}
@@ -1899,7 +1899,7 @@ int spell_check_msm(void)
if (!ascii_isdigit(*p)) {
return FAIL;
}
- incr = (getdigits_long(&p, true, 0) * 102) / (SBLOCKSIZE / 10);
+ incr = (getdigits_int(&p, true, 0) * 102) / (SBLOCKSIZE / 10);
if (*p != ',') {
return FAIL;
}
@@ -1907,7 +1907,7 @@ int spell_check_msm(void)
if (!ascii_isdigit(*p)) {
return FAIL;
}
- added = getdigits_long(&p, true, 0) * 1024;
+ added = getdigits_int(&p, true, 0) * 1024;
if (*p != NUL) {
return FAIL;
}
@@ -3177,7 +3177,7 @@ static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile)
if (os_time() > last_msg_time) {
last_msg_time = os_time();
vim_snprintf(message, sizeof(message),
- _("line %6d, word %6ld - %s"),
+ _("line %6d, word %6d - %s"),
lnum, spin->si_foldwcount + spin->si_keepwcount, w);
msg_start();
msg_outtrans_long(message, 0);
@@ -3635,7 +3635,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_
static int spell_read_wordfile(spellinfo_T *spin, char *fname)
{
FILE *fd;
- long lnum = 0;
+ linenr_T lnum = 0;
char rline[MAXLINELEN];
char *line;
char *pc = NULL;
@@ -3682,7 +3682,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname)
if (spin->si_conv.vc_type != CONV_NONE) {
pc = string_convert(&spin->si_conv, rline, NULL);
if (pc == NULL) {
- smsg(0, _("Conversion failure for word in %s line %ld: %s"),
+ smsg(0, _("Conversion failure for word in %s line %" PRIdLINENR ": %s"),
fname, lnum, rline);
continue;
}
@@ -3696,10 +3696,10 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname)
line++;
if (strncmp(line, "encoding=", 9) == 0) {
if (spin->si_conv.vc_type != CONV_NONE) {
- smsg(0, _("Duplicate /encoding= line ignored in %s line %ld: %s"),
+ smsg(0, _("Duplicate /encoding= line ignored in %s line %" PRIdLINENR ": %s"),
fname, lnum, line - 1);
} else if (did_word) {
- smsg(0, _("/encoding= line after word ignored in %s line %ld: %s"),
+ smsg(0, _("/encoding= line after word ignored in %s line %" PRIdLINENR ": %s"),
fname, lnum, line - 1);
} else {
char *enc;
@@ -3720,12 +3720,12 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname)
if (strncmp(line, "regions=", 8) == 0) {
if (spin->si_region_count > 1) {
- smsg(0, _("Duplicate /regions= line ignored in %s line %ld: %s"),
+ smsg(0, _("Duplicate /regions= line ignored in %s line %" PRIdLINENR ": %s"),
fname, lnum, line);
} else {
line += 8;
if (strlen(line) > MAXREGIONS * 2) {
- smsg(0, _("Too many regions in %s line %ld: %s"),
+ smsg(0, _("Too many regions in %s line %" PRIdLINENR ": %s"),
fname, lnum, line);
} else {
spin->si_region_count = (int)strlen(line) / 2;
@@ -3738,7 +3738,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname)
continue;
}
- smsg(0, _("/ line ignored in %s line %ld: %s"),
+ smsg(0, _("/ line ignored in %s line %" PRIdLINENR ": %s"),
fname, lnum, line - 1);
continue;
}
@@ -3765,13 +3765,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname)
l = (uint8_t)(*p) - '0';
if (l == 0 || l > spin->si_region_count) {
- smsg(0, _("Invalid region nr in %s line %ld: %s"),
+ smsg(0, _("Invalid region nr in %s line %" PRIdLINENR ": %s"),
fname, lnum, p);
break;
}
regionmask |= 1 << (l - 1);
} else {
- smsg(0, _("Unrecognized flags in %s line %ld: %s"),
+ smsg(0, _("Unrecognized flags in %s line %" PRIdLINENR ": %s"),
fname, lnum, p);
break;
}
@@ -4168,7 +4168,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
FUNC_ATTR_NONNULL_ALL
{
hashtab_T ht;
- long tot = 0;
+ int tot = 0;
long perc;
// Skip the root itself, it's not actually used. The first sibling is the
@@ -4178,7 +4178,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
}
hash_init(&ht);
- const long n = node_compress(spin, root->wn_sibling, &ht, &tot);
+ const int n = node_compress(spin, root->wn_sibling, &ht, &tot);
#ifndef SPELL_PRINTTREE
if (spin->si_verbose || p_verbose > 2)
@@ -4192,7 +4192,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
perc = (tot - n) * 100 / tot;
}
vim_snprintf(IObuff, IOSIZE,
- _("Compressed %s of %ld nodes; %ld (%ld%%) remaining"),
+ _("Compressed %s of %d nodes; %d (%ld%%) remaining"),
name, tot, tot - n, perc);
spell_message(spin, IObuff);
}
@@ -4206,7 +4206,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
/// Returns the number of compressed nodes.
///
/// @param tot total count of nodes before compressing, incremented while going through the tree
-static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, long *tot)
+static int node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, int *tot)
FUNC_ATTR_NONNULL_ALL
{
wordnode_T *np;
@@ -4216,7 +4216,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
hashitem_T *hi;
long len = 0;
unsigned nr, n;
- long compressed = 0;
+ int compressed = 0;
// Go through the list of siblings. Compress each child and then try
// finding an identical child to replace it.
@@ -4262,7 +4262,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
}
}
}
- *tot += len + 1; // add one for the node that stores the length
+ *tot += (int)len + 1; // add one for the node that stores the length
// Make a hash key for the node and its siblings, so that we can quickly
// find a lookalike node. This must be done after compressing the sibling
@@ -5559,15 +5559,15 @@ void spell_add_word(char *word, int len, SpellAddType what, int idx, bool undo)
}
if (what == SPELL_ADD_BAD || undo) {
- long fpos_next = 0;
- long fpos = 0;
+ int fpos_next = 0;
+ int fpos = 0;
// When the word appears as good word we need to remove that one,
// since its flags sort before the one with WF_BANNED.
fd = os_fopen(fname, "r");
if (fd != NULL) {
while (!vim_fgets(line, MAXWLEN * 2, fd)) {
fpos = fpos_next;
- fpos_next = ftell(fd);
+ fpos_next = (int)ftell(fd);
if (fpos_next < 0) {
break; // should never happen
}
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 2ee93b4934..7b92e69821 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -403,6 +403,7 @@ int spell_check_sps(void)
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
+ // Note: Keep this in sync with p_sps_values.
} else if (strcmp(buf, "best") == 0) {
f = SPS_BEST;
} else if (strcmp(buf, "fast") == 0) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9b238e6ff9..6bb841b1f8 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -15,6 +15,7 @@
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
@@ -3554,7 +3555,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
msg_putchar(','); // Separate with commas.
}
msg_puts(spo_name_tab[i]);
- const long n = spp->sp_offsets[i];
+ const int n = spp->sp_offsets[i];
if (i != SPO_LC_OFF) {
if (spp->sp_off_flags & mask) {
msg_putchar('s');
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index 0a63392a04..c56624bc37 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/globals.h"
#include "nvim/macros.h"
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 636d16d4e6..674d22ba44 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -56,7 +56,6 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 0de62440f9..13e51b9a9e 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -822,7 +822,7 @@ void op_format(oparg_T *oap, bool keep_cursor)
saved_cursor = oap->cursor_start;
}
- format_lines((linenr_T)oap->line_count, keep_cursor);
+ format_lines(oap->line_count, keep_cursor);
// Leave the cursor at the first non-blank of the last formatted line.
// If the cursor was moved one line back (e.g. with "Q}") go to the next
diff --git a/src/nvim/types.h b/src/nvim/types.h
index c2c815a0bc..7b23fe419b 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -31,8 +31,6 @@ typedef union {
typedef handle_T NS;
-typedef struct expand expand_T;
-
typedef uint64_t proftime_T;
typedef enum {
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 5396bfcda2..758ee036b4 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -260,10 +260,10 @@ int u_save_buf(buf_T *buf, linenr_T top, linenr_T bot)
}
if (top + 2 == bot) {
- u_saveline(buf, (linenr_T)(top + 1));
+ u_saveline(buf, top + 1);
}
- return u_savecommon(buf, top, bot, (linenr_T)0, false);
+ return u_savecommon(buf, top, bot, 0, false);
}
/// Save the line "lnum" (used by ":s" and "~" command).
@@ -289,9 +289,9 @@ int u_inssub(linenr_T lnum)
/// becomes empty.
/// Careful: may trigger autocommands that reload the buffer.
/// Returns FAIL when lines could not be saved, OK otherwise.
-int u_savedel(linenr_T lnum, long nlines)
+int u_savedel(linenr_T lnum, linenr_T nlines)
{
- return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines,
+ return u_savecommon(curbuf, lnum - 1, lnum + nlines,
nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false);
}
@@ -378,7 +378,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
u_entry_T *uep;
u_entry_T *prev_uep;
- long size = bot - top - 1;
+ linenr_T size = bot - top - 1;
// If curbuf->b_u_synced == true make a new header.
if (buf->b_u_synced) {
@@ -1488,7 +1488,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT
time_t seq_time = undo_read_time(&bi);
// Optional header fields.
- long last_save_nr = 0;
+ int last_save_nr = 0;
while (true) {
int len = undo_read_byte(&bi);
@@ -1519,7 +1519,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT
}
}
- long num_read_uhps = 0;
+ int num_read_uhps = 0;
int c;
while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) {
@@ -1655,7 +1655,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT
error:
xfree(line_ptr);
if (uhp_table != NULL) {
- for (long i = 0; i < num_read_uhps; i++) {
+ for (int i = 0; i < num_read_uhps; i++) {
if (uhp_table[i] != NULL) {
u_free_uhp(uhp_table[i]);
}
@@ -1920,7 +1920,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
// When "file" is true use "step" as a number of file writes.
// When "absolute" is true use "step" as the sequence number to jump to.
// "sec" must be false then.
-void undo_time(long step, bool sec, bool file, bool absolute)
+void undo_time(int step, bool sec, bool file, bool absolute)
{
if (text_locked()) {
text_locked_msg();
@@ -1938,8 +1938,8 @@ void undo_time(long step, bool sec, bool file, bool absolute)
u_oldcount = -1;
}
- long target;
- long closest;
+ int target;
+ int closest;
u_header_T *uhp = NULL;
bool dosec = sec;
bool dofile = file;
@@ -1953,7 +1953,7 @@ void undo_time(long step, bool sec, bool file, bool absolute)
closest = -1;
} else {
if (dosec) {
- target = (long)(curbuf->b_u_time_cur) + step;
+ target = (int)curbuf->b_u_time_cur + step;
} else if (dofile) {
if (step < 0) {
// Going back to a previous write. If there were changes after
@@ -1998,7 +1998,7 @@ void undo_time(long step, bool sec, bool file, bool absolute)
closest = -1;
} else {
if (dosec) {
- closest = (long)(os_time() + 1);
+ closest = (int)(os_time() + 1);
} else if (dofile) {
closest = curbuf->b_u_save_nr_last + 2;
} else {
@@ -2010,7 +2010,7 @@ void undo_time(long step, bool sec, bool file, bool absolute)
}
}
long closest_start = closest;
- long closest_seq = curbuf->b_u_seq_cur;
+ int closest_seq = curbuf->b_u_seq_cur;
int mark;
int nomark = 0; // shut up compiler
@@ -2042,8 +2042,8 @@ void undo_time(long step, bool sec, bool file, bool absolute)
while (uhp != NULL) {
uhp->uh_walk = mark;
- long val = dosec ? (long)(uhp->uh_time) :
- dofile ? uhp->uh_save_nr
+ int val = dosec ? (int)(uhp->uh_time) :
+ dofile ? uhp->uh_save_nr
: uhp->uh_seq;
if (round == 1 && !(dofile && val == 0)) {
@@ -2298,7 +2298,7 @@ static void u_undoredo(int undo, bool do_buf_event)
}
linenr_T oldsize = bot - top - 1; // number of lines before undo
- linenr_T newsize = (linenr_T)uep->ue_size; // number of lines after undo
+ linenr_T newsize = uep->ue_size; // number of lines after undo
if (top < newlnum) {
// If the saved cursor is somewhere in this undo block, move it to
@@ -2676,13 +2676,13 @@ void ex_undolist(exarg_T *eap)
while (uhp != NULL) {
if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
&& uhp->uh_walk != mark) {
- vim_snprintf(IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes);
+ vim_snprintf(IObuff, IOSIZE, "%6d %7d ", uhp->uh_seq, changes);
undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time);
if (uhp->uh_save_nr > 0) {
while (strlen(IObuff) < 33) {
xstrlcat(IObuff, " ", IOSIZE);
}
- vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr);
+ vim_snprintf_add(IObuff, IOSIZE, " %3d", uhp->uh_save_nr);
}
GA_APPEND(char *, &ga, xstrdup(IObuff));
}
@@ -2850,7 +2850,7 @@ static void u_getbot(buf_T *buf)
// inserted (0 - deleted) since calling u_save. This is equal to the
// old line count subtracted from the current line count.
linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount;
- uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra;
+ uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) {
iemsg(_(e_undo_line_missing));
uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index 9cc2e4a52b..aa7d6e3355 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -26,7 +26,7 @@ struct u_entry {
linenr_T ue_bot; // number of line below undo block
linenr_T ue_lcount; // linecount when u_save called
char **ue_array; // array of lines in undo block
- long ue_size; // number of lines in ue_array
+ linenr_T ue_size; // number of lines in ue_array
#ifdef U_DEBUG
int ue_magic; // magic number to check allocation
#endif
@@ -51,19 +51,19 @@ struct u_header {
u_header_T *ptr; // pointer to previous header for alt. redo
long seq;
} uh_alt_prev;
- long uh_seq; // sequence number, higher == newer undo
+ int uh_seq; // sequence number, higher == newer undo
int uh_walk; // used by undo_time()
- u_entry_T *uh_entry; // pointer to first entry
+ u_entry_T *uh_entry; // pointer to first entry
u_entry_T *uh_getbot_entry; // pointer to where ue_bot must be set
pos_T uh_cursor; // cursor position before saving
colnr_T uh_cursor_vcol;
- int uh_flags; // see below
- fmark_T uh_namedm[NMARKS]; // marks before undo/after redo
+ int uh_flags; // see below
+ fmark_T uh_namedm[NMARKS]; // marks before undo/after redo
extmark_undo_vec_t uh_extmark; // info to move extmarks
- visualinfo_T uh_visual; // Visual areas before undo/after redo
- time_t uh_time; // timestamp when the change was made
- long uh_save_nr; // set when the file was saved after the
- // changes in this block
+ visualinfo_T uh_visual; // Visual areas before undo/after redo
+ time_t uh_time; // timestamp when the change was made
+ int uh_save_nr; // set when the file was saved after the
+ // changes in this block
#ifdef U_DEBUG
int uh_magic; // magic number to check allocation
#endif
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 131b1ce3ef..4bab3f52cd 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -16,6 +16,7 @@
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h
index 0d9838abf2..34f1439b10 100644
--- a/src/nvim/usercmd.h
+++ b/src/nvim/usercmd.h
@@ -3,6 +3,7 @@
#include <stdint.h>
+#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/garray.h"
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index fc1f15b285..22db3751cd 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -96,83 +96,10 @@ typedef enum {
#define FAIL 0
#define NOTDONE 2 // not OK or FAIL but skipped
-// Type values for type().
-#define VAR_TYPE_NUMBER 0
-#define VAR_TYPE_STRING 1
-#define VAR_TYPE_FUNC 2
-#define VAR_TYPE_LIST 3
-#define VAR_TYPE_DICT 4
-#define VAR_TYPE_FLOAT 5
-#define VAR_TYPE_BOOL 6
-#define VAR_TYPE_SPECIAL 7
-#define VAR_TYPE_BLOB 10
-
-// values for xp_context when doing command line completion
-
-enum {
- EXPAND_UNSUCCESSFUL = -2,
- EXPAND_OK = -1,
- EXPAND_NOTHING = 0,
- EXPAND_COMMANDS,
- EXPAND_FILES,
- EXPAND_DIRECTORIES,
- EXPAND_SETTINGS,
- EXPAND_BOOL_SETTINGS,
- EXPAND_TAGS,
- EXPAND_OLD_SETTING,
- EXPAND_HELP,
- EXPAND_BUFFERS,
- EXPAND_EVENTS,
- EXPAND_MENUS,
- EXPAND_SYNTAX,
- EXPAND_HIGHLIGHT,
- EXPAND_AUGROUP,
- EXPAND_USER_VARS,
- EXPAND_MAPPINGS,
- EXPAND_TAGS_LISTFILES,
- EXPAND_FUNCTIONS,
- EXPAND_USER_FUNC,
- EXPAND_EXPRESSION,
- EXPAND_MENUNAMES,
- EXPAND_USER_COMMANDS,
- EXPAND_USER_CMD_FLAGS,
- EXPAND_USER_NARGS,
- EXPAND_USER_COMPLETE,
- EXPAND_ENV_VARS,
- EXPAND_LANGUAGE,
- EXPAND_COLORS,
- EXPAND_COMPILER,
- EXPAND_USER_DEFINED,
- EXPAND_USER_LIST,
- EXPAND_USER_LUA,
- EXPAND_SHELLCMD,
- EXPAND_SIGN,
- EXPAND_PROFILE,
- EXPAND_FILETYPE,
- EXPAND_FILES_IN_PATH,
- EXPAND_OWNSYNTAX,
- EXPAND_LOCALES,
- EXPAND_HISTORY,
- EXPAND_USER,
- EXPAND_SYNTIME,
- EXPAND_USER_ADDR_TYPE,
- EXPAND_PACKADD,
- EXPAND_MESSAGES,
- EXPAND_MAPCLEAR,
- EXPAND_ARGLIST,
- EXPAND_DIFF_BUFFERS,
- EXPAND_BREAKPOINT,
- EXPAND_SCRIPTNAMES,
- EXPAND_RUNTIME,
- EXPAND_CHECKHEALTH,
- EXPAND_LUA,
-};
-
// Minimal size for block 0 of a swap file.
// NOTE: This depends on size of struct block0! It's not done with a sizeof(),
// because struct block0 is defined in memline.c (Sorry).
// The maximal block size is arbitrary.
-
#define MIN_SWAP_PAGE_SIZE 1048
#define MAX_SWAP_PAGE_SIZE 50000
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 09495fbaac..f28d6ea869 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -2869,6 +2869,18 @@ describe('API', function()
eq(false, eval('g:fired'))
end)
+ it('TextChanged and TextChangedI do not trigger without changes', function()
+ local buf = meths.create_buf(true, false)
+ command([[let g:changed = '']])
+ meths.create_autocmd({'TextChanged', 'TextChangedI'}, {
+ buffer = buf,
+ command = 'let g:changed ..= mode()',
+ })
+ meths.set_current_buf(buf)
+ feed('i')
+ eq('', meths.get_var('changed'))
+ end)
+
it('scratch-buffer', function()
eq({id=2}, meths.create_buf(false, true))
eq({id=3}, meths.create_buf(true, true))
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index f2b360f0d8..a23dad226a 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -40,6 +40,7 @@ describe('fileio', function()
os.remove('Xtest_startup_file1')
os.remove('Xtest_startup_file1~')
os.remove('Xtest_startup_file2')
+ os.remove('Xtest_startup_file2~')
os.remove('Xtest_тест.md')
os.remove('Xtest-u8-int-max')
os.remove('Xtest-overwrite-forced')
@@ -136,7 +137,7 @@ describe('fileio', function()
it('backup with full path with spaces', function()
skip(is_ci('cirrus'))
clear()
- mkdir('Xtest_backup with spaces')
+ mkdir('Xtest_backupdir with spaces')
command('set backup')
command('set backupdir=Xtest_backupdir\\ with\\ spaces//')
command('write Xtest_startup_file1')
@@ -258,8 +259,8 @@ describe('fileio', function()
screen:expect([[
{2:WARNING: The file has been changed since}|
{2: reading it!!!} |
- {3:Do you really want to write to it (y/n)^?}|
- |
+ {3:Do you really want to write to it (y/n)?}|
+ ^ |
]])
feed("n")
diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua
index a786887bbd..6c677d76d1 100644
--- a/test/functional/core/path_spec.lua
+++ b/test/functional/core/path_spec.lua
@@ -6,16 +6,19 @@ local command = helpers.command
local insert = helpers.insert
local feed = helpers.feed
local is_os = helpers.is_os
+local mkdir = helpers.mkdir
+local rmdir = helpers.rmdir
+local write_file = helpers.write_file
+
+local function join_path(...)
+ local pathsep = (is_os('win') and '\\' or '/')
+ return table.concat({...}, pathsep)
+end
describe('path collapse', function()
local targetdir
local expected_path
- local function join_path(...)
- local pathsep = (is_os('win') and '\\' or '/')
- return table.concat({...}, pathsep)
- end
-
before_each(function()
targetdir = join_path('test', 'functional', 'fixtures')
clear()
@@ -57,6 +60,27 @@ describe('path collapse', function()
end)
end)
+describe('expand wildcard', function()
+ before_each(clear)
+
+ it('with special characters #24421', function()
+ local folders = is_os('win') and {
+ '{folder}',
+ 'folder$name'
+ } or {
+ 'folder-name',
+ 'folder#name'
+ }
+ for _, folder in ipairs(folders) do
+ mkdir(folder)
+ local file = join_path(folder, 'file.txt')
+ write_file(file, '')
+ eq(file, eval('expand("'..folder..'/*")'))
+ rmdir(folder)
+ end
+ end)
+end)
+
describe('file search', function()
before_each(clear)
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index 8c299636cc..ea3397d50d 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -941,6 +941,15 @@ describe('completion', function()
end)
end)
+ it('cmdline completion supports various string options', function()
+ eq('auto', funcs.getcompletion('set foldcolumn=', 'cmdline')[2])
+ eq({'nosplit', 'split'}, funcs.getcompletion('set inccommand=', 'cmdline'))
+ eq({'ver:3,hor:6', 'hor:', 'ver:'}, funcs.getcompletion('set mousescroll=', 'cmdline'))
+ eq('BS', funcs.getcompletion('set termpastefilter=', 'cmdline')[2])
+ eq('SpecialKey', funcs.getcompletion('set winhighlight=', 'cmdline')[1])
+ eq('SpecialKey', funcs.getcompletion('set winhighlight=NonText:', 'cmdline')[1])
+ end)
+
describe('from the commandline window', function()
it('is cleared after CTRL-C', function ()
feed('q:')
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 3e0f6d1fcc..436873b464 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -171,6 +171,7 @@ describe('swapfile detection', function()
local screen2 = Screen.new(256, 40)
screen2:attach()
exec(init)
+ command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
-- With shortmess+=F
command('set shortmess+=F')
@@ -219,11 +220,29 @@ describe('swapfile detection', function()
nvim2:close()
end)
+ it('default SwapExists handler selects "(E)dit" and skips prompt', function()
+ exec(init)
+ command('edit Xfile1')
+ command("put ='some text...'")
+ command('preserve') -- Make sure the swap file exists.
+ local nvimpid = funcs.getpid()
+
+ local nvim1 = spawn(new_argv(), true, nil, true)
+ set_session(nvim1)
+ local screen = Screen.new(75, 18)
+ screen:attach()
+ exec(init)
+ feed(':edit Xfile1\n')
+
+ screen:expect({ any = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid) })
+ nvim1:close()
+ end)
+
-- oldtest: Test_swap_prompt_splitwin()
it('selecting "q" in the attention prompt', function()
exec(init)
command('edit Xfile1')
- command('preserve') -- should help to make sure the swap file exists
+ command('preserve') -- Make sure the swap file exists.
local screen = Screen.new(75, 18)
screen:set_default_attr_ids({
@@ -235,7 +254,9 @@ describe('swapfile detection', function()
set_session(nvim1)
screen:attach()
exec(init)
+ command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
feed(':split Xfile1\n')
+ -- The default SwapExists handler does _not_ skip this prompt.
screen:expect({
any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
})
@@ -267,6 +288,7 @@ describe('swapfile detection', function()
set_session(nvim2)
screen:attach()
exec(init)
+ command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
command('set more')
command('au bufadd * let foo_w = wincol()')
feed(':e Xfile1<CR>')
@@ -300,8 +322,9 @@ describe('swapfile detection', function()
nvim2:close()
end)
- -- oldtest: Test_nocatch_process_still_running()
- it('allows deleting swapfile created before boot vim-patch:8.2.2586', function()
+ --- @param swapexists boolean Enable the default SwapExists handler.
+ --- @param on_swapfile_running fun(screen: any) Called after swapfile ("STILL RUNNING") prompt.
+ local function test_swapfile_after_reboot(swapexists, on_swapfile_running)
local screen = Screen.new(75, 30)
screen:set_default_attr_ids({
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
@@ -311,6 +334,9 @@ describe('swapfile detection', function()
screen:attach()
exec(init)
+ if not swapexists then
+ command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
+ end
command('set nohidden')
exec([=[
@@ -347,12 +373,7 @@ describe('swapfile detection', function()
os.rename('Xswap', swname)
feed(':edit Xswaptest<CR>')
- screen:expect({any = table.concat({
- pesc('{2:E325: ATTENTION}'),
- 'file name: .*Xswaptest',
- 'process ID: %d* %(STILL RUNNING%)',
- pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
- }, '.*')})
+ on_swapfile_running(screen)
feed('e')
@@ -374,7 +395,26 @@ describe('swapfile detection', function()
}, '.*')})
feed('e')
+ end
+
+ -- oldtest: Test_nocatch_process_still_running()
+ it('swapfile created before boot vim-patch:8.2.2586', function()
+ test_swapfile_after_reboot(false, function(screen)
+ screen:expect({any = table.concat({
+ pesc('{2:E325: ATTENTION}'),
+ 'file name: .*Xswaptest',
+ 'process ID: %d* %(STILL RUNNING%)',
+ pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
+ }, '.*')})
+ end)
+ end)
+
+ it('swapfile created before boot + default SwapExists handler', function()
+ test_swapfile_after_reboot(true, function(screen)
+ screen:expect({ any = 'W325: Ignoring swapfile from Nvim process' })
+ end)
end)
+
end)
describe('quitting swapfile dialog on startup stops TUI properly', function()
diff --git a/test/functional/legacy/digraph_spec.lua b/test/functional/legacy/digraph_spec.lua
index 0cb0bb84be..7eeb83eb5f 100644
--- a/test/functional/legacy/digraph_spec.lua
+++ b/test/functional/legacy/digraph_spec.lua
@@ -22,7 +22,7 @@ describe('digraph', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
feed('1')
screen:expect([[
@@ -31,7 +31,7 @@ describe('digraph', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
feed('2')
screen:expect([[
@@ -40,7 +40,7 @@ describe('digraph', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
end)
end)
diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua
index 186bf395cc..939999e21b 100644
--- a/test/functional/legacy/edit_spec.lua
+++ b/test/functional/legacy/edit_spec.lua
@@ -43,7 +43,7 @@ describe('edit', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
feed('=')
screen:expect([[
diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua
index 7903885420..13df861b91 100644
--- a/test/functional/plugin/lsp/snippet_spec.lua
+++ b/test/functional/plugin/lsp/snippet_spec.lua
@@ -1,130 +1,70 @@
local helpers = require('test.functional.helpers')(after_each)
-local snippet = require('vim.lsp._snippet')
+local snippet = require('vim.lsp._snippet_grammar')
local eq = helpers.eq
local exec_lua = helpers.exec_lua
-describe('vim.lsp._snippet', function()
+describe('vim.lsp._snippet_grammar', function()
before_each(helpers.clear)
after_each(helpers.clear)
local parse = function(...)
- return exec_lua('return require("vim.lsp._snippet").parse(...)', ...)
+ local res = exec_lua('return require("vim.lsp._snippet_grammar").parse(...)', ...)
+ return res.data.children
end
- it('should parse only text', function()
+ it('parses only text', function()
eq({
- type = snippet.NodeType.SNIPPET,
- children = {
- {
- type = snippet.NodeType.TEXT,
- raw = 'TE\\$\\}XT',
- esc = 'TE$}XT',
- },
- },
+ { type = snippet.NodeType.Text, data = { text = 'TE$}XT' } },
}, parse('TE\\$\\}XT'))
end)
- it('should parse tabstop', function()
+ it('parses tabstops', function()
eq({
- type = snippet.NodeType.SNIPPET,
- children = {
- {
- type = snippet.NodeType.TABSTOP,
- tabstop = 1,
- },
- {
- type = snippet.NodeType.TABSTOP,
- tabstop = 2,
- },
- },
+ { type = snippet.NodeType.Tabstop, data = { tabstop = 1 } },
+ { type = snippet.NodeType.Tabstop, data = { tabstop = 2 } },
}, parse('$1${2}'))
end)
- it('should parse placeholders', function()
+ it('parses nested placeholders', function()
eq({
- type = snippet.NodeType.SNIPPET,
- children = {
- {
- type = snippet.NodeType.PLACEHOLDER,
+ {
+ type = snippet.NodeType.Placeholder,
+ data = {
tabstop = 1,
- children = {
- {
- type = snippet.NodeType.PLACEHOLDER,
+ value = {
+ type = snippet.NodeType.Placeholder,
+ data = {
tabstop = 2,
- children = {
- {
- type = snippet.NodeType.TEXT,
- raw = 'TE\\$\\}XT',
- esc = 'TE$}XT',
- },
- {
- type = snippet.NodeType.TABSTOP,
- tabstop = 3,
- },
- {
- type = snippet.NodeType.TABSTOP,
- tabstop = 1,
- transform = {
- type = snippet.NodeType.TRANSFORM,
- pattern = 'regex',
- option = 'i',
- format = {
- {
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- modifier = 'upcase',
- },
- },
- },
- },
- {
- type = snippet.NodeType.TEXT,
- raw = 'TE\\$\\}XT',
- esc = 'TE$}XT',
- },
- },
+ value = { type = snippet.NodeType.Tabstop, data = { tabstop = 3 } },
},
},
},
},
- }, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}'))
+ }, parse('${1:${2:${3}}}'))
end)
- it('should parse variables', function()
+ it('parses variables', function()
eq({
- type = snippet.NodeType.SNIPPET,
- children = {
- {
- type = snippet.NodeType.VARIABLE,
- name = 'VAR',
- },
- {
- type = snippet.NodeType.VARIABLE,
+ { type = snippet.NodeType.Variable, data = { name = 'VAR' } },
+ { type = snippet.NodeType.Variable, data = { name = 'VAR' } },
+ {
+ type = snippet.NodeType.Variable,
+ data = {
name = 'VAR',
+ default = { type = snippet.NodeType.Tabstop, data = { tabstop = 1 } },
},
- {
- type = snippet.NodeType.VARIABLE,
+ },
+ {
+ type = snippet.NodeType.Variable,
+ data = {
name = 'VAR',
- children = {
+ regex = 'regex',
+ options = '',
+ format = {
{
- type = snippet.NodeType.TABSTOP,
- tabstop = 1,
- },
- },
- },
- {
- type = snippet.NodeType.VARIABLE,
- name = 'VAR',
- transform = {
- type = snippet.NodeType.TRANSFORM,
- pattern = 'regex',
- format = {
- {
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- modifier = 'upcase',
- },
+ type = snippet.NodeType.Format,
+ data = { capture = 1, modifier = 'upcase' },
},
},
},
@@ -132,105 +72,82 @@ describe('vim.lsp._snippet', function()
}, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}'))
end)
- it('should parse choice', function()
+ it('parses choice', function()
eq({
- type = snippet.NodeType.SNIPPET,
- children = {
- {
- type = snippet.NodeType.CHOICE,
- tabstop = 1,
- items = {
- ',',
- '|',
- },
- },
+ {
+ type = snippet.NodeType.Choice,
+ data = { tabstop = 1, values = { ',', '|' } },
},
}, parse('${1|\\,,\\||}'))
end)
- it('should parse format', function()
- eq({
- type = snippet.NodeType.SNIPPET,
- children = {
+ it('parses format', function()
+ eq(
+ {
{
- type = snippet.NodeType.VARIABLE,
- name = 'VAR',
- transform = {
- type = snippet.NodeType.TRANSFORM,
- pattern = 'regex',
+ type = snippet.NodeType.Variable,
+ data = {
+ name = 'VAR',
+ regex = 'regex',
+ options = '',
format = {
{
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- modifier = 'upcase',
+ type = snippet.NodeType.Format,
+ data = { capture = 1, modifier = 'upcase' },
},
{
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- if_text = 'if_text',
- else_text = '',
+ type = snippet.NodeType.Format,
+ data = { capture = 1, if_text = 'if_text' },
},
{
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- if_text = '',
- else_text = 'else_text',
+ type = snippet.NodeType.Format,
+ data = { capture = 1, else_text = 'else_text' },
},
{
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- else_text = 'else_text',
- if_text = 'if_text',
+ type = snippet.NodeType.Format,
+ data = { capture = 1, if_text = 'if_text', else_text = 'else_text' },
},
{
- type = snippet.NodeType.FORMAT,
- capture_index = 1,
- if_text = '',
- else_text = 'else_text',
+ type = snippet.NodeType.Format,
+ data = { capture = 1, else_text = 'else_text' },
},
},
},
},
},
- }, parse('${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}'))
+ parse(
+ '${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}'
+ )
+ )
end)
- it('should parse empty strings', function()
+ it('parses empty strings', function()
eq({
- children = {
- {
- children = { {
- esc = '',
- raw = '',
- type = 7,
- } },
+ {
+ type = snippet.NodeType.Placeholder,
+ data = {
tabstop = 1,
- type = 2,
- },
- {
- esc = ' ',
- raw = ' ',
- type = 7,
+ value = { type = snippet.NodeType.Text, data = { text = '' } },
},
- {
+ },
+ {
+ type = snippet.NodeType.Text,
+ data = { text = ' ' },
+ },
+ {
+ type = snippet.NodeType.Variable,
+ data = {
name = 'VAR',
- transform = {
- format = {
- {
- capture_index = 1,
- else_text = '',
- if_text = '',
- type = 6,
- },
+ regex = 'erg',
+ format = {
+ {
+ type = snippet.NodeType.Format,
+ data = { capture = 1, if_text = '' },
},
- option = 'g',
- pattern = 'erg',
- type = 5,
},
- type = 3,
+ options = 'g',
},
},
- type = 0,
- }, parse('${1:} ${VAR/erg/${1:?:}/g}'))
+ }, parse('${1:} ${VAR/erg/${1:+}/g}'))
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 155c9ad96c..73e05d8d11 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -2301,7 +2301,7 @@ describe('LSP', function()
{ label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} },
{ label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} },
-- nested snippet tokens
- { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} },
+ { label='foocar', sortText="i", insertText='foodar(${1:${2|typ1,typ2|}}) {$0\\}', insertTextFormat=2, textEdit={} },
-- braced tabstop
{ label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} },
-- plain text
@@ -2317,7 +2317,7 @@ describe('LSP', function()
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } },
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } },
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
- { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(var1 typ2 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(typ1) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:${2|typ1,typ2|}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar()', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} } } } } },
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="k", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } },
}
@@ -3483,6 +3483,50 @@ describe('LSP', function()
end
}
end)
+ it("Fallback to command execution on resolve error", function()
+ clear()
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local server = _create_server({
+ capabilities = {
+ executeCommandProvider = {
+ commands = {"command:1"},
+ },
+ codeActionProvider = {
+ resolveProvider = true
+ }
+ },
+ handlers = {
+ ["textDocument/codeAction"] = function()
+ return {
+ {
+ title = "Code Action 1",
+ command = {
+ title = "Command 1",
+ command = "command:1",
+ }
+ }
+ }
+ end,
+ ["codeAction/resolve"] = function()
+ return nil, "resolve failed"
+ end,
+ }
+ })
+
+ local client_id = vim.lsp.start({
+ name = "dummy",
+ cmd = server.cmd,
+ })
+
+ vim.lsp.buf.code_action({ apply = true })
+ vim.lsp.stop_client(client_id)
+ return server.messages
+ ]])
+ eq("codeAction/resolve", result[4].method)
+ eq("workspace/executeCommand", result[5].method)
+ eq("command:1", result[5].params.command)
+ end)
end)
describe('vim.lsp.commands', function()
it('Accepts only string keys', function()
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
index f90e4f7e9d..39fc2c2562 100644
--- a/test/functional/terminal/window_spec.lua
+++ b/test/functional/terminal/window_spec.lua
@@ -44,7 +44,7 @@ describe(':terminal window', function()
{7:6 } |
{3:-- TERMINAL --} |
]])
- feed_data({'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
+ feed_data('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
screen:expect([[
{7:1 }tty ready |
{7:2 }rows: 6, cols: 48 |
@@ -55,8 +55,6 @@ describe(':terminal window', function()
{3:-- TERMINAL --} |
]])
- skip(is_os('win'), 'win: :terminal resize is unreliable #7007')
-
-- numberwidth=9
feed([[<C-\><C-N>]])
feed([[:set numberwidth=9 number<CR>i]])
@@ -69,7 +67,7 @@ describe(':terminal window', function()
{7: 6 } |
{3:-- TERMINAL --} |
]])
- feed_data({' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
+ feed_data(' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
screen:expect([[
{7: 1 }tty ready |
{7: 2 }rows: 6, cols: 48 |
@@ -82,6 +80,41 @@ describe(':terminal window', function()
end)
end)
+ describe("with 'statuscolumn'", function()
+ it('wraps text', function()
+ command([[set number statuscolumn=++%l\ \ ]])
+ screen:expect([[
+ {7:++1 }tty ready |
+ {7:++2 }rows: 6, cols: 45 |
+ {7:++3 }{1: } |
+ {7:++4 } |
+ {7:++5 } |
+ {7:++6 } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\n\n\n\n\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+ screen:expect([[
+ {7:++4 } |
+ {7:++5 } |
+ {7:++6 } |
+ {7:++7 } |
+ {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS|
+ {7:++9 }TUVWXYZ{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+ screen:expect([[
+ {7:++7 } |
+ {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR|
+ {7:++9 }STUVWXYZ |
+ {7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR|
+ {7:++11 }STUVWXYZrows: 6, cols: 44 |
+ {7:++12 }{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+ end)
+
describe("with 'colorcolumn'", function()
before_each(function()
feed([[<C-\><C-N>]])
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
index 9ed86e87f1..a8abbc002b 100644
--- a/test/functional/treesitter/fold_spec.lua
+++ b/test/functional/treesitter/fold_spec.lua
@@ -359,3 +359,116 @@ void ui_refresh(void)
end)
end)
+
+describe('treesitter foldtext', function()
+ local test_text = [[
+void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *))
+{
+ int width = INT_MAX, height = INT_MAX;
+ bool ext_widgets[kUIExtCount];
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ ext_widgets[i] = true;
+ }
+
+ bool inclusive = ui_override();
+ for (size_t i = 0; i < ui_count; i++) {
+ UI *ui = uis[i];
+ width = MIN(ui->width, width);
+ height = MIN(ui->height, height);
+ foo = BAR(ui->bazaar, bazaar);
+ for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive);
+ }
+ }
+}]]
+ local screen
+
+ before_each(function()
+ screen = Screen.new(60, 5)
+ screen:set_default_attr_ids({
+ [0] = {foreground = Screen.colors.Blue, bold = true},
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
+ [2] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.SeaGreen};
+ [3] = {foreground = Screen.colors.DarkCyan, background = Screen.colors.LightGray};
+ [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray};
+ [5] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.Brown};
+ [6] = {background = Screen.colors.Red1};
+ [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Red};
+ [8] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red};
+ [9] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Red};
+ [10] = {bold = true};
+ })
+ screen:attach()
+ end)
+
+ it('displays highlighted content', function()
+ command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]])
+ insert(test_text)
+ exec_lua([[vim.treesitter.get_parser(0, "c")]])
+
+ feed('ggVGzf')
+ screen:expect{grid=[[
+ {2:^void}{1: }{3:qsort}{4:(}{2:void}{1: }{5:*}{3:base}{4:,}{1: }{2:size_t}{1: }{3:nel}{4:,}{1: }{2:size_t}{1: }{3:width}{4:,}{1: }{2:int}{1: }{4:(}{5:*}{3:compa}|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
+
+ it('handles deep nested captures', function()
+ command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]])
+ insert([[
+function FoldInfo.new()
+ return setmetatable({
+ start_counts = {},
+ stop_counts = {},
+ levels0 = {},
+ levels = {},
+ }, FoldInfo)
+end]])
+ exec_lua([[vim.treesitter.get_parser(0, "lua")]])
+
+ feed('ggjVGkzfgg')
+ screen:expect{grid=[[
+ ^function FoldInfo.new() |
+ {1: }{5:return}{1: }{4:setmetatable({}{1:·····································}|
+ end |
+ {0:~ }|
+ |
+ ]]}
+
+ command('hi! Visual guibg=Red')
+ feed('GVgg')
+ screen:expect{grid=[[
+ ^f{6:unction FoldInfo.new()} |
+ {7: }{8:return}{7: }{9:setmetatable({}{7:·····································}|
+ {6:end} |
+ {0:~ }|
+ {10:-- VISUAL LINE --} |
+ ]]}
+
+ feed('10l<C-V>')
+ screen:expect{grid=[[
+ {6:function F}^oldInfo.new() |
+ {7: }{8:return}{7: }{9:se}{4:tmetatable({}{1:·····································}|
+ {6:end} |
+ {0:~ }|
+ {10:-- VISUAL BLOCK --} |
+ ]]}
+ end)
+
+ it('falls back to default', function()
+ command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext()]])
+ insert(test_text)
+
+ feed('ggVGzf')
+ screen:expect{grid=[[
+ {1:^+-- 19 lines: void qsort(void *base, size_t nel, size_t widt}|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
+end)
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index 17e6855ee4..e4766103c2 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -577,10 +577,10 @@ describe('Command-line coloring', function()
|
{EOB:~ }|
{EOB:~ }|
+ {EOB:~ }|
{MSEP: }|
:+ |
{ERR:E5404: Chunk 1 end 3 not in range (1, 2]}|
- |
:++^ |
]])
end)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index ea65cf35bd..38649a2be3 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -2439,14 +2439,14 @@ describe('builtin popupmenu', function()
prefix |
bef{n: word } |
tex{n: }^ |
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
-- can't draw the pum, but check we don't crash
screen:try_resize(12,2)
screen:expect([[
{1:<<<}t^ |
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
-- but state is preserved, pum reappears
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index bc9517aa60..7cc1accd3f 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -795,7 +795,7 @@ local function screen_tests(linegrid)
screen:try_resize(1, 1)
screen:expect([[
resize^ |
- {2:-- INSERT -} |
+ {2:-- INSERT --}|
]])
feed('<esc>:ls')
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 7b20f47f7a..b27b0be802 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -3303,4 +3303,22 @@ func Test_glob_extended_bash()
let &shell=_shell
endfunc
+" Test for glob() with extended patterns (MS-Windows)
+" Vim doesn't use 'shell' to expand wildcards on MS-Windows.
+" Unlike bash, it doesn't support {,} expansion.
+func Test_glob_extended_mswin()
+ CheckMSWindows
+
+ call mkdir('Xtestglob/foo/bar/src', 'p')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.sh')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.h')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.cpp')
+
+ " Sort output of glob() otherwise we end up with different
+ " ordering depending on whether file system is case-sensitive.
+ let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h', 'Xtestglob/foo/bar/src/foo.sh']
+ call assert_equal(expected, sort(glob('Xtestglob/**/foo.*', 0, 1)))
+ call delete('Xtestglob', 'rf')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_history.vim b/test/old/testdir/test_history.vim
index f1c31dee04..bb6d671725 100644
--- a/test/old/testdir/test_history.vim
+++ b/test/old/testdir/test_history.vim
@@ -244,8 +244,13 @@ endfunc
" Test for making sure the key value is not stored in history
func Test_history_crypt_key()
CheckFeature cryptv
+
call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt')
call assert_equal('set bs=2 key= ts=8', histget(':'))
+
+ call assert_fails("call feedkeys(':set bs=2 key-=abc ts=8\<CR>', 'xt')")
+ call assert_equal('set bs=2 key-= ts=8', histget(':'))
+
set key& bs& ts&
endfunc
diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim
index c56efe8786..76b7cc920b 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -299,11 +299,11 @@ func Test_set_completion()
call assert_equal('"set tabstop thesaurus thesaurusfunc', @:)
" Expand current value
- call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:)
+ call feedkeys(":set suffixes=\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set suffixes=.bak,~,.o,.h,.info,.swp,.obj', @:)
- call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
+ call feedkeys(":set suffixes:\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"set suffixes:.bak,~,.o,.h,.info,.swp,.obj', @:)
" Expand key codes.
" call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -364,14 +364,20 @@ func Test_set_completion()
call assert_equal("\"set invtabstop=", @:)
" Expand options for 'spellsuggest'
- call feedkeys(":set spellsuggest=best,file:xyz\<Tab>\<C-B>\"\<CR>", 'xt')
- call assert_equal("\"set spellsuggest=best,file:xyz", @:)
-
- " Expand value for 'key'
- " set key=abcd
- " call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
- " call assert_equal('"set key=*****', @:)
- " set key=
+ call feedkeys(":set spellsuggest=file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"set spellsuggest=file:test_options.vim", @:)
+ call feedkeys(":set spellsuggest=best,file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"set spellsuggest=best,file:test_options.vim", @:)
+
+ " Expanding value for 'key' is disallowed
+ if exists('+key')
+ set key=abcd
+ call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set key=', @:)
+ call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set key-=', @:)
+ set key=
+ endif
" Expand values for 'filetype'
call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt')
@@ -386,6 +392,286 @@ func Test_set_completion()
call assert_equal('"set syntax=' .. getcompletion('a*', 'syntax')->join(), @:)
endfunc
+" Test handling of expanding individual string option values
+func Test_set_completion_string_values()
+ "
+ " Test basic enum string options that have well-defined enum names
+ "
+
+ " call assert_equal(['lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['truncate'], getcompletion('set display=t', 'cmdline'))
+ call assert_equal(['uhex'], getcompletion('set display=*ex*', 'cmdline'))
+
+ " Test that if a value is set, it will populate the results, but only if
+ " typed value is empty.
+ set display=uhex,lastline
+ " call assert_equal(['uhex,lastline', 'lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['uhex,lastline', 'lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['uhex'], getcompletion('set display=u', 'cmdline'))
+ " If the set value is part of the enum list, it will show as the first
+ " result with no duplicate.
+ set display=uhex
+ " call assert_equal(['uhex', 'lastline', 'truncate'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['uhex', 'lastline', 'truncate', 'msgsep'], getcompletion('set display=', 'cmdline'))
+ " If empty value, will just show the normal list without an empty item
+ set display=
+ " call assert_equal(['lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline'))
+ call assert_equal(['lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline'))
+ " Test escaping of the values
+ " call assert_equal('vert:\|,fold:-,eob:~,lastline:@', getcompletion('set fillchars=', 'cmdline')[0])
+ call assert_equal('vert:\|,foldsep:\|,fold:-', getcompletion('set fillchars=', 'cmdline')[0])
+
+ " Test comma-separated lists will expand after a comma.
+ call assert_equal(['uhex'], getcompletion('set display=truncate,*ex*', 'cmdline'))
+ " Also test the positioning of the expansion is correct
+ call feedkeys(":set display=truncate,l\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set display=truncate,lastline', @:)
+ set display&
+
+ " Test single-value options will not expand after a comma
+ call assert_equal([], getcompletion('set ambw=single,', 'cmdline'))
+
+ " Test the other simple options to make sure they have basic auto-complete,
+ " but don't exhaustively validate their results.
+ call assert_equal('single', getcompletion('set ambw=', 'cmdline')[0])
+ call assert_match('light\|dark', getcompletion('set bg=', 'cmdline')[1])
+ call assert_equal('indent', getcompletion('set backspace=', 'cmdline')[0])
+ call assert_equal('yes', getcompletion('set backupcopy=', 'cmdline')[1])
+ call assert_equal('backspace', getcompletion('set belloff=', 'cmdline')[1])
+ call assert_equal('min:', getcompletion('set briopt=', 'cmdline')[1])
+ if exists('+browsedir')
+ call assert_equal('current', getcompletion('set browsedir=', 'cmdline')[1])
+ endif
+ call assert_equal('unload', getcompletion('set bufhidden=', 'cmdline')[1])
+ call assert_equal('nowrite', getcompletion('set buftype=', 'cmdline')[1])
+ call assert_equal('internal', getcompletion('set casemap=', 'cmdline')[1])
+ if exists('+clipboard')
+ " call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
+ call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[0])
+ endif
+ call assert_equal('.', getcompletion('set complete=', 'cmdline')[1])
+ call assert_equal('menu', getcompletion('set completeopt=', 'cmdline')[1])
+ if exists('+completeslash')
+ call assert_equal('backslash', getcompletion('set completeslash=', 'cmdline')[1])
+ endif
+ if exists('+cryptmethod')
+ call assert_equal('zip', getcompletion('set cryptmethod=', 'cmdline')[1])
+ endif
+ if exists('+cursorlineopt')
+ call assert_equal('line', getcompletion('set cursorlineopt=', 'cmdline')[1])
+ endif
+ call assert_equal('throw', getcompletion('set debug=', 'cmdline')[1])
+ call assert_equal('ver', getcompletion('set eadirection=', 'cmdline')[1])
+ call assert_equal('mac', getcompletion('set fileformat=', 'cmdline')[2])
+ if exists('+foldclose')
+ call assert_equal('all', getcompletion('set foldclose=', 'cmdline')[0])
+ endif
+ if exists('+foldmethod')
+ call assert_equal('expr', getcompletion('set foldmethod=', 'cmdline')[1])
+ endif
+ if exists('+foldopen')
+ call assert_equal('all', getcompletion('set foldopen=', 'cmdline')[1])
+ endif
+ call assert_equal('stack', getcompletion('set jumpoptions=', 'cmdline')[0])
+ call assert_equal('stopsel', getcompletion('set keymodel=', 'cmdline')[1])
+ call assert_equal('expr:1', getcompletion('set lispoptions=', 'cmdline')[1])
+ call assert_match('popup', getcompletion('set mousemodel=', 'cmdline')[2])
+ call assert_equal('bin', getcompletion('set nrformats=', 'cmdline')[1])
+ if exists('+rightleftcmd')
+ call assert_equal('search', getcompletion('set rightleftcmd=', 'cmdline')[0])
+ endif
+ call assert_equal('ver', getcompletion('set scrollopt=', 'cmdline')[1])
+ call assert_equal('exclusive', getcompletion('set selection=', 'cmdline')[1])
+ call assert_equal('key', getcompletion('set selectmode=', 'cmdline')[1])
+ if exists('+ssop')
+ call assert_equal('buffers', getcompletion('set ssop=', 'cmdline')[1])
+ endif
+ call assert_equal('statusline', getcompletion('set showcmdloc=', 'cmdline')[1])
+ if exists('+signcolumn')
+ call assert_equal('yes', getcompletion('set signcolumn=', 'cmdline')[1])
+ endif
+ if exists('+spelloptions')
+ call assert_equal('camel', getcompletion('set spelloptions=', 'cmdline')[0])
+ endif
+ if exists('+spellsuggest')
+ call assert_equal('best', getcompletion('set spellsuggest+=', 'cmdline')[0])
+ endif
+ call assert_equal('screen', getcompletion('set splitkeep=', 'cmdline')[1])
+ " call assert_equal('sync', getcompletion('set swapsync=', 'cmdline')[1])
+ call assert_equal('usetab', getcompletion('set switchbuf=', 'cmdline')[1])
+ call assert_equal('ignore', getcompletion('set tagcase=', 'cmdline')[1])
+ if exists('+termwintype')
+ call assert_equal('conpty', getcompletion('set termwintype=', 'cmdline')[1])
+ endif
+ if exists('+toolbar')
+ call assert_equal('text', getcompletion('set toolbar=', 'cmdline')[1])
+ endif
+ if exists('+tbis')
+ call assert_equal('medium', getcompletion('set tbis=', 'cmdline')[2])
+ endif
+ if exists('+ttymouse')
+ set ttymouse=
+ call assert_equal('xterm2', getcompletion('set ttymouse=', 'cmdline')[1])
+ set ttymouse&
+ endif
+ call assert_equal('insert', getcompletion('set virtualedit=', 'cmdline')[1])
+ call assert_equal('longest', getcompletion('set wildmode=', 'cmdline')[1])
+ call assert_equal('full', getcompletion('set wildmode=list,longest:', 'cmdline')[0])
+ call assert_equal('tagfile', getcompletion('set wildoptions=', 'cmdline')[1])
+ if exists('+winaltkeys')
+ call assert_equal('yes', getcompletion('set winaltkeys=', 'cmdline')[1])
+ endif
+
+ " Other string options that queries the system rather than fixed enum names
+ call assert_equal(['all', 'BufAdd'], getcompletion('set eventignore=', 'cmdline')[0:1])
+ call assert_equal('latin1', getcompletion('set fileencodings=', 'cmdline')[1])
+ " call assert_equal('top', getcompletion('set printoptions=', 'cmdline')[0])
+ " call assert_equal('SpecialKey', getcompletion('set wincolor=', 'cmdline')[0])
+
+ call assert_equal('eol', getcompletion('set listchars+=', 'cmdline')[0])
+ call assert_equal(['multispace', 'leadmultispace'], getcompletion('set listchars+=', 'cmdline')[-2:])
+ call assert_equal('eol', getcompletion('setl listchars+=', 'cmdline')[0])
+ call assert_equal(['multispace', 'leadmultispace'], getcompletion('setl listchars+=', 'cmdline')[-2:])
+ call assert_equal('stl', getcompletion('set fillchars+=', 'cmdline')[0])
+ call assert_equal('stl', getcompletion('setl fillchars+=', 'cmdline')[0])
+
+ "
+ " Unique string options below
+ "
+
+ " keyprotocol: only auto-complete when after ':' with known protocol types
+ " call assert_equal([&keyprotocol], getcompletion('set keyprotocol=', 'cmdline'))
+ " call feedkeys(":set keyprotocol+=someterm:m\<Tab>\<C-B>\"\<CR>", 'xt')
+ " call assert_equal('"set keyprotocol+=someterm:mok2', @:)
+ " set keyprotocol&
+
+ " previewpopup / completepopup
+ " call assert_equal('height:', getcompletion('set previewpopup=', 'cmdline')[0])
+ " call assert_equal('EndOfBuffer', getcompletion('set previewpopup=highlight:End*Buffer', 'cmdline')[0])
+ " call feedkeys(":set previewpopup+=border:\<Tab>\<C-B>\"\<CR>", 'xt')
+ " call assert_equal('"set previewpopup+=border:on', @:)
+ " call feedkeys(":set completepopup=height:10,align:\<Tab>\<C-B>\"\<CR>", 'xt')
+ " call assert_equal('"set completepopup=height:10,align:item', @:)
+ " call assert_equal([], getcompletion('set completepopup=bogusname:', 'cmdline'))
+ " set previewpopup& completepopup&
+
+ " diffopt: special handling of algorithm:<alg_list>
+ call assert_equal('filler', getcompletion('set diffopt+=', 'cmdline')[0])
+ call assert_equal([], getcompletion('set diffopt+=iblank,foldcolumn:', 'cmdline'))
+ call assert_equal('patience', getcompletion('set diffopt+=iblank,algorithm:pat*', 'cmdline')[0])
+
+ " highlight: special parsing, including auto-completing highlight groups
+ " after ':'
+ " call assert_equal([&hl, '8'], getcompletion('set hl=', 'cmdline')[0:1])
+ " call assert_equal('8', getcompletion('set hl+=', 'cmdline')[0])
+ " call assert_equal(['8:', '8b', '8i'], getcompletion('set hl+=8', 'cmdline')[0:2])
+ " call assert_equal('8bi', getcompletion('set hl+=8b', 'cmdline')[0])
+ " call assert_equal('NonText', getcompletion('set hl+=8:No*ext', 'cmdline')[0])
+ " If all the display modes are used up we should be suggesting nothing. Make
+ " a hl typed option with all the modes which will look like '8bi-nrsuc2d=t',
+ " and make sure nothing is suggested from that.
+ " let hl_display_modes = join(
+ " \ filter(map(getcompletion('set hl+=8', 'cmdline'),
+ " \ {idx, val -> val[1]}),
+ " \ {idx, val -> val != ':'}),
+ " \ '')
+ " call assert_equal([], getcompletion('set hl+=8'..hl_display_modes, 'cmdline'))
+
+ "
+ " Test flag lists
+ "
+
+ " Test set=. Show the original value if nothing is typed after '='.
+ " Otherwise, the list should avoid showing what's already typed.
+ set mouse=v
+ call assert_equal(['v','a','n','i','c','h','r'], getcompletion('set mouse=', 'cmdline'))
+ set mouse=nvi
+ call assert_equal(['nvi','a','n','v','i','c','h','r'], getcompletion('set mouse=', 'cmdline'))
+ call assert_equal(['a','v','i','c','r'], getcompletion('set mouse=hn', 'cmdline'))
+
+ " Test set+=. Never show original value, and it also tries to avoid listing
+ " flags that's already in the option value.
+ call assert_equal(['a','c','h','r'], getcompletion('set mouse+=', 'cmdline'))
+ call assert_equal(['a','c','r'], getcompletion('set mouse+=hn', 'cmdline'))
+ call assert_equal([], getcompletion('set mouse+=acrhn', 'cmdline'))
+
+ " Test that the position of the expansion is correct (even if there are
+ " additional values after the current cursor)
+ call feedkeys(":set mouse=hn\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set mouse=han', @:)
+ set mouse&
+
+ " Test that other flag list options have auto-complete, but don't
+ " exhaustively validate their results.
+ if exists('+concealcursor')
+ call assert_equal('n', getcompletion('set cocu=', 'cmdline')[0])
+ endif
+ call assert_equal('a', getcompletion('set cpo=', 'cmdline')[1])
+ call assert_equal('t', getcompletion('set fo=', 'cmdline')[1])
+ if exists('+guioptions')
+ call assert_equal('!', getcompletion('set go=', 'cmdline')[1])
+ endif
+ call assert_equal('r', getcompletion('set shortmess=', 'cmdline')[1])
+ call assert_equal('b', getcompletion('set whichwrap=', 'cmdline')[1])
+
+ "
+ "Test set-=
+ "
+
+ " Normal single-value option just shows the existing value
+ set ambiwidth=double
+ call assert_equal(['double'], getcompletion('set ambw-=', 'cmdline'))
+ set ambiwidth&
+
+ " Works on numbers and term options as well
+ call assert_equal([string(&laststatus)], getcompletion('set laststatus-=', 'cmdline'))
+ set t_Ce=testCe
+ " call assert_equal(['testCe'], getcompletion('set t_Ce-=', 'cmdline'))
+ set t_Ce&
+
+ " Comma-separated lists should present each option
+ set diffopt=context:123,,,,,iblank,iwhiteall
+ call assert_equal(['context:123', 'iblank', 'iwhiteall'], getcompletion('set diffopt-=', 'cmdline'))
+ call assert_equal(['context:123', 'iblank'], getcompletion('set diffopt-=*n*', 'cmdline'))
+ call assert_equal(['iblank', 'iwhiteall'], getcompletion('set diffopt-=i', 'cmdline'))
+ " Don't present more than one option as it doesn't make sense in set-=
+ call assert_equal([], getcompletion('set diffopt-=iblank,', 'cmdline'))
+ " Test empty option
+ set diffopt=
+ call assert_equal([], getcompletion('set diffopt-=', 'cmdline'))
+ set diffopt&
+
+ " Test escaping output
+ call assert_equal('vert:\|', getcompletion('set fillchars-=', 'cmdline')[0])
+
+ " Test files with commas in name are being parsed and escaped properly
+ set path=has\\\ space,file\\,with\\,comma,normal_file
+ if exists('+completeslash')
+ call assert_equal(['has\\\ space', 'file\,with\,comma', 'normal_file'], getcompletion('set path-=', 'cmdline'))
+ else
+ call assert_equal(['has\\\ space', 'file\\,with\\,comma', 'normal_file'], getcompletion('set path-=', 'cmdline'))
+ endif
+ set path&
+
+ " Flag list should present orig value, then individual flags
+ set mouse=v
+ call assert_equal(['v'], getcompletion('set mouse-=', 'cmdline'))
+ set mouse=avn
+ call assert_equal(['avn','a','v','n'], getcompletion('set mouse-=', 'cmdline'))
+ " Don't auto-complete when we have at least one flags already
+ call assert_equal([], getcompletion('set mouse-=n', 'cmdline'))
+ " Test empty option
+ set mouse=
+ call assert_equal([], getcompletion('set mouse-=', 'cmdline'))
+ set mouse&
+
+ " 'whichwrap' is an odd case where it's both flag list and comma-separated
+ set ww=b,h
+ call assert_equal(['b','h'], getcompletion('set ww-=', 'cmdline'))
+ set ww&
+endfunc
+
func Test_set_option_errors()
call assert_fails('set scroll=-1', 'E49:')
call assert_fails('set backupcopy=', 'E474:')
@@ -1433,7 +1719,7 @@ func Test_opt_cdhome()
set cdhome&
endfunc
-func Test_set_completion_2()
+func Test_set_completion_fuzzy()
CheckOption termguicolors
" Test default option completion