aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/scripts/reviewers_add.js10
-rw-r--r--.github/workflows/test_windows.yml7
-rw-r--r--runtime/autoload/typst.vim5
-rw-r--r--runtime/autoload/zip.vim4
-rw-r--r--runtime/doc/insert.txt10
-rw-r--r--runtime/doc/lsp.txt2
-rw-r--r--runtime/doc/news.txt5
-rw-r--r--runtime/doc/options.txt68
-rw-r--r--runtime/doc/pi_netrw.txt2
-rw-r--r--runtime/doc/pi_paren.txt19
-rw-r--r--runtime/doc/quickref.txt2
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/doc/vvars.txt8
-rw-r--r--runtime/ftplugin/cook.vim13
-rw-r--r--runtime/ftplugin/leo.vim13
-rw-r--r--runtime/ftplugin/sway.vim15
-rw-r--r--runtime/lua/vim/_defaults.lua13
-rw-r--r--runtime/lua/vim/_meta/options.lua58
-rw-r--r--runtime/lua/vim/_meta/vvars.lua8
-rw-r--r--runtime/lua/vim/filetype.lua5
-rw-r--r--runtime/lua/vim/lsp/buf.lua116
-rw-r--r--runtime/lua/vim/lsp/util.lua131
-rw-r--r--runtime/lua/vim/treesitter/dev.lua6
-rw-r--r--runtime/lua/vim/treesitter/health.lua18
-rw-r--r--runtime/plugin/matchparen.vim17
-rw-r--r--runtime/syntax/shared/debversions.vim8
-rw-r--r--runtime/syntax/typst.vim6
-rwxr-xr-xscripts/gen_eval_files.lua4
-rw-r--r--src/nvim/api/deprecated.c42
-rw-r--r--src/nvim/api/options.c5
-rw-r--r--src/nvim/buffer.c3
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/cmdexpand.c12
-rw-r--r--src/nvim/cmdexpand_defs.h2
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/errors.h2
-rw-r--r--src/nvim/eval.c6
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/ex_docmd.c132
-rw-r--r--src/nvim/generators/gen_options.lua20
-rw-r--r--src/nvim/grid_defs.h4
-rw-r--r--src/nvim/insexpand.c17
-rw-r--r--src/nvim/mbyte.c10
-rw-r--r--src/nvim/normal.c55
-rw-r--r--src/nvim/normal_defs.h5
-rw-r--r--src/nvim/option.c416
-rw-r--r--src/nvim/option.h1
-rw-r--r--src/nvim/option_vars.h18
-rw-r--r--src/nvim/options.lua96
-rw-r--r--src/nvim/optionstr.c20
-rw-r--r--src/nvim/os/input.c62
-rw-r--r--src/nvim/popupmenu.c10
-rw-r--r--src/nvim/search.c15
-rw-r--r--src/nvim/types_defs.h4
-rw-r--r--src/nvim/vvars.lua10
-rw-r--r--test/functional/editor/defaults_spec.lua48
-rw-r--r--test/functional/editor/mode_normal_spec.lua20
-rw-r--r--test/functional/lua/runtime_spec.lua4
-rw-r--r--test/functional/testnvim.lua48
-rw-r--r--test/functional/ui/mouse_spec.lua31
-rw-r--r--test/functional/ui/popupmenu_spec.lua24
-rw-r--r--test/old/testdir/gen_opt_test.vim2
-rw-r--r--test/old/testdir/shared.vim12
-rw-r--r--test/old/testdir/test_autocmd.vim5
-rw-r--r--test/old/testdir/test_cmdline.vim5
-rw-r--r--test/old/testdir/test_compiler.vim7
-rw-r--r--test/old/testdir/test_expr.vim8
-rw-r--r--test/old/testdir/test_filetype.vim5
-rw-r--r--test/old/testdir/test_findfile.vim532
-rw-r--r--test/old/testdir/test_ins_complete.vim93
-rw-r--r--test/old/testdir/test_modeline.vim2
-rw-r--r--test/old/testdir/test_normal.vim4
-rw-r--r--test/old/testdir/test_options.vim2
-rw-r--r--test/old/testdir/test_popup.vim22
-rw-r--r--test/old/testdir/test_tagfunc.vim4
-rw-r--r--test/testutil.lua68
76 files changed, 1606 insertions, 889 deletions
diff --git a/.github/scripts/reviewers_add.js b/.github/scripts/reviewers_add.js
index 50195497af..08b4e85b74 100644
--- a/.github/scripts/reviewers_add.js
+++ b/.github/scripts/reviewers_add.js
@@ -39,10 +39,6 @@ module.exports = async ({ github, context }) => {
reviewers.add("lewis6991");
}
- if (labels.includes("documentation")) {
- reviewers.add("clason");
- }
-
if (labels.includes("editorconfig")) {
reviewers.add("gpanders");
}
@@ -53,7 +49,6 @@ module.exports = async ({ github, context }) => {
if (labels.includes("filetype")) {
reviewers.add("clason");
- reviewers.add("gpanders");
}
if (labels.includes("inccommand")) {
@@ -90,10 +85,6 @@ module.exports = async ({ github, context }) => {
reviewers.add("famiu");
}
- if (labels.includes("test")) {
- reviewers.add("justinmk");
- }
-
if (labels.includes("treesitter")) {
reviewers.add("bfredl");
reviewers.add("clason");
@@ -110,7 +101,6 @@ module.exports = async ({ github, context }) => {
}
if (labels.includes("vim-patch")) {
- reviewers.add("seandewar");
reviewers.add("zeertzjq");
}
diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml
index db7ad93f55..8829296756 100644
--- a/.github/workflows/test_windows.yml
+++ b/.github/workflows/test_windows.yml
@@ -31,6 +31,13 @@ jobs:
cmake --preset ci -D CMAKE_BUILD_TYPE='RelWithDebInfo' ${{ inputs.build_flags }}
cmake --build build
+ # FIXME(dundargoc): this workaround is needed as the python3 provider
+ # tests suddenly started to become extremely flaky, and this removes the
+ # flakiness for some reason.
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.13'
+
- name: Install test deps
run: |
$PSNativeCommandArgumentPassing = 'Legacy'
diff --git a/runtime/autoload/typst.vim b/runtime/autoload/typst.vim
index baf765a30b..6d097dd922 100644
--- a/runtime/autoload/typst.vim
+++ b/runtime/autoload/typst.vim
@@ -1,6 +1,6 @@
" Language: Typst
" Maintainer: Gregory Anders
-" Last Change: 2024 Oct 21
+" Last Change: 2024 Nov 02
" Based on: https://github.com/kaarmu/typst.vim
function! typst#indentexpr() abort
@@ -18,6 +18,9 @@ function! typst#indentexpr() abort
" Use last indent for block comments
if l:synname == 'typstCommentBlock'
return l:ind
+ " do not change the indents of bullet lists
+ elseif l:synname == 'typstMarkupBulletList'
+ return indent(a:lnum)
endif
if l:pline =~ '\v[{[(]\s*$'
diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim
index 172de98d36..a8ea6a3fab 100644
--- a/runtime/autoload/zip.vim
+++ b/runtime/autoload/zip.vim
@@ -408,7 +408,9 @@ fun! s:SetSaneOpts()
let dict.shellslash = &shellslash
let &report = 10
- let &shellslash = 0
+ if exists('+shellslash')
+ let &shellslash = 0
+ endif
return dict
endfun
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 27b28624f8..48fd442b7e 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1907,6 +1907,16 @@ These commands are used to start inserting text. You can end insert mode with
<Esc>. See |mode-ins-repl| for the other special characters in Insert mode.
The effect of [count] takes place after Insert mode is exited.
+The following commands insert text, but stay in normal mode:
+
+ *]<Space>*
+]<Space> Insert an empty line below the cursor without leaving
+ Normal mode, repeat [count] times.
+
+ *[<Space>*
+[<Space> Insert an empty line above the cursor without leaving
+ Normal mode, repeat [count] times.
+
When 'autoindent' is on, the indent for a new line is obtained from the
previous line. When 'smartindent' or 'cindent' is on, the indent for a line
is automatically adjusted for C programs.
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 6e5c85c019..87df1e06d6 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1911,7 +1911,7 @@ make_floating_popup_options({width}, {height}, {opts})
|vim.lsp.util.open_floating_preview.Opts|.
Return: ~
- (`table`) Options
+ (`vim.api.keyset.win_config`)
*vim.lsp.util.make_formatting_params()*
make_formatting_params({options})
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index f82cf4453e..df794596b8 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -113,6 +113,8 @@ OPTIONS
of just string |global-local| options.
• `:setlocal {option}<` copies the global value to the local value for number
and boolean |global-local| options instead of removing the local value.
+• Setting |hidden-options| now gives an error. In particular, setting
+ 'noshellslash' is now only allowed on Windows.
PLUGINS
@@ -176,6 +178,7 @@ DEFAULTS
• |[t|, |]t|, |[T|, |]T|, |[CTRL-T|, |]CTRL-T| navigate through the |tag-matchlist|
• |[a|, |]a|, |[A|, |]A| navigate through the |argument-list|
• |[b|, |]b|, |[B|, |]B| navigate through the |buffer-list|
+ • |[<Space>|, |]<Space>| add an empty line above and below the cursor
• Snippet:
• `<Tab>` in Insert and Select mode maps to `vim.snippet.jump({ direction = 1 })`
@@ -209,6 +212,8 @@ LSP
`textDocument/rangesFormatting` request).
• |vim.lsp.buf.code_action()| actions show client name when there are multiple
clients.
+• |vim.lsp.buf.signature_help()| can now cycle through different signatures
+ using `<C-s>` and also support multiple clients.
LUA
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index feaff3e425..3217f5c565 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -370,10 +370,11 @@ Note: In the future more global options can be made |global-local|. Using
":setlocal" on a global option might work differently then.
*option-value-function*
-Some options ('completefunc', 'omnifunc', 'operatorfunc', 'quickfixtextfunc',
-'tagfunc' and 'thesaurusfunc') are set to a function name or a function
-reference or a lambda function. When using a lambda it will be converted to
-the name, e.g. "<lambda>123". Examples:
+Some options ('completefunc', 'findfunc', 'omnifunc', 'operatorfunc',
+'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to a function name
+or a function reference or a lambda function. When using a lambda it will be
+converted to the name, e.g. "<lambda>123".
+Examples:
>
set opfunc=MyOpFunc
set opfunc=function('MyOpFunc')
@@ -641,11 +642,12 @@ Hidden options *hidden-options*
Not all options are supported in all versions. This depends on the supported
features and sometimes on the system. A remark about this is in curly braces
-below. When an option is not supported it may still be set without getting an
-error, this is called a hidden option. You can't get the value of a hidden
-option though, it is not stored.
+below. When an option is not supported, it is called a hidden option. Trying
+to get the value of a hidden option will not give an error, it will return the
+default value for that option instead. You can't change the value of a hidden
+option.
-To test if option "foo" can be used with ":set" use something like this: >
+To test if "foo" is a valid option name, use something like this: >
if exists('&foo')
This also returns true for a hidden option. To test if option "foo" is really
supported use something like this: >
@@ -1572,7 +1574,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")
local to buffer
- only for MS-Windows
+ only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion:
- When this option is set to "slash", a forward slash is used for path
completion in insert mode. This is useful when editing HTML tag, or
@@ -2598,34 +2600,34 @@ A jump table for the options with a short description can be found at |Q_op|.
eob EndOfBuffer |hl-EndOfBuffer|
lastline NonText |hl-NonText|
- *'findexpr'* *'fexpr'* *E1514*
-'findexpr' 'fexpr' string (default "")
+ *'findfunc'* *'ffu'* *E1514*
+'findfunc' 'ffu' string (default "")
global or local to buffer |global-local|
- Expression that is evaluated to obtain the filename(s) for the |:find|
+ Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
mechanism is used.
- While evaluating the expression, the |v:fname| variable is set to the
- argument of the |:find| command.
+ The value can be the name of a function, a |lambda| or a |Funcref|.
+ See |option-value-function| for more information.
- The expression is evaluated only once per |:find| command invocation.
- The expression can process all the directories specified in 'path'.
+ The function is called with two arguments. The first argument is a
+ |String| and is the |:find| command argument. The second argument is
+ a |Boolean| and is set to |v:true| when the function is called to get
+ a List of command-line completion matches for the |:find| command.
+ The function should return a List of strings.
- The expression may be evaluated for command-line completion as well,
- in which case the |v:cmdcomplete| variable will be set to |v:true|,
- otherwise it will be set to |v:false|.
+ The function is called only once per |:find| command invocation.
+ The function can process all the directories specified in 'path'.
- If a match is found, the expression should return a |List| containing
- one or more file names. If a match is not found, the expression
+ If a match is found, the function should return a |List| containing
+ one or more file names. If a match is not found, the function
should return an empty List.
- If any errors are encountered during the expression evaluation, an
+ If any errors are encountered during the function invocation, an
empty List is used as the return value.
- Using a function call without arguments is faster |expr-option-function|
-
It is not allowed to change text or jump to another window while
- evaluating 'findexpr' |textlock|.
+ executing the 'findfunc' |textlock|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -2633,18 +2635,18 @@ A jump table for the options with a short description can be found at |Q_op|.
Examples:
>vim
" Use glob()
- func FindExprGlob()
- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+ func FindFuncGlob(cmdarg, cmdcomplete)
+ let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
- set findexpr=FindExprGlob()
+ set findfunc=FindFuncGlob
" Use the 'git ls-files' output
- func FindGitFiles()
+ func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
- return fnames->filter('v:val =~? v:fname')
+ return fnames->filter('v:val =~? a:cmdarg')
endfunc
- set findexpr=FindGitFiles()
+ set findfunc=FindGitFiles
<
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
@@ -5276,9 +5278,9 @@ A jump table for the options with a short description can be found at |Q_op|.
security reasons.
*'shellslash'* *'ssl'* *'noshellslash'* *'nossl'*
-'shellslash' 'ssl' boolean (default off)
+'shellslash' 'ssl' boolean (default on, Windows: off)
global
- only for MS-Windows
+ only modifiable in MS-Windows
When set, a forward slash is used when expanding file names. This is
useful when a Unix-like shell is used instead of cmd.exe. Backward
slashes can still be typed, but they are changed to forward slashes by
diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt
index 8db15df004..f8712f7d2e 100644
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/doc/pi_netrw.txt
@@ -1538,7 +1538,7 @@ OPENING FILES AND LAUNCHING APPS *netrw-gx* *:Open* *:Launch* {{
Netrw determines which special handler by the following method:
* if |g:netrw_browsex_viewer| exists, then it will be used to attempt to
- view files. Examples of useful settings (place into your <.vimrc>):
+ view files.
If the viewer you wish to use does not support handling of a remote URL
directory, set |g:netrw_browsex_support_remote| to 0.
* otherwise:
diff --git a/runtime/doc/pi_paren.txt b/runtime/doc/pi_paren.txt
index 77083362da..caa88e8d56 100644
--- a/runtime/doc/pi_paren.txt
+++ b/runtime/doc/pi_paren.txt
@@ -10,6 +10,7 @@ The functionality mentioned here is a |standard-plugin|.
This plugin is only available if 'compatible' is not set.
You can avoid loading this plugin by setting the "loaded_matchparen" variable: >
+
:let loaded_matchparen = 1
The plugin installs CursorMoved, CursorMovedI and WinEnter autocommands to
@@ -29,6 +30,16 @@ the ":highlight" command. Example: >
:hi MatchParen ctermbg=blue guibg=lightblue
+By default the plugin will highlight both the paren under the cursor and the
+matching one using the |hl-MatchParen| highlighting group. This may result in
+the cursor briefly disappearing from the screen as the MatchParen colors take
+over the cursor highlight. To prevent this from happening and have the plugin
+only highlight the matching paren and not the one under the cursor
+(effectively leaving the cursor style unchanged), you can set the
+"matchparen_disable_cursor_hl" variable: >
+
+ :let matchparen_disable_cursor_hl = 1
+
The characters to be matched come from the 'matchpairs' option. You can
change the value to highlight different matches. Note that not everything is
possible. For example, you can't highlight single or double quotes, because
@@ -46,10 +57,10 @@ are:
closed folds.
- 'synmaxcol' times 2 bytes before or after the cursor to avoid a delay
in a long line with syntax highlighting.
-- A timeout of 300 msec (60 msec in Insert mode). This can be changed with the
- g:matchparen_timeout and g:matchparen_insert_timeout variables and their
- buffer-local equivalents b:matchparen_timeout and
- b:matchparen_insert_timeout.
+- A timeout of 300 msec (60 msec in Insert mode). This can be changed with
+ the "g:matchparen_timeout" and "g:matchparen_insert_timeout" variables and
+ their buffer-local equivalents "b:matchparen_timeout" and
+ "b:matchparen_insert_timeout".
If you would like the |%| command to work better, the |matchit| plugin can be
used. This plugin also helps to skip matches in comments. This is unrelated
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index f64865a031..f43ddb57fb 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -705,7 +705,7 @@ Short explanation of each option: *option-list*
'fileignorecase' 'fic' ignore case when using file names
'filetype' 'ft' type of file, used for autocommands
'fillchars' 'fcs' characters to use for displaying special items
-'findexpr' 'fexpr' expression to evaluate for |:find|
+'findfunc' 'ffu' function to be called for the |:find| command
'fixendofline' 'fixeol' make sure last line in file has <EOL>
'foldclose' 'fcl' close a fold when the cursor leaves it
'foldcolumn' 'fdc' width of the column used to indicate folds
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 10816ec358..8fa94a2601 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -163,6 +163,7 @@ of these in your config by simply removing the mapping, e.g. ":unmap Y".
- |[t|, |]t|, |[T|, |]T|, |[CTRL-T|, |]CTRL-T|
- |[a|, |]a|, |[A|, |]A|
- |[b|, |]b|, |[B|, |]B|
+- |[<Space>|, |]<Space>|
- Nvim LSP client defaults |lsp-defaults|
- K |K-lsp-default|
diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt
index 1c1d88c29c..15d836a83d 100644
--- a/runtime/doc/vvars.txt
+++ b/runtime/doc/vvars.txt
@@ -48,11 +48,6 @@ v:cmdbang
can only be used in autocommands. For user commands |<bang>|
can be used.
- *v:cmdcomplete* *cmdcomplete-variable*
-v:cmdcomplete
- When evaluating 'findexpr': if 'findexpr' is used for cmdline
- completion the value is |v:true|, otherwise it is |v:false|.
-
*v:collate* *collate-variable*
v:collate
The current locale setting for collation order of the runtime
@@ -259,8 +254,7 @@ v:fcs_reason
*v:fname* *fname-variable*
v:fname
When evaluating 'includeexpr': the file name that was
- detected. When evaluating 'findexpr': the argument passed to
- the |:find| command. Empty otherwise.
+ detected. Empty otherwise.
*v:fname_diff* *fname_diff-variable*
v:fname_diff
diff --git a/runtime/ftplugin/cook.vim b/runtime/ftplugin/cook.vim
new file mode 100644
index 0000000000..3697803e74
--- /dev/null
+++ b/runtime/ftplugin/cook.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: Cooklang
+" Maintainer: Riley Bruins <ribru17@gmail.com>
+" Last Change: 2024 Nov 03
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=:-- commentstring=--\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/leo.vim b/runtime/ftplugin/leo.vim
new file mode 100644
index 0000000000..a009c02df4
--- /dev/null
+++ b/runtime/ftplugin/leo.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin
+" Language: Leo
+" Maintainer: Riley Bruins <ribru17@gmail.com>
+" Last Change: 2024 Nov 03
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=:// commentstring=//\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/sway.vim b/runtime/ftplugin/sway.vim
new file mode 100644
index 0000000000..35970b257c
--- /dev/null
+++ b/runtime/ftplugin/sway.vim
@@ -0,0 +1,15 @@
+" Vim filetype plugin
+" Language: Sway
+" Maintainer: Riley Bruins <ribru17@gmail.com>
+" Last Change: 2024 Nov 01
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl commentstring=//\ %s
+" From Rust comments
+setl comments=s0:/*!,ex:*/,s1:/*,mb:*,ex:*/,:///,://!,://
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index d3b7bda871..f399360f1e 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -363,6 +363,19 @@ do
cmd({ cmd = 'blast' })
end
end, { desc = ':blast' })
+
+ -- Add empty lines
+ vim.keymap.set('n', '[<Space>', function()
+ local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
+ local linenr = vim.api.nvim_win_get_cursor(0)[1]
+ vim.api.nvim_buf_set_lines(0, linenr - 1, linenr - 1, true, repeated)
+ end, { desc = 'Add empty line above cursor' })
+
+ vim.keymap.set('n', ']<Space>', function()
+ local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
+ local linenr = vim.api.nvim_win_get_cursor(0)[1]
+ vim.api.nvim_buf_set_lines(0, linenr, linenr, true, repeated)
+ end, { desc = 'Add empty line below cursor' })
end
end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 5e5b6b5ed1..45ab14a774 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -1106,7 +1106,7 @@ vim.bo.cot = vim.bo.completeopt
vim.go.completeopt = vim.o.completeopt
vim.go.cot = vim.go.completeopt
---- only for MS-Windows
+--- only modifiable in MS-Windows
--- When this option is set it overrules 'shellslash' for completion:
--- - When this option is set to "slash", a forward slash is used for path
--- completion in insert mode. This is useful when editing HTML tag, or
@@ -2294,31 +2294,31 @@ vim.wo.fcs = vim.wo.fillchars
vim.go.fillchars = vim.o.fillchars
vim.go.fcs = vim.go.fillchars
---- Expression that is evaluated to obtain the filename(s) for the `:find`
+--- Function that is called to obtain the filename(s) for the `:find`
--- command. When this option is empty, the internal `file-searching`
--- mechanism is used.
---
---- While evaluating the expression, the `v:fname` variable is set to the
---- argument of the `:find` command.
+--- The value can be the name of a function, a `lambda` or a `Funcref`.
+--- See `option-value-function` for more information.
---
---- The expression is evaluated only once per `:find` command invocation.
---- The expression can process all the directories specified in 'path'.
+--- The function is called with two arguments. The first argument is a
+--- `String` and is the `:find` command argument. The second argument is
+--- a `Boolean` and is set to `v:true` when the function is called to get
+--- a List of command-line completion matches for the `:find` command.
+--- The function should return a List of strings.
---
---- The expression may be evaluated for command-line completion as well,
---- in which case the `v:cmdcomplete` variable will be set to `v:true`,
---- otherwise it will be set to `v:false`.
+--- The function is called only once per `:find` command invocation.
+--- The function can process all the directories specified in 'path'.
---
---- If a match is found, the expression should return a `List` containing
---- one or more file names. If a match is not found, the expression
+--- If a match is found, the function should return a `List` containing
+--- one or more file names. If a match is not found, the function
--- should return an empty List.
---
---- If any errors are encountered during the expression evaluation, an
+--- If any errors are encountered during the function invocation, an
--- empty List is used as the return value.
---
---- Using a function call without arguments is faster `expr-option-function`
----
--- It is not allowed to change text or jump to another window while
---- evaluating 'findexpr' `textlock`.
+--- executing the 'findfunc' `textlock`.
---
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
@@ -2327,28 +2327,28 @@ vim.go.fcs = vim.go.fillchars
---
--- ```vim
--- " Use glob()
---- func FindExprGlob()
---- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+--- func FindFuncGlob(cmdarg, cmdcomplete)
+--- let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
--- return glob(pat, v:false, v:true)
--- endfunc
---- set findexpr=FindExprGlob()
+--- set findfunc=FindFuncGlob
---
--- " Use the 'git ls-files' output
---- func FindGitFiles()
+--- func FindGitFiles(cmdarg, cmdcomplete)
--- let fnames = systemlist('git ls-files')
---- return fnames->filter('v:val =~? v:fname')
+--- return fnames->filter('v:val =~? a:cmdarg')
--- endfunc
---- set findexpr=FindGitFiles()
+--- set findfunc=FindGitFiles
--- ```
---
---
--- @type string
-vim.o.findexpr = ""
-vim.o.fexpr = vim.o.findexpr
-vim.bo.findexpr = vim.o.findexpr
-vim.bo.fexpr = vim.bo.findexpr
-vim.go.findexpr = vim.o.findexpr
-vim.go.fexpr = vim.go.findexpr
+vim.o.findfunc = ""
+vim.o.ffu = vim.o.findfunc
+vim.bo.findfunc = vim.o.findfunc
+vim.bo.ffu = vim.bo.findfunc
+vim.go.findfunc = vim.o.findfunc
+vim.go.ffu = vim.go.findfunc
--- When writing a file and this option is on, <EOL> at the end of file
--- will be restored if missing. Turn this option off if you want to
@@ -5547,7 +5547,7 @@ vim.o.srr = vim.o.shellredir
vim.go.shellredir = vim.o.shellredir
vim.go.srr = vim.go.shellredir
---- only for MS-Windows
+--- only modifiable in MS-Windows
--- When set, a forward slash is used when expanding file names. This is
--- useful when a Unix-like shell is used instead of cmd.exe. Backward
--- slashes can still be typed, but they are changed to forward slashes by
@@ -5564,7 +5564,7 @@ vim.go.srr = vim.go.shellredir
--- Also see 'completeslash'.
---
--- @type boolean
-vim.o.shellslash = false
+vim.o.shellslash = true
vim.o.ssl = vim.o.shellslash
vim.go.shellslash = vim.o.shellslash
vim.go.ssl = vim.go.shellslash
diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index b104356334..e00402ab3f 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -44,11 +44,6 @@ vim.v.cmdarg = ...
--- @type integer
vim.v.cmdbang = ...
---- When evaluating 'findexpr': if 'findexpr' is used for cmdline
---- completion the value is `v:true`, otherwise it is `v:false`.
---- @type boolean
-vim.v.cmdcomplete = ...
-
--- The current locale setting for collation order of the runtime
--- environment. This allows Vim scripts to be aware of the
--- current locale encoding. Technical: it's the value of
@@ -272,8 +267,7 @@ vim.v.fcs_choice = ...
vim.v.fcs_reason = ...
--- When evaluating 'includeexpr': the file name that was
---- detected. When evaluating 'findexpr': the argument passed to
---- the `:find` command. Empty otherwise.
+--- detected. Empty otherwise.
--- @type string
vim.v.fname = ...
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index f6928c6428..6caf25c588 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -588,6 +588,7 @@ local extension = {
ibi = 'ibasic',
icn = 'icon',
idl = detect.idl,
+ idr = 'idris2',
inc = detect.inc,
inf = 'inform',
INF = 'inform',
@@ -595,6 +596,7 @@ local extension = {
inko = 'inko',
inp = detect.inp,
ms = detect_seq(detect.nroff, 'xmath'),
+ ipkg = 'ipkg',
iss = 'iss',
mst = 'ist',
ist = 'ist',
@@ -668,12 +670,14 @@ local extension = {
journal = 'ledger',
ldg = 'ledger',
ledger = 'ledger',
+ leo = 'leo',
less = 'less',
lex = 'lex',
lxx = 'lex',
['l++'] = 'lex',
l = 'lex',
lhs = 'lhaskell',
+ lidr = 'lidris2',
ll = 'lifelines',
ly = 'lilypond',
ily = 'lilypond',
@@ -1151,6 +1155,7 @@ local extension = {
sface = 'surface',
svelte = 'svelte',
svg = 'svg',
+ sw = 'sway',
swift = 'swift',
swiftinterface = 'swift',
swig = 'swig',
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 152226a757..6d7597c5ff 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -258,6 +258,33 @@ function M.implementation(opts)
get_locations(ms.textDocument_implementation, opts)
end
+--- @param results table<integer,{err: lsp.ResponseError?, result: lsp.SignatureHelp?}>
+local function process_signature_help_results(results)
+ local signatures = {} --- @type [vim.lsp.Client,lsp.SignatureInformation][]
+
+ -- Pre-process results
+ for client_id, r in pairs(results) do
+ local err = r.err
+ local client = assert(lsp.get_client_by_id(client_id))
+ if err then
+ vim.notify(
+ client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message,
+ vim.log.levels.ERROR
+ )
+ api.nvim_command('redraw')
+ else
+ local result = r.result --- @type lsp.SignatureHelp
+ if result and result.signatures and result.signatures[1] then
+ for _, sig in ipairs(result.signatures) do
+ signatures[#signatures + 1] = { client, sig }
+ end
+ end
+ end
+ end
+
+ return signatures
+end
+
local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
--- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts
@@ -270,58 +297,79 @@ local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
function M.signature_help(config)
local method = ms.textDocument_signatureHelp
- config = config or {}
+ config = config and vim.deepcopy(config) or {}
config.focus_id = method
- lsp.buf_request(0, method, client_positional_params(), function(err, result, ctx)
- local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
-
- if err then
- vim.notify(
- client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message,
- vim.log.levels.ERROR
- )
- api.nvim_command('redraw')
- return
- end
-
+ lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx)
if api.nvim_get_current_buf() ~= ctx.bufnr then
-- Ignore result since buffer changed. This happens for slow language servers.
return
end
- -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler
- -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
- if not result or not result.signatures or not result.signatures[1] then
+ local signatures = process_signature_help_results(results)
+
+ if not next(signatures) then
if config.silent ~= true then
print('No signature help available')
end
return
end
- local triggers =
- vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
-
local ft = vim.bo[ctx.bufnr].filetype
- local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
- if not lines or vim.tbl_isempty(lines) then
- if config.silent ~= true then
- print('No signature help available')
+ local total = #signatures
+ local idx = 0
+
+ --- @param update_win? integer
+ local function show_signature(update_win)
+ idx = (idx % total) + 1
+ local client, result = signatures[idx][1], signatures[idx][2]
+ --- @type string[]?
+ local triggers =
+ vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
+ local lines, hl =
+ util.convert_signature_help_to_markdown_lines({ signatures = { result } }, ft, triggers)
+ if not lines then
+ return
end
- return
+
+ local sfx = total > 1 and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or ''
+ local title = string.format('Signature Help: %s%s', client.name, sfx)
+ if config.border then
+ config.title = title
+ else
+ table.insert(lines, 1, '# ' .. title)
+ if hl then
+ hl[1] = hl[1] + 1
+ hl[3] = hl[3] + 1
+ end
+ end
+
+ config._update_win = update_win
+
+ local buf, win = util.open_floating_preview(lines, 'markdown', config)
+
+ if hl then
+ vim.api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1)
+ vim.hl.range(
+ buf,
+ sig_help_ns,
+ 'LspSignatureActiveParameter',
+ { hl[1], hl[2] },
+ { hl[3], hl[4] }
+ )
+ end
+ return buf, win
end
- local fbuf = util.open_floating_preview(lines, 'markdown', config)
+ local fbuf, fwin = show_signature()
- -- Highlight the active parameter.
- if hl then
- vim.hl.range(
- fbuf,
- sig_help_ns,
- 'LspSignatureActiveParameter',
- { hl[1], hl[2] },
- { hl[3], hl[4] }
- )
+ if total > 1 then
+ vim.keymap.set('n', '<C-s>', function()
+ show_signature(fwin)
+ end, {
+ buffer = fbuf,
+ desc = 'Cycle next signature',
+ })
end
end)
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 763cd940c3..4ca4239127 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -737,7 +737,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
if active_signature >= #signature_help.signatures or active_signature < 0 then
active_signature = 0
end
- local signature = signature_help.signatures[active_signature + 1]
+ local signature = vim.deepcopy(signature_help.signatures[active_signature + 1])
local label = signature.label
if ft then
-- wrap inside a code block for proper rendering
@@ -804,9 +804,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
active_offset[2] = active_offset[2] + #contents[1]
end
- active_hl = {}
- list_extend(active_hl, get_pos_from_offset(active_offset[1], contents) or {})
- list_extend(active_hl, get_pos_from_offset(active_offset[2], contents) or {})
+ local a_start = get_pos_from_offset(active_offset[1], contents)
+ local a_end = get_pos_from_offset(active_offset[2], contents)
+ if a_start and a_end then
+ active_hl = { a_start[1], a_start[2], a_end[1], a_end[2] }
+ end
end
return contents, active_hl
@@ -818,7 +820,7 @@ end
---@param width integer window width (in character cells)
---@param height integer window height (in character cells)
---@param opts? vim.lsp.util.open_floating_preview.Opts
----@return table Options
+---@return vim.api.keyset.win_config
function M.make_floating_popup_options(width, height, opts)
validate('opts', opts, 'table', true)
opts = opts or {}
@@ -1500,6 +1502,8 @@ end
--- to display the full window height.
--- (default: `'auto'`)
--- @field anchor_bias? 'auto'|'above'|'below'
+---
+--- @field _update_win? integer
--- Shows contents in a floating window.
---
@@ -1521,43 +1525,49 @@ function M.open_floating_preview(contents, syntax, opts)
local bufnr = api.nvim_get_current_buf()
- -- check if this popup is focusable and we need to focus
- if opts.focus_id and opts.focusable ~= false and opts.focus then
- -- Go back to previous window if we are in a focusable one
- local current_winnr = api.nvim_get_current_win()
- if vim.w[current_winnr][opts.focus_id] then
- api.nvim_command('wincmd p')
- return bufnr, current_winnr
- end
- do
- local win = find_window_by_var(opts.focus_id, bufnr)
- if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then
- -- focus and return the existing buf, win
- api.nvim_set_current_win(win)
- api.nvim_command('stopinsert')
- return api.nvim_win_get_buf(win), win
+ local floating_winnr = opts._update_win
+
+ -- Create/get the buffer
+ local floating_bufnr --- @type integer
+ if floating_winnr then
+ floating_bufnr = api.nvim_win_get_buf(floating_winnr)
+ else
+ -- check if this popup is focusable and we need to focus
+ if opts.focus_id and opts.focusable ~= false and opts.focus then
+ -- Go back to previous window if we are in a focusable one
+ local current_winnr = api.nvim_get_current_win()
+ if vim.w[current_winnr][opts.focus_id] then
+ api.nvim_command('wincmd p')
+ return bufnr, current_winnr
+ end
+ do
+ local win = find_window_by_var(opts.focus_id, bufnr)
+ if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then
+ -- focus and return the existing buf, win
+ api.nvim_set_current_win(win)
+ api.nvim_command('stopinsert')
+ return api.nvim_win_get_buf(win), win
+ end
end
end
- end
- -- check if another floating preview already exists for this buffer
- -- and close it if needed
- local existing_float = vim.b[bufnr].lsp_floating_preview
- if existing_float and api.nvim_win_is_valid(existing_float) then
- api.nvim_win_close(existing_float, true)
+ -- check if another floating preview already exists for this buffer
+ -- and close it if needed
+ local existing_float = vim.b[bufnr].lsp_floating_preview
+ if existing_float and api.nvim_win_is_valid(existing_float) then
+ api.nvim_win_close(existing_float, true)
+ end
+ floating_bufnr = api.nvim_create_buf(false, true)
end
- -- Create the buffer
- local floating_bufnr = api.nvim_create_buf(false, true)
-
-- Set up the contents, using treesitter for markdown
local do_stylize = syntax == 'markdown' and vim.g.syntax_on ~= nil
+
if do_stylize then
local width = M._make_floating_popup_size(contents, opts)
contents = M._normalize_markdown(contents, { width = width })
vim.bo[floating_bufnr].filetype = 'markdown'
vim.treesitter.start(floating_bufnr)
- api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents)
else
-- Clean up input: trim empty lines
contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true })
@@ -1565,19 +1575,47 @@ function M.open_floating_preview(contents, syntax, opts)
if syntax then
vim.bo[floating_bufnr].syntax = syntax
end
- api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents)
end
- -- Compute size of float needed to show (wrapped) lines
- if opts.wrap then
- opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0)
+ vim.bo[floating_bufnr].modifiable = true
+ api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents)
+
+ if floating_winnr then
+ api.nvim_win_set_config(floating_winnr, {
+ border = opts.border,
+ title = opts.title,
+ })
else
- opts.wrap_at = nil
- end
- local width, height = M._make_floating_popup_size(contents, opts)
+ -- Compute size of float needed to show (wrapped) lines
+ if opts.wrap then
+ opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0)
+ else
+ opts.wrap_at = nil
+ end
+
+ -- TODO(lewis6991): These function assume the current window to determine options,
+ -- therefore it won't work for opts._update_win and the current window if the floating
+ -- window
+ local width, height = M._make_floating_popup_size(contents, opts)
+ local float_option = M.make_floating_popup_options(width, height, opts)
- local float_option = M.make_floating_popup_options(width, height, opts)
- local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
+ floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
+
+ api.nvim_buf_set_keymap(
+ floating_bufnr,
+ 'n',
+ 'q',
+ '<cmd>bdelete<cr>',
+ { silent = true, noremap = true, nowait = true }
+ )
+ close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr })
+
+ -- save focus_id
+ if opts.focus_id then
+ api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr)
+ end
+ api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr)
+ end
if do_stylize then
vim.wo[floating_winnr].conceallevel = 2
@@ -1590,21 +1628,6 @@ function M.open_floating_preview(contents, syntax, opts)
vim.bo[floating_bufnr].modifiable = false
vim.bo[floating_bufnr].bufhidden = 'wipe'
- api.nvim_buf_set_keymap(
- floating_bufnr,
- 'n',
- 'q',
- '<cmd>bdelete<cr>',
- { silent = true, noremap = true, nowait = true }
- )
- close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr })
-
- -- save focus_id
- if opts.focus_id then
- api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr)
- end
- api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr)
-
return floating_bufnr, floating_winnr
end
diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua
index 7b522ca92b..d68a30cc11 100644
--- a/runtime/lua/vim/treesitter/dev.lua
+++ b/runtime/lua/vim/treesitter/dev.lua
@@ -527,7 +527,7 @@ function M.inspect_tree(opts)
end,
})
- api.nvim_create_autocmd('BufHidden', {
+ api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, {
group = group,
buffer = buf,
once = true,
@@ -665,10 +665,10 @@ function M.edit_query(lang)
api.nvim_buf_clear_namespace(query_buf, edit_ns, 0, -1)
end,
})
- api.nvim_create_autocmd('BufHidden', {
+ api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, {
group = group,
buffer = buf,
- desc = 'Close the editor window when the source buffer is hidden',
+ desc = 'Close the editor window when the source buffer is hidden or unloaded',
once = true,
callback = function()
close_win(query_win)
diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 637f9ea543..53b64d1dec 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -4,10 +4,21 @@ local health = vim.health
--- Performs a healthcheck for treesitter integration
function M.check()
- local parsers = vim.api.nvim_get_runtime_file('parser/*', true)
+ health.start('Treesitter features')
+
+ health.info(
+ string.format(
+ 'Treesitter ABI support: min %d, max %d',
+ vim.treesitter.minimum_language_version,
+ ts.language_version
+ )
+ )
- health.info(string.format('Nvim runtime ABI version: %d', ts.language_version))
+ local can_wasm = vim._ts_add_language_from_wasm ~= nil
+ health.info(string.format('WASM parser support: %s', tostring(can_wasm)))
+ health.start('Treesitter parsers')
+ local parsers = vim.api.nvim_get_runtime_file('parser/*', true)
for _, parser in pairs(parsers) do
local parsername = vim.fn.fnamemodify(parser, ':t:r')
local is_loadable, err_or_nil = pcall(ts.language.add, parsername)
@@ -28,9 +39,6 @@ function M.check()
)
end
end
-
- local can_wasm = vim._ts_add_language_from_wasm ~= nil
- health.info(string.format('Can load WASM parsers: %s', tostring(can_wasm)))
end
return M
diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim
index 2899612dce..661a34b578 100644
--- a/runtime/plugin/matchparen.vim
+++ b/runtime/plugin/matchparen.vim
@@ -17,6 +17,9 @@ endif
if !exists("g:matchparen_insert_timeout")
let g:matchparen_insert_timeout = 60
endif
+if !exists("g:matchparen_disable_cursor_hl")
+ let g:matchparen_disable_cursor_hl = 0
+endif
let s:has_matchaddpos = exists('*matchaddpos')
@@ -189,10 +192,18 @@ func s:Highlight_Matching_Pair()
" If a match is found setup match highlighting.
if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom
if s:has_matchaddpos
- call add(w:matchparen_ids, matchaddpos('MatchParen', [[c_lnum, c_col - before], [m_lnum, m_col]], 10))
+ if !g:matchparen_disable_cursor_hl
+ call add(w:matchparen_ids, matchaddpos('MatchParen', [[c_lnum, c_col - before], [m_lnum, m_col]], 10))
+ else
+ call add(w:matchparen_ids, matchaddpos('MatchParen', [[m_lnum, m_col]], 10))
+ endif
else
- exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) .
- \ 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
+ if !g:matchparen_disable_cursor_hl
+ exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) .
+ \ 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
+ else
+ exe '3match MatchParen /\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
+ endif
call add(w:matchparen_ids, 3)
endif
let w:paren_hl_on = 1
diff --git a/runtime/syntax/shared/debversions.vim b/runtime/syntax/shared/debversions.vim
index a3ad0b2624..404a0a49e3 100644
--- a/runtime/syntax/shared/debversions.vim
+++ b/runtime/syntax/shared/debversions.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Debian version information
" Maintainer: Debian Vim Maintainers
-" Last Change: 2024 Oct 31
+" Last Change: 2024 Nov 04
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/shared/debversions.vim
let s:cpo = &cpo
@@ -11,7 +11,7 @@ let g:debSharedSupportedVersions = [
\ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy',
\ 'bullseye', 'bookworm', 'trixie', 'forky',
\
- \ 'focal', 'jammy', 'mantic', 'noble', 'oracular', 'plucky',
+ \ 'focal', 'jammy', 'noble', 'oracular', 'plucky',
\ 'devel'
\ ]
let g:debSharedUnsupportedVersions = [
@@ -23,8 +23,8 @@ let g:debSharedUnsupportedVersions = [
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'trusty', 'utopic', 'vivid', 'wily', 'xenial', 'yakkety', 'zesty',
- \ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'hirsute',
- \ 'impish', 'kinetic', 'lunar', 'groovy'
+ \ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'groovy',
+ \ 'hirsute', 'impish', 'kinetic', 'lunar', 'mantic',
\ ]
let &cpo=s:cpo
diff --git a/runtime/syntax/typst.vim b/runtime/syntax/typst.vim
index 82fdadb3d5..dae1424780 100644
--- a/runtime/syntax/typst.vim
+++ b/runtime/syntax/typst.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Typst
" Maintainer: Gregory Anders <greg@gpanders.com>
-" Last Change: 2024-07-14
+" Last Change: 2024 Nov 02
" Based on: https://github.com/kaarmu/typst.vim
if exists('b:current_syntax')
@@ -18,8 +18,8 @@ syntax cluster typstCommon
" Common > Comment {{{2
syntax cluster typstComment
\ contains=typstCommentBlock,typstCommentLine
-syntax match typstCommentBlock
- \ #/\*\%(\_.\{-}\)\*/#
+syntax region typstCommentBlock
+ \ start="/\*" end="\*/" keepend
\ contains=typstCommentTodo,@Spell
syntax match typstCommentLine
\ #//.*#
diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua
index 2928361695..edf95043c5 100755
--- a/scripts/gen_eval_files.lua
+++ b/scripts/gen_eval_files.lua
@@ -717,7 +717,9 @@ local function get_option_meta()
local optinfo = vim.api.nvim_get_all_options_info()
local ret = {} --- @type table<string,vim.option_meta>
for _, o in ipairs(opts) do
- if not o.immutable and not o.hidden and o.enable_if ~= false and o.desc then
+ local is_window_option = #o.scope == 1 and o.scope[1] == 'window'
+ local is_option_hidden = o.immutable and not o.varname and not is_window_option
+ if not is_option_hidden and o.desc then
if o.full_name == 'cmdheight' then
table.insert(o.scope, 'tab')
end
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 6376011106..b3ba832fec 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -633,6 +633,36 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
set_option_to(channel_id, win, kOptReqWin, name, value, err);
}
+/// Check if option has a value in the requested scope.
+///
+/// @param opt_idx Option index in options[] table.
+/// @param req_scope Requested option scope. See OptReqScope in option.h.
+///
+/// @return true if option has a value in the requested scope, false otherwise.
+static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope)
+{
+ if (opt_idx == kOptInvalid) {
+ return false;
+ }
+
+ vimoption_T *opt = get_option(opt_idx);
+
+ // TTY option.
+ if (is_tty_option(opt->fullname)) {
+ return req_scope == kOptReqGlobal;
+ }
+
+ switch (req_scope) {
+ case kOptReqGlobal:
+ return opt->var != VAR_WIN;
+ case kOptReqBuf:
+ return opt->indir & PV_BUF;
+ case kOptReqWin:
+ return opt->indir & PV_WIN;
+ }
+ UNREACHABLE;
+}
+
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
@@ -647,9 +677,15 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er
return (Object)OBJECT_INIT;
});
- OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err);
- if (ERROR_SET(err)) {
- return (Object)OBJECT_INIT;
+ OptIndex opt_idx = find_option(name.data);
+ OptVal value = NIL_OPTVAL;
+
+ if (option_has_scope(opt_idx, req_scope)) {
+ value = get_option_value_for(opt_idx, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL,
+ req_scope, from, err);
+ if (ERROR_SET(err)) {
+ return (Object)OBJECT_INIT;
+ }
}
VALIDATE_S(value.type != kOptValTypeNil, "option name", name.data, {
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 1a0edd551e..96866d80ba 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -80,7 +80,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
*opt_idxp = find_option(name);
int flags = get_option_attrs(*opt_idxp);
if (flags == 0) {
- // hidden or unknown option
+ // unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
} else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
@@ -175,7 +175,6 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
}
OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
- bool hidden = is_option_hidden(opt_idx);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -189,7 +188,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
goto err;
}
- VALIDATE_S(!hidden && value.type != kOptValTypeNil, "option", name.data, {
+ VALIDATE_S(value.type != kOptValTypeNil, "option", name.data, {
goto err;
});
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 42cc745fe6..fe22742a84 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -2049,7 +2049,6 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
clear_string_option(&buf->b_p_indk);
clear_string_option(&buf->b_p_fp);
clear_string_option(&buf->b_p_fex);
- clear_string_option(&buf->b_p_fexpr);
clear_string_option(&buf->b_p_kp);
clear_string_option(&buf->b_p_mps);
clear_string_option(&buf->b_p_fo);
@@ -2098,6 +2097,8 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_tfu);
callback_free(&buf->b_tfu_cb);
+ clear_string_option(&buf->b_p_ffu);
+ callback_free(&buf->b_ffu_cb);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 88e8d59faa..e6bd63f4f8 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -543,8 +543,10 @@ struct file_buffer {
Callback b_cfu_cb; ///< 'completefunc' callback
char *b_p_ofu; ///< 'omnifunc'
Callback b_ofu_cb; ///< 'omnifunc' callback
- char *b_p_tfu; ///< 'tagfunc'
+ char *b_p_tfu; ///< 'tagfunc' option value
Callback b_tfu_cb; ///< 'tagfunc' callback
+ char *b_p_ffu; ///< 'findfunc' option value
+ Callback b_ffu_cb; ///< 'findfunc' callback
int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
@@ -608,7 +610,6 @@ struct file_buffer {
char *b_p_mp; ///< 'makeprg' local value
char *b_p_efm; ///< 'errorformat' local value
char *b_p_ep; ///< 'equalprg' local value
- char *b_p_fexpr; ///< 'findexpr' local value
char *b_p_path; ///< 'path' local value
int b_p_ar; ///< 'autoread' local value
char *b_p_tags; ///< 'tags' local value
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index aeaed536fc..8d1f87cbcf 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -109,7 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_FILES
&& xp->xp_context != EXPAND_FILES_IN_PATH
&& xp->xp_context != EXPAND_FILETYPE
- && xp->xp_context != EXPAND_FINDEXPR
+ && xp->xp_context != EXPAND_FINDFUNC
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_KEYMAP
&& xp->xp_context != EXPAND_LUA
@@ -1229,7 +1229,7 @@ char *addstar(char *fname, size_t len, int context)
// For help tags the translation is done in find_help_tags().
// For a tag pattern starting with "/" no translation is needed.
- if (context == EXPAND_FINDEXPR
+ if (context == EXPAND_FINDFUNC
|| context == EXPAND_HELP
|| context == EXPAND_COLORS
|| context == EXPAND_COMPILER
@@ -1829,7 +1829,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) {
- xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR : EXPAND_FILES_IN_PATH;
+ xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC : EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
@@ -2500,8 +2500,8 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
int ret = FAIL;
- if (xp->xp_context == EXPAND_FINDEXPR) {
- ret = expand_findexpr(pat, matches, numMatches);
+ if (xp->xp_context == EXPAND_FINDFUNC) {
+ ret = expand_findfunc(pat, matches, numMatches);
} else {
if (xp->xp_context == EXPAND_FILES) {
flags |= EW_FILE;
@@ -2722,7 +2722,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH
- || xp->xp_context == EXPAND_FINDEXPR
+ || xp->xp_context == EXPAND_FINDFUNC
|| xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
}
diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h
index ce51f30bec..dfda9fdaed 100644
--- a/src/nvim/cmdexpand_defs.h
+++ b/src/nvim/cmdexpand_defs.h
@@ -107,7 +107,7 @@ enum {
EXPAND_KEYMAP,
EXPAND_DIRS_IN_CDPATH,
EXPAND_SHELLCMDLINE,
- EXPAND_FINDEXPR,
+ EXPAND_FINDFUNC,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index d22fb65827..a690c70875 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -801,7 +801,7 @@ static int diff_write(buf_T *buf, diffin_T *din)
// Always use 'fileformat' set to "unix".
char *save_ff = buf->b_p_ff;
- buf->b_p_ff = xstrdup(FF_UNIX);
+ buf->b_p_ff = xstrdup("unix");
const bool save_cmod_flags = cmdmod.cmod_flags;
// Writing the buffer is an implementation detail of performing the diff,
// so it shouldn't update the '[ and '] marks.
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
index 6682a42d61..7897a71489 100644
--- a/src/nvim/errors.h
+++ b/src/nvim/errors.h
@@ -186,7 +186,7 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")
EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
-EXTERN const char e_invalid_return_type_from_findexpr[] INIT( = N_("E1514: 'findexpr' did not return a List type"));
+EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type"));
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index bf85ed1646..58c98c42ff 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -270,7 +270,6 @@ static struct vimvar {
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
- VV(VV_CMDCOMPLETE, "cmdcomplete", VAR_BOOL, VV_RO),
// Neovim
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
@@ -462,7 +461,6 @@ void eval_init(void)
set_vim_var_nr(VV_HLSEARCH, 1);
set_vim_var_nr(VV_COUNT1, 1);
set_vim_var_special(VV_EXITING, kSpecialVarNull);
- set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse);
set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
@@ -4793,6 +4791,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_ffu_cb, copyID, NULL, NULL);
}
// 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
@@ -4804,6 +4803,9 @@ bool garbage_collect(bool testing)
// 'tagfunc' callback
ABORTING(set_ref_in_tagfunc)(copyID);
+ // 'findfunc' callback
+ ABORTING(set_ref_in_findfunc)(copyID);
+
FOR_ALL_TAB_WINDOWS(tp, wp) {
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index b5605bb644..bb9b00abc7 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -167,7 +167,6 @@ typedef enum {
VV_COLLATE,
VV_EXITING,
VV_MAXCOL,
- VV_CMDCOMPLETE,
// Nvim
VV_STDERR,
VV_MSGPACK_TYPES,
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 74ba19b30a..92695aa4de 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5165,55 +5165,68 @@ static void ex_wrongmodifier(exarg_T *eap)
eap->errmsg = _(e_invcmd);
}
-/// Evaluate the 'findexpr' expression and return the result. When evaluating
-/// the expression, v:fname is set to the ":find" command argument.
-static list_T *eval_findexpr(const char *pat, bool cmdcomplete)
+/// callback function for 'findfunc'
+static Callback ffu_cb;
+
+static Callback *get_findfunc_callback(void)
+{
+ return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
+}
+
+/// Call 'findfunc' to obtain a list of file names.
+static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete)
{
const sctx_T saved_sctx = current_sctx;
- char *findexpr = get_findexpr();
+ typval_T args[3];
+ args[0].v_type = VAR_STRING;
+ args[0].vval.v_string = pat;
+ args[1].v_type = VAR_BOOL;
+ args[1].vval.v_bool = cmdcomplete;
+ args[2].v_type = VAR_UNKNOWN;
- set_vim_var_string(VV_FNAME, pat, -1);
- set_vim_var_bool(VV_CMDCOMPLETE, cmdcomplete ? kBoolVarTrue : kBoolVarFalse);
- current_sctx = curbuf->b_p_script_ctx[BV_FEXPR].script_ctx;
+ // Lock the text to prevent weird things from happening. Also disallow
+ // switching to another window, it should not be needed and may end up in
+ // Insert mode in another buffer.
+ textlock++;
- char *arg = skipwhite(findexpr);
+ sctx_T *ctx = get_option_sctx(kOptFindfunc);
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
- textlock++;
+ Callback *cb = get_findfunc_callback();
+ typval_T rettv;
+ int retval = callback_call(cb, 2, args, &rettv);
+
+ current_sctx = saved_sctx;
+
+ textlock--;
- // Evaluate the expression. If the expression is "FuncName()" call the
- // function directly.
- typval_T tv;
list_T *retlist = NULL;
- if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
- retlist = NULL;
- } else {
- if (tv.v_type == VAR_LIST) {
- retlist = tv_list_copy(NULL, tv.vval.v_list, true, get_copyID());
+
+ if (retval == OK) {
+ if (rettv.v_type == VAR_LIST) {
+ retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID());
} else {
- emsg(_(e_invalid_return_type_from_findexpr));
+ emsg(_(e_invalid_return_type_from_findfunc));
}
- tv_clear(&tv);
- }
- textlock--;
- clear_evalarg(&EVALARG_EVALUATE, NULL);
- set_vim_var_string(VV_FNAME, NULL, 0);
- set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse);
- current_sctx = saved_sctx;
+ tv_clear(&rettv);
+ }
return retlist;
}
-/// Find file names matching "pat" using 'findexpr' and return it in "files".
+/// Find file names matching "pat" using 'findfunc' and return it in "files".
/// Used for expanding the :find, :sfind and :tabfind command argument.
/// Returns OK on success and FAIL otherwise.
-int expand_findexpr(const char *pat, char ***files, int *numMatches)
+int expand_findfunc(char *pat, char ***files, int *numMatches)
{
*numMatches = 0;
*files = NULL;
- list_T *l = eval_findexpr(pat, true);
+ list_T *l = call_findfunc(pat, kBoolVarTrue);
if (l == NULL) {
return FAIL;
}
@@ -5240,16 +5253,16 @@ int expand_findexpr(const char *pat, char ***files, int *numMatches)
return OK;
}
-/// Use 'findexpr' to find file 'findarg'. The 'count' argument is used to find
+/// Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find
/// the n'th matching file.
-static char *findexpr_find_file(char *findarg, size_t findarg_len, int count)
+static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
{
char *ret_fname = NULL;
const char cc = findarg[findarg_len];
findarg[findarg_len] = NUL;
- list_T *fname_list = eval_findexpr(findarg, false);
+ list_T *fname_list = call_findfunc(findarg, kBoolVarFalse);
int fname_count = tv_list_len(fname_list);
if (fname_count == 0) {
@@ -5274,6 +5287,55 @@ static char *findexpr_find_file(char *findarg, size_t findarg_len, int count)
return ret_fname;
}
+/// Process the 'findfunc' option value.
+/// Returns NULL on success and an error message on failure.
+const char *did_set_findfunc(optset_T *args)
+{
+ buf_T *buf = (buf_T *)args->os_buf;
+ int retval;
+
+ if (args->os_flags & OPT_LOCAL) {
+ // buffer-local option set
+ retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb);
+ } else {
+ // global option set
+ retval = option_set_callback_func(p_ffu, &ffu_cb);
+ // when using :set, free the local callback
+ if (!(args->os_flags & OPT_GLOBAL)) {
+ callback_free(&buf->b_ffu_cb);
+ }
+ }
+
+ if (retval == FAIL) {
+ return e_invarg;
+ }
+
+ // If the option value starts with <SID> or s:, then replace that with
+ // the script identifier.
+ char **varp = (char **)args->os_varp;
+ char *name = get_scriptlocal_funcname(*varp);
+ if (name != NULL) {
+ free_string_option(*varp);
+ *varp = name;
+ }
+
+ return NULL;
+}
+
+void free_findfunc_option(void)
+{
+ callback_free(&ffu_cb);
+}
+
+/// Mark the global 'findfunc' callback with "copyID" so that it is not
+/// garbage collected.
+bool set_ref_in_findfunc(int copyID)
+{
+ bool abort = false;
+ abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL);
+ return abort;
+}
+
/// :sview [+command] file split window with new file, read-only
/// :split [[+command] file] split window with current or new file
/// :vsplit [[+command] file] split window vertically with current or new file
@@ -5305,8 +5367,8 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
- if (*get_findexpr() != NUL) {
- fname = findexpr_find_file(eap->arg, strlen(eap->arg),
+ if (*get_findfunc() != NUL) {
+ fname = findfunc_find_file(eap->arg, strlen(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
} else {
char *file_to_find = NULL;
@@ -5512,8 +5574,8 @@ static void ex_find(exarg_T *eap)
}
char *fname = NULL;
- if (*get_findexpr() != NUL) {
- fname = findexpr_find_file(eap->arg, strlen(eap->arg),
+ if (*get_findfunc() != NUL) {
+ fname = findfunc_find_file(eap->arg, strlen(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
} else {
char *file_to_find = NULL;
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 0cb5fa8e95..92349b5298 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -191,23 +191,17 @@ local function dump_option(i, o)
w(get_cond(o.enable_if))
end
- -- An option cannot be both hidden and immutable.
- assert(not o.hidden or not o.immutable)
-
- local has_var = true
if o.varname then
w(' .var=&' .. o.varname)
- elseif o.hidden or o.immutable then
- -- Hidden and immutable options can directly point to the default value.
+ elseif o.immutable then
+ -- Immutable options can directly point to the default value.
w((' .var=&options[%u].def_val.data'):format(i - 1))
elseif #o.scope == 1 and o.scope[1] == 'window' then
w(' .var=VAR_WIN')
else
- has_var = false
+ -- Option must be immutable or have a variable.
+ assert(false)
end
- -- `enable_if = false` should be present iff there is no variable.
- assert((o.enable_if == false) == not has_var)
- w(' .hidden=' .. (o.hidden and 'true' or 'false'))
w(' .immutable=' .. (o.immutable and 'true' or 'false'))
if #o.scope == 1 and o.scope[1] == 'global' then
w(' .indir=PV_NONE')
@@ -237,7 +231,10 @@ local function dump_option(i, o)
end
if o.enable_if then
w('#else')
- w(' .var=NULL')
+ -- Hidden option directly points to default value.
+ w((' .var=&options[%u].def_val.data'):format(i - 1))
+ -- Option is always immutable on the false branch of `enable_if`.
+ w(' .immutable=true')
w(' .indir=PV_NONE')
w('#endif')
end
@@ -260,6 +257,7 @@ local function dump_option(i, o)
end
w([[
+#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/insexpand.h"
#include "nvim/mapping.h"
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 19a79ff810..8fa3092fd6 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -7,10 +7,6 @@
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
-// Includes final NUL. MAX_MCO is no longer used, but at least 4*(MAX_MCO+1)+1=29
-// ensures we can fit all composed chars which did fit before.
-#define MAX_SCHAR_SIZE 32
-
enum {
kZIndexDefaultGrid = 0,
kZIndexFloatDefault = 50,
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 2482cef7a1..c8314d1bb2 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -2368,13 +2368,13 @@ static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
/// Invoked when the 'completefunc' option is set. The option value can be a
/// name of a function (string), or function(<name>) or funcref(<name>) or a
/// lambda expression.
-const char *did_set_completefunc(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_completefunc(optset_T *args)
{
- if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL) {
+ buf_T *buf = (buf_T *)args->os_buf;
+ if (option_set_callback_func(buf->b_p_cfu, &cfu_cb) == FAIL) {
return e_invarg;
}
-
- set_buflocal_cfu_callback(curbuf);
+ set_buflocal_cfu_callback(buf);
return NULL;
}
@@ -2412,14 +2412,19 @@ void set_buflocal_ofu_callback(buf_T *buf)
/// lambda expression.
const char *did_set_thesaurusfunc(optset_T *args FUNC_ATTR_UNUSED)
{
+ buf_T *buf = (buf_T *)args->os_buf;
int retval;
- if (*curbuf->b_p_tsrfu != NUL) {
+ if (args->os_flags & OPT_LOCAL) {
// buffer-local option set
- retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
+ retval = option_set_callback_func(buf->b_p_tsrfu, &buf->b_tsrfu_cb);
} else {
// global option set
retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
+ // when using :set, free the local callback
+ if (!(args->os_flags & OPT_GLOBAL)) {
+ callback_free(&buf->b_tsrfu_cb);
+ }
}
return retval == FAIL ? e_invarg : NULL;
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 6340ff8c94..65f718f925 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -839,6 +839,13 @@ bool utf_composinglike(const char *p1, const char *p2, GraphemeState *state)
return arabic_combine(first, second);
}
+/// same as utf_composinglike but operating on UCS-4 values
+bool utf_iscomposing(int c1, int c2, GraphemeState *state)
+{
+ return (!utf8proc_grapheme_break_stateful(c1, c2, state)
+ || arabic_combine(c1, c2));
+}
+
/// Get the screen char at the beginning of a string
///
/// Caller is expected to check for things like unprintable chars etc
@@ -1852,8 +1859,7 @@ StrCharInfo utfc_next_impl(StrCharInfo cur)
while (true) {
uint8_t const next_len = utf8len_tab[*next];
int32_t const next_code = utf_ptr2CharInfo_impl(next, (uintptr_t)next_len);
- if (utf8proc_grapheme_break_stateful(prev_code, next_code, &state)
- && !arabic_combine(prev_code, next_code)) {
+ if (!utf_iscomposing(prev_code, next_code, &state)) {
return (StrCharInfo){
.ptr = (char *)next,
.chr = (CharInfo){ .value = next_code, .len = (next_code < 0 ? 1 : next_len) },
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index be9987cc7f..aa247e39e6 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -835,21 +835,29 @@ static void normal_get_additional_char(NormalState *s)
// because if it's put back with vungetc() it's too late to apply
// mapping.
no_mapping--;
+ GraphemeState state = GRAPHEME_STATE_INIT;
+ int prev_code = s->ca.nchar;
+
while ((s->c = vpeekc()) > 0
&& (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) {
s->c = plain_vgetc();
- // TODO(bfredl): only allowing up to two composing chars is cringe af.
- // Could reuse/abuse schar_T to at least allow us to input anything we are able
- // to display and use the stateful utf8proc algorithm like utf_composinglike
- if (!utf_iscomposing_legacy(s->c)) {
+
+ if (!utf_iscomposing(prev_code, s->c, &state)) {
vungetc(s->c); // it wasn't, put it back
break;
- } else if (s->ca.ncharC1 == 0) {
- s->ca.ncharC1 = s->c;
- } else {
- s->ca.ncharC2 = s->c;
}
+
+ // first composing char, first put base char into buffer
+ if (s->ca.nchar_len == 0) {
+ s->ca.nchar_len = utf_char2bytes(s->ca.nchar, s->ca.nchar_composing);
+ }
+
+ if (s->ca.nchar_len + utf_char2len(s->c) < (int)sizeof(s->ca.nchar_composing)) {
+ s->ca.nchar_len += utf_char2bytes(s->c, s->ca.nchar_composing + s->ca.nchar_len);
+ }
+ prev_code = s->c;
}
+ s->ca.nchar_composing[s->ca.nchar_len] = NUL;
no_mapping++;
// Vim may be in a different mode when the user types the next key,
// but when replaying a recording the next key is already in the
@@ -1735,7 +1743,12 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
static void prep_redo_cmd(cmdarg_T *cap)
{
prep_redo(cap->oap->regname, cap->count0,
- NUL, cap->cmdchar, NUL, NUL, cap->nchar);
+ NUL, cap->cmdchar, NUL, NUL, NUL);
+ if (cap->nchar_len > 0) {
+ AppendToRedobuff(cap->nchar_composing);
+ } else {
+ AppendCharToRedobuff(cap->nchar);
+ }
}
/// Prepare for redo of any command.
@@ -4548,17 +4561,15 @@ static void nv_replace(cmdarg_T *cap)
// Give 'r' to edit(), to get the redo command right.
invoke_edit(cap, true, 'r', false);
} else {
- prep_redo(cap->oap->regname, cap->count1,
- NUL, 'r', NUL, had_ctrl_v, cap->nchar);
+ prep_redo(cap->oap->regname, cap->count1, NUL, 'r', NUL, had_ctrl_v, 0);
curbuf->b_op_start = curwin->w_cursor;
const int old_State = State;
- if (cap->ncharC1 != 0) {
- AppendCharToRedobuff(cap->ncharC1);
- }
- if (cap->ncharC2 != 0) {
- AppendCharToRedobuff(cap->ncharC2);
+ if (cap->nchar_len > 0) {
+ AppendToRedobuff(cap->nchar_composing);
+ } else {
+ AppendCharToRedobuff(cap->nchar);
}
// This is slow, but it handles replacing a single-byte with a
@@ -4576,15 +4587,13 @@ static void nv_replace(cmdarg_T *cap)
curwin->w_cursor.col++;
}
} else {
- ins_char(cap->nchar);
+ if (cap->nchar_len) {
+ ins_char_bytes(cap->nchar_composing, (size_t)cap->nchar_len);
+ } else {
+ ins_char(cap->nchar);
+ }
}
State = old_State;
- if (cap->ncharC1 != 0) {
- ins_char(cap->ncharC1);
- }
- if (cap->ncharC2 != 0) {
- ins_char(cap->ncharC2);
- }
}
curwin->w_cursor.col--; // cursor on the last replaced char
// if the character on the left of the current cursor is a multi-byte
diff --git a/src/nvim/normal_defs.h b/src/nvim/normal_defs.h
index 0309f6bc80..7b49b28a0f 100644
--- a/src/nvim/normal_defs.h
+++ b/src/nvim/normal_defs.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h"
/// Motion types, used for operators and for yank/delete registers.
///
@@ -47,8 +48,8 @@ typedef struct {
int prechar; ///< prefix character (optional, always 'g')
int cmdchar; ///< command character
int nchar; ///< next command character (optional)
- int ncharC1; ///< first composing character (optional)
- int ncharC2; ///< second composing character (optional)
+ char nchar_composing[MAX_SCHAR_SIZE]; ///< next char with composing chars (optional)
+ int nchar_len; ///< len of nchar_composing (when zero, use nchar instead)
int extra_char; ///< yet another character (optional)
int opcount; ///< count before an operator
int count0; ///< count before command, default 0
diff --git a/src/nvim/option.c b/src/nvim/option.c
index a8ec5b2919..0396f7740e 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -565,19 +565,23 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
void free_all_options(void)
{
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
- if (options[opt_idx].indir == PV_NONE) {
+ bool hidden = is_option_hidden(opt_idx);
+
+ if (options[opt_idx].indir == PV_NONE || hidden) {
// global option: free value and default value.
- if (options[opt_idx].var != NULL) {
+ // hidden option: free default value only.
+ if (!hidden) {
optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
}
} else if (options[opt_idx].var != VAR_WIN) {
- // buffer-local option: free global value
+ // buffer-local option: free global value.
optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
}
optval_free(options[opt_idx].def_val);
}
free_operatorfunc_option();
free_tagfunc_option();
+ free_findfunc_option();
XFREE_CLEAR(fenc_default);
XFREE_CLEAR(p_term);
XFREE_CLEAR(p_ttytype);
@@ -645,10 +649,8 @@ void set_init_3(void)
xfree(p);
if (buf_is_empty(curbuf)) {
- int idx_ffs = find_option("ffs");
-
// Apply the first entry of 'fileformats' to the initial buffer.
- if (idx_ffs >= 0 && (options[idx_ffs].flags & kOptFlagWasSet)) {
+ if (options[kOptFileformats].flags & kOptFlagWasSet) {
set_fileformat(default_fileformat(), OPT_LOCAL);
}
}
@@ -1240,21 +1242,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
}
uint8_t nextchar = (uint8_t)(*p); // next non-white char after option name
- uint32_t flags = 0; // flags for current option
- void *varp = NULL; // pointer to variable for current option
-
- if (options[opt_idx].var == NULL) { // hidden option: skip
- // Only give an error message when requesting the value of
- // a hidden option, ignore setting it.
- if (vim_strchr("=:!&<", nextchar) == NULL
- && (!option_has_type(opt_idx, kOptValTypeBoolean) || nextchar == '?')) {
- *errmsg = e_unsupportedoption;
- }
- return;
- }
-
- flags = options[opt_idx].flags;
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ // flags for current option
+ uint32_t flags = options[opt_idx].flags;
+ // pointer to variable for current option
+ void *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) {
return;
@@ -1324,11 +1315,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
}
}
- // Don't try to change hidden option.
- if (varp == NULL) {
- return;
- }
-
OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp,
errbuf, errbuflen, errmsg);
@@ -1336,8 +1322,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
return;
}
- *errmsg = set_option(opt_idx, varp, newval, opt_flags, 0, false, op == OP_NONE, errbuf,
- errbuflen);
+ *errmsg = set_option(opt_idx, newval, opt_flags, 0, false, op == OP_NONE, errbuf, errbuflen);
}
/// Parse 'arg' for option settings.
@@ -1586,7 +1571,7 @@ char *find_shada_parameter(int type)
static char *option_expand(OptIndex opt_idx, char *val)
{
// if option doesn't need expansion nothing to do
- if (!(options[opt_idx].flags & kOptFlagExpand) || options[opt_idx].var == NULL) {
+ if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
return NULL;
}
@@ -1901,7 +1886,7 @@ static const char *did_set_arabic(optset_T *args)
// set rightleft mode
if (!win->w_p_rl) {
win->w_p_rl = true;
- changed_window_setting(curwin);
+ changed_window_setting(win);
}
// Enable Arabic shaping (major part of what Arabic requires)
@@ -1932,7 +1917,7 @@ static const char *did_set_arabic(optset_T *args)
// reset rightleft mode
if (win->w_p_rl) {
win->w_p_rl = false;
- changed_window_setting(curwin);
+ changed_window_setting(win);
}
// 'arabicshape' isn't reset, it is a global option and
@@ -1943,8 +1928,8 @@ static const char *did_set_arabic(optset_T *args)
// window may still want it "on".
// Revert to the default keymap
- curbuf->b_p_iminsert = B_IMODE_NONE;
- curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
+ win->w_buffer->b_p_iminsert = B_IMODE_NONE;
+ win->w_buffer->b_p_imsearch = B_IMODE_USE_INSERT;
}
return errmsg;
@@ -2066,9 +2051,7 @@ static const char *did_set_helpheight(optset_T *args)
{
// Change window height NOW
if (!ONE_WINDOW) {
- buf_T *buf = (buf_T *)args->os_buf;
- win_T *win = (win_T *)args->os_win;
- if (buf->b_help && win->w_height < p_hh) {
+ if (curbuf->b_help && curwin->w_height < p_hh) {
win_setheight((int)p_hh);
}
}
@@ -2397,14 +2380,16 @@ static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED)
/// Process the updated 'readonly' option value.
static const char *did_set_readonly(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
+
// when 'readonly' is reset globally, also reset readonlymode
- if (!curbuf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) {
+ if (!buf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) {
readonlymode = false;
}
// when 'readonly' is set may give W10 again
- if (curbuf->b_p_ro) {
- curbuf->b_did_warn = false;
+ if (buf->b_p_ro) {
+ buf->b_did_warn = false;
}
redraw_titles();
@@ -2520,8 +2505,7 @@ static const char *did_set_swapfile(optset_T *args)
if (buf->b_p_swf && p_uc) {
ml_open_file(buf); // create the swap file
} else {
- // no need to reset curbuf->b_may_swap, ml_open_file() will check
- // buf->b_p_swf
+ // no need to reset buf->b_may_swap, ml_open_file() will check buf->b_p_swf
mf_close_file(buf, true); // remove the swap file
}
return NULL;
@@ -2561,8 +2545,10 @@ static const char *did_set_titlelen(optset_T *args)
/// Process the updated 'undofile' option value.
static const char *did_set_undofile(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
+
// Only take action when the option was set.
- if (!curbuf->b_p_udf && !p_udf) {
+ if (!buf->b_p_udf && !p_udf) {
return NULL;
}
@@ -2575,7 +2561,7 @@ static const char *did_set_undofile(optset_T *args)
// only for the current buffer: Try to read in the undofile,
// if one exists, the buffer wasn't changed and the buffer was
// loaded
- if ((curbuf == bp
+ if ((buf == bp
|| (args->os_flags & OPT_GLOBAL) || args->os_flags == 0)
&& !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) {
u_compute_hash(bp, hash);
@@ -2615,7 +2601,7 @@ static const char *did_set_undolevels(optset_T *args)
if (pp == &p_ul) { // global 'undolevels'
did_set_global_undolevels(args->os_newval.number, args->os_oldval.number);
- } else if (pp == &curbuf->b_p_ul) { // buffer local 'undolevels'
+ } else if (pp == &buf->b_p_ul) { // buffer local 'undolevels'
did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number);
}
@@ -2680,8 +2666,7 @@ static const char *did_set_winheight(optset_T *args)
{
// Change window height NOW
if (!ONE_WINDOW) {
- win_T *win = (win_T *)args->os_win;
- if (win->w_height < p_wh) {
+ if (curwin->w_height < p_wh) {
win_setheight((int)p_wh);
}
}
@@ -2692,9 +2677,7 @@ static const char *did_set_winheight(optset_T *args)
/// Process the new 'winwidth' option value.
static const char *did_set_winwidth(optset_T *args)
{
- win_T *win = (win_T *)args->os_win;
-
- if (!ONE_WINDOW && win->w_width < p_wiw) {
+ if (!ONE_WINDOW && curwin->w_width < p_wiw) {
win_setwidth((int)p_wiw);
}
return NULL;
@@ -2757,15 +2740,14 @@ static void do_spelllang_source(win_T *win)
/// Check the bounds of numeric options.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Pointer to option variable.
/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value.
/// @param[out] errbuf Buffer for error message. Cannot be NULL.
/// @param errbuflen Length of error buffer.
///
/// @return Error message, if any.
-static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt *newval,
- char *errbuf, size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(4)
+static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, char *errbuf,
+ size_t errbuflen)
+ FUNC_ATTR_NONNULL_ARG(3)
{
const char *errmsg = NULL;
@@ -2798,9 +2780,7 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt
}
break;
case kOptScroll:
- if (varp == &(curwin->w_p_scr)
- && (*newval <= 0
- || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0))
+ if ((*newval <= 0 || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0))
&& full_screen) {
if (*newval != 0) {
errmsg = e_scroll;
@@ -2818,13 +2798,12 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt
/// Validate and bound check option value.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Pointer to option variable.
/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value.
/// @param[out] errbuf Buffer for error message. Cannot be NULL.
/// @param errbuflen Length of error buffer.
///
/// @return Error message, if any.
-static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *newval, char *errbuf,
+static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *errbuf,
size_t errbuflen)
{
OptInt value = *newval;
@@ -2834,143 +2813,137 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new
return e_invarg;
}
- if (varp == &p_wh) {
+ switch (opt_idx) {
+ case kOptHelpheight:
+ case kOptTitlelen:
+ case kOptUpdatecount:
+ case kOptReport:
+ case kOptUpdatetime:
+ case kOptSidescroll:
+ case kOptFoldlevel:
+ case kOptShiftwidth:
+ case kOptTextwidth:
+ case kOptWritedelay:
+ case kOptTimeoutlen:
+ if (value < 0) {
+ return e_positive;
+ }
+ break;
+ case kOptWinheight:
if (value < 1) {
return e_positive;
} else if (p_wmh > value) {
return e_winheight;
}
- } else if (varp == &p_hh) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_wmh) {
+ break;
+ case kOptWinminheight:
if (value < 0) {
return e_positive;
} else if (value > p_wh) {
return e_winheight;
}
- } else if (varp == &p_wiw) {
+ break;
+ case kOptWinwidth:
if (value < 1) {
return e_positive;
} else if (p_wmw > value) {
return e_winwidth;
}
- } else if (varp == &p_wmw) {
+ break;
+ case kOptWinminwidth:
if (value < 0) {
return e_positive;
} else if (value > p_wiw) {
return e_winwidth;
}
- } else if (varp == &p_titlelen) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_uc) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_ch) {
+ break;
+ case kOptMaxcombine:
+ *newval = MAX_MCO;
+ break;
+ case kOptCmdheight:
if (value < 0) {
return e_positive;
} else {
p_ch_was_zero = value == 0;
}
- } else if (varp == &p_tm) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_hi) {
+ break;
+ case kOptHistory:
if (value < 0) {
return e_positive;
} else if (value > 10000) {
return e_invarg;
}
- } else if (varp == &p_pyx) {
+ break;
+ case kOptPyxversion:
if (value == 0) {
*newval = 3;
} else if (value != 3) {
return e_invarg;
}
- } else if (varp == &p_re) {
+ break;
+ case kOptRegexpengine:
if (value < 0 || value > 2) {
return e_invarg;
}
- } else if (varp == &p_report) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_so) {
+ break;
+ case kOptScrolloff:
if (value < 0 && full_screen) {
return e_positive;
}
- } else if (varp == &p_siso) {
+ break;
+ case kOptSidescrolloff:
if (value < 0 && full_screen) {
return e_positive;
}
- } else if (varp == &p_cwh) {
+ break;
+ case kOptCmdwinheight:
if (value < 1) {
return e_positive;
}
- } else if (varp == &p_ut) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_ss) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curwin->w_p_fdl || varp == &curwin->w_allbuf_opt.wo_fdl) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curwin->w_p_cole || varp == &curwin->w_allbuf_opt.wo_cole) {
+ break;
+ case kOptConceallevel:
if (value < 0) {
return e_positive;
} else if (value > 3) {
return e_invarg;
}
- } else if (varp == &curwin->w_p_nuw || varp == &curwin->w_allbuf_opt.wo_nuw) {
+ break;
+ case kOptNumberwidth:
if (value < 1) {
return e_positive;
} else if (value > MAX_NUMBERWIDTH) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_iminsert || varp == &p_iminsert) {
+ break;
+ case kOptIminsert:
if (value < 0 || value > B_IMODE_LAST) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_imsearch || varp == &p_imsearch) {
+ break;
+ case kOptImsearch:
if (value < -1 || value > B_IMODE_LAST) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_channel || varp == &p_channel) {
+ break;
+ case kOptChannel:
return e_invarg;
- } else if (varp == &curbuf->b_p_scbk || varp == &p_scbk) {
+ case kOptScrollback:
if (value < -1 || value > SB_MAX) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_sw || varp == &p_sw) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curbuf->b_p_ts || varp == &p_ts) {
+ break;
+ case kOptTabstop:
if (value < 1) {
return e_positive;
} else if (value > TABSTOP_MAX) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_tw || varp == &p_tw) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_wd) {
- if (value < 0) {
- return e_positive;
- }
+ break;
+ default:
+ break;
}
- return check_num_option_bounds(opt_idx, varp, newval, errbuf, errbuflen);
+ return check_num_option_bounds(opt_idx, newval, errbuf, errbuflen);
}
/// Called after an option changed: check if something needs to be redrawn.
@@ -3135,7 +3108,8 @@ bool optval_equal(OptVal o1, OptVal o2)
return o1.data.number == o2.data.number;
case kOptValTypeString:
return o1.data.string.size == o2.data.string.size
- && strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size);
+ && (o1.data.string.data == o2.data.string.data
+ || strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size));
}
UNREACHABLE;
}
@@ -3160,6 +3134,7 @@ static OptValType option_get_type(const OptIndex opt_idx)
///
/// @return Option value stored in varp.
OptVal optval_from_varp(OptIndex opt_idx, void *varp)
+ FUNC_ATTR_NONNULL_ARG(2)
{
// Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc'
// changed.
@@ -3169,7 +3144,7 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
if (option_is_multitype(opt_idx)) {
// Multitype options are stored as OptVal.
- return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp;
+ return *(OptVal *)varp;
}
OptValType type = option_get_type(opt_idx);
@@ -3178,11 +3153,11 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
case kOptValTypeNil:
return NIL_OPTVAL;
case kOptValTypeBoolean:
- return BOOLEAN_OPTVAL(varp == NULL ? false : TRISTATE_FROM_INT(*(int *)varp));
+ return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp));
case kOptValTypeNumber:
- return NUMBER_OPTVAL(varp == NULL ? 0 : *(OptInt *)varp);
+ return NUMBER_OPTVAL(*(OptInt *)varp);
case kOptValTypeString:
- return STRING_OPTVAL(varp == NULL ? (String)STRING_INIT : cstr_as_string(*(char **)varp));
+ return STRING_OPTVAL(cstr_as_string(*(char **)varp));
}
UNREACHABLE;
}
@@ -3318,7 +3293,10 @@ static char *option_get_valid_types(OptIndex opt_idx)
/// @return True if option is hidden, false otherwise. Returns false if option name is invalid.
bool is_option_hidden(OptIndex opt_idx)
{
- return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL;
+ // Hidden options are always immutable and point to their default value
+ return opt_idx == kOptInvalid
+ ? false
+ : (options[opt_idx].immutable && options[opt_idx].var == &options[opt_idx].def_val.data);
}
static inline bool option_is_global_local(OptIndex opt_idx)
@@ -3458,8 +3436,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
.os_win = curwin
};
- if (direct || opt->hidden) {
- // Don't do any extra processing if setting directly or if option is hidden.
+ if (direct) {
+ // Don't do any extra processing if setting directly.
}
// Disallow changing immutable options.
else if (opt->immutable && !optval_equal(old_value, new_value)) {
@@ -3488,7 +3466,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
// If option is hidden or if an error is detected, restore the previous value and don't do any
// further processing.
- if (opt->hidden || errmsg != NULL) {
+ if (errmsg != NULL) {
set_option_varp(opt_idx, varp, old_value, true);
// When resetting some values, need to act on it.
if (restore_chartab) {
@@ -3593,14 +3571,19 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
/// Validate the new value for an option.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param varp Pointer to option variable.
/// @param newval[in,out] New option value. Might be modified.
-static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval,
- int opt_flags, char *errbuf, size_t errbuflen)
+static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, int opt_flags,
+ char *errbuf, size_t errbuflen)
{
const char *errmsg = NULL;
vimoption_T *opt = &options[opt_idx];
+ // Always allow unsetting local value of global-local option.
+ if (option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL)
+ && optval_equal(*newval, get_option_unset_value(opt_idx))) {
+ return NULL;
+ }
+
if (newval->type == kOptValTypeNil) {
// Don't try to unset local value if scope is global.
// TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is
@@ -3620,7 +3603,7 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
errmsg = errbuf;
} else if (newval->type == kOptValTypeNumber) {
// Validate and bound check num option values.
- errmsg = validate_num_option(opt_idx, varp, &newval->data.number, errbuf, errbuflen);
+ errmsg = validate_num_option(opt_idx, &newval->data.number, errbuf, errbuflen);
}
return errmsg;
@@ -3629,7 +3612,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
/// Set the value of an option using an OptVal.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Option variable pointer, cannot be NULL.
/// @param value New option value. Might get freed.
/// @param opt_flags Option flags.
/// @param set_sid Script ID. Special values:
@@ -3641,17 +3623,16 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
/// @param errbuflen Length of error buffer.
///
/// @return NULL on success, an untranslated error message on error.
-static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags,
- scid_T set_sid, const bool direct, const bool value_replaced,
- char *errbuf, size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(2)
+static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
+ const bool direct, const bool value_replaced, char *errbuf,
+ size_t errbuflen)
{
assert(opt_idx != kOptInvalid);
const char *errmsg = NULL;
if (!direct) {
- errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
+ errmsg = validate_option_value(opt_idx, &value, opt_flags, errbuf, errbuflen);
if (errmsg != NULL) {
optval_free(value);
@@ -3668,11 +3649,9 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
const bool is_opt_local_unset = is_option_local_value_unset(opt_idx);
// When using ":set opt=val" for a global option with a local value the local value will be reset,
- // use the global value here.
- if (scope_both && option_is_global_local(opt_idx)) {
- varp = opt->var;
- }
-
+ // use the global value in that case.
+ void *varp
+ = scope_both && option_is_global_local(opt_idx) ? opt->var : get_varp_scope(opt, opt_flags);
void *varp_local = get_varp_scope(opt, OPT_LOCAL);
void *varp_global = get_varp_scope(opt, OPT_GLOBAL);
@@ -3746,16 +3725,11 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
{
static char errbuf[IOSIZE];
- vimoption_T *opt = get_option(opt_idx);
-
- if (opt->var == NULL) {
+ if (is_option_hidden(opt_idx)) {
return;
}
- const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags);
-
- const char *errmsg = set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true,
+ const char *errmsg = set_option(opt_idx, optval_copy(value), opt_flags, set_sid, true, true,
errbuf, sizeof(errbuf));
assert(errmsg == NULL);
(void)errmsg; // ignore unused warning
@@ -3817,14 +3791,7 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt
return _(e_sandbox);
}
- void *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp == NULL) {
- // hidden option is not changed
- return NULL;
- }
-
- return set_option(opt_idx, varp, optval_copy(value), opt_flags, 0, false, true, errbuf,
- sizeof(errbuf));
+ return set_option(opt_idx, optval_copy(value), opt_flags, 0, false, true, errbuf, sizeof(errbuf));
}
/// Unset the local value of a global-local option.
@@ -3959,11 +3926,6 @@ int get_option_attrs(OptIndex opt_idx)
vimoption_T *opt = get_option(opt_idx);
- // Hidden option
- if (opt->var == NULL) {
- return 0;
- }
-
int attrs = 0;
if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) {
@@ -3975,79 +3937,10 @@ int get_option_attrs(OptIndex opt_idx)
attrs |= SOPT_BUF;
}
+ assert(attrs != 0);
return attrs;
}
-/// Check if option has a value in the requested scope.
-///
-/// @param opt_idx Option index in options[] table.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-///
-/// @return true if option has a value in the requested scope, false otherwise.
-static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope)
-{
- if (opt_idx == kOptInvalid) {
- return false;
- }
-
- vimoption_T *opt = get_option(opt_idx);
-
- // Hidden option.
- if (opt->var == NULL) {
- return false;
- }
- // TTY option.
- if (is_tty_option(opt->fullname)) {
- return req_scope == kOptReqGlobal;
- }
-
- switch (req_scope) {
- case kOptReqGlobal:
- return opt->var != VAR_WIN;
- case kOptReqBuf:
- return opt->indir & PV_BUF;
- case kOptReqWin:
- return opt->indir & PV_WIN;
- }
- UNREACHABLE;
-}
-
-/// Get the option value in the requested scope.
-///
-/// @param opt_idx Option index in options[] table.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Pointer to buffer or window for local option value.
-/// @param[out] err Error message, if any.
-///
-/// @return Option value in the requested scope. Returns a Nil option value if option is not found,
-/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or
-/// buffer-local value depending on opt_scope).
-OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err)
-{
- if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) {
- return NIL_OPTVAL;
- }
-
- vimoption_T *opt = get_option(opt_idx);
- switchwin_T switchwin;
- aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
- if (ERROR_SET(err)) {
- return NIL_OPTVAL;
- }
-
- char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL);
- OptVal retv = optval_from_varp(opt_idx, varp);
-
- if (switched) {
- restore_option_context(ctx, req_scope);
- }
-
- return retv;
-}
-
/// Get option value for buffer / window.
///
/// @param opt_idx Option index in options[] table.
@@ -4212,8 +4105,8 @@ static int optval_default(OptIndex opt_idx, void *varp)
{
vimoption_T *opt = &options[opt_idx];
- // Hidden or immutable options always use their default value.
- if (varp == NULL || opt->hidden || opt->immutable) {
+ // Hidden options always use their default value.
+ if (is_option_hidden(opt_idx)) {
return true;
}
@@ -4542,8 +4435,8 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
switch ((int)p->indir) {
case PV_FP:
return &(buf->b_p_fp);
- case PV_FEXPR:
- return &(buf->b_p_fexpr);
+ case PV_FFU:
+ return &(buf->b_p_ffu);
case PV_EFM:
return &(buf->b_p_efm);
case PV_GP:
@@ -4623,9 +4516,9 @@ void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T
void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{
- // hidden option, always return NULL
- if (p->var == NULL) {
- return NULL;
+ // hidden options always use the same var pointer
+ if (is_option_hidden(get_opt_idx(p))) {
+ return p->var;
}
switch ((int)p->indir) {
@@ -4665,8 +4558,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var;
case PV_FP:
return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var;
- case PV_FEXPR:
- return *buf->b_p_fexpr != NUL ? &(buf->b_p_fexpr) : p->var;
+ case PV_FFU:
+ return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var;
case PV_EFM:
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
case PV_GP:
@@ -4938,13 +4831,13 @@ char *get_equalprg(void)
return curbuf->b_p_ep;
}
-/// Get the value of 'findexpr', either the buffer-local one or the global one.
-char *get_findexpr(void)
+/// Get the value of 'findfunc', either the buffer-local one or the global one.
+char *get_findfunc(void)
{
- if (*curbuf->b_p_fexpr == NUL) {
- return p_fexpr;
+ if (*curbuf->b_p_ffu == NUL) {
+ return p_ffu;
}
- return curbuf->b_p_fexpr;
+ return curbuf->b_p_ffu;
}
/// Copy options from one window to another.
@@ -5184,13 +5077,13 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_fenc = xstrdup(p_fenc);
switch (*p_ffs) {
case 'm':
- buf->b_p_ff = xstrdup(FF_MAC);
+ buf->b_p_ff = xstrdup("mac");
break;
case 'd':
- buf->b_p_ff = xstrdup(FF_DOS);
+ buf->b_p_ff = xstrdup("dos");
break;
case 'u':
- buf->b_p_ff = xstrdup(FF_UNIX);
+ buf->b_p_ff = xstrdup("unix");
break;
default:
buf->b_p_ff = xstrdup(p_ff);
@@ -5345,8 +5238,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_mp = empty_string_option;
buf->b_p_efm = empty_string_option;
buf->b_p_ep = empty_string_option;
- buf->b_p_fexpr = xstrdup(p_fexpr);
- COPY_OPT_SCTX(buf, BV_FEXPR);
+ buf->b_p_ffu = empty_string_option;
buf->b_p_kp = empty_string_option;
buf->b_p_path = empty_string_option;
buf->b_p_tags = empty_string_option;
@@ -5526,7 +5418,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
nextchar = *p;
opt_idx = find_option_len(arg, (size_t)(p - arg));
- if (opt_idx == kOptInvalid || options[opt_idx].var == NULL) {
+ if (opt_idx == kOptInvalid || is_option_hidden(opt_idx)) {
xp->xp_context = EXPAND_NOTHING;
return;
}
@@ -5750,7 +5642,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM
char *str;
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
str = options[opt_idx].fullname;
- if (options[opt_idx].var == NULL) {
+ if (is_option_hidden(opt_idx)) {
continue;
}
if (xp->xp_context == EXPAND_BOOL_SETTINGS
@@ -5998,6 +5890,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
static void option_value2string(vimoption_T *opt, int scope)
{
void *varp = get_varp_scope(opt, scope);
+ assert(varp != NULL);
if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
OptInt wc = 0;
@@ -6014,9 +5907,8 @@ static void option_value2string(vimoption_T *opt, int scope)
}
} else { // string
varp = *(char **)(varp);
- if (varp == NULL) { // Just in case.
- NameBuff[0] = NUL;
- } else if (opt->flags & kOptFlagExpand) {
+
+ if (opt->flags & kOptFlagExpand) {
home_replace(NULL, varp, NameBuff, MAXPATHL, false);
} else {
xstrlcpy(NameBuff, varp, MAXPATHL);
@@ -6320,13 +6212,13 @@ void set_fileformat(int eol_style, int opt_flags)
switch (eol_style) {
case EOL_UNIX:
- p = FF_UNIX;
+ p = "unix";
break;
case EOL_MAC:
- p = FF_MAC;
+ p = "mac";
break;
case EOL_DOS:
- p = FF_DOS;
+ p = "dos";
break;
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 9b74429467..138d90da97 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -49,7 +49,6 @@ typedef struct {
///< buffer-local option: global value
idopt_T indir; ///< global option: PV_NONE;
///< local option: indirect option index
- bool hidden; ///< option is hidden, any attempt to set its value will be ignored.
bool immutable; ///< option is immutable, trying to set its value will give an error.
/// callback function to invoke after an option is modified to validate and
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index a88b51dae7..a60bd047c1 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -31,21 +31,6 @@
#define DFLT_GREPFORMAT "%f:%l:%m,%f:%l%m,%f %l%m"
-// default values for b_p_ff 'fileformat' and p_ffs 'fileformats'
-#define FF_DOS "dos"
-#define FF_MAC "mac"
-#define FF_UNIX "unix"
-
-#ifdef USE_CRNL
-# define DFLT_FF "dos"
-# define DFLT_FFS_VIM "dos,unix"
-# define DFLT_FFS_VI "dos,unix" // also autodetect in compatible mode
-#else
-# define DFLT_FF "unix"
-# define DFLT_FFS_VIM "unix,dos"
-# define DFLT_FFS_VI ""
-#endif
-
// Possible values for 'encoding'
#define ENC_UCSBOM "ucs-bom" // check for BOM at start of file
@@ -451,7 +436,7 @@ EXTERN char *p_ffs; ///< 'fileformats'
EXTERN int p_fic; ///< 'fileignorecase'
EXTERN char *p_ft; ///< 'filetype'
EXTERN char *p_fcs; ///< 'fillchar'
-EXTERN char *p_fexpr; ///< 'findexpr'
+EXTERN char *p_ffu; ///< 'findfunc'
EXTERN int p_fixeol; ///< 'fixendofline'
EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
@@ -530,6 +515,7 @@ EXTERN char *p_mef; ///< 'makeef'
EXTERN char *p_mp; ///< 'makeprg'
EXTERN char *p_mps; ///< 'matchpairs'
EXTERN OptInt p_mat; ///< 'matchtime'
+EXTERN OptInt p_mco; ///< 'maxcombine'
EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 1b8e3ea256..e648d4350a 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -13,7 +13,7 @@
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
---- @field enable_if? string|false
+--- @field enable_if? string
--- @field defaults? vim.option_defaults
--- @field secure? true
--- @field noglob? true
@@ -87,11 +87,11 @@ return {
{
abbreviation = 'al',
defaults = { if_true = 224 },
- enable_if = false,
full_name = 'aleph',
scope = { 'global' },
short_desc = N_('ASCII code of the letter Aleph (Hebrew)'),
type = 'number',
+ immutable = true,
},
{
abbreviation = 'ari',
@@ -789,11 +789,11 @@ return {
current Use the current directory.
{path} Use the specified directory
]=],
- enable_if = false,
full_name = 'browsedir',
scope = { 'global' },
short_desc = N_('which directory to start browsing in'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'bh',
@@ -1493,7 +1493,7 @@ return {
cb = 'did_set_completeslash',
defaults = { if_true = '' },
desc = [=[
- only for MS-Windows
+ only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion:
- When this option is set to "slash", a forward slash is used for path
completion in insert mode. This is useful when editing HTML tag, or
@@ -2684,7 +2684,9 @@ return {
abbreviation = 'ff',
cb = 'did_set_fileformat',
defaults = {
- if_true = macros('DFLT_FF', 'string'),
+ condition = 'USE_CRNL',
+ if_true = 'dos',
+ if_false = 'unix',
doc = 'Windows: "dos", Unix: "unix"',
},
desc = [=[
@@ -2717,7 +2719,9 @@ return {
abbreviation = 'ffs',
cb = 'did_set_fileformats',
defaults = {
- if_true = macros('DFLT_FFS_VIM', 'string'),
+ condition = 'USE_CRNL',
+ if_true = 'dos,unix',
+ if_false = 'unix,dos',
doc = 'Windows: "dos,unix", Unix: "unix,dos"',
},
deny_duplicates = true,
@@ -2906,35 +2910,35 @@ return {
varname = 'p_fcs',
},
{
- abbreviation = 'fexpr',
- cb = 'did_set_optexpr',
+ abbreviation = 'ffu',
+ cb = 'did_set_findfunc',
defaults = { if_true = '' },
desc = [=[
- Expression that is evaluated to obtain the filename(s) for the |:find|
+ Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
mechanism is used.
- While evaluating the expression, the |v:fname| variable is set to the
- argument of the |:find| command.
+ The value can be the name of a function, a |lambda| or a |Funcref|.
+ See |option-value-function| for more information.
- The expression is evaluated only once per |:find| command invocation.
- The expression can process all the directories specified in 'path'.
+ The function is called with two arguments. The first argument is a
+ |String| and is the |:find| command argument. The second argument is
+ a |Boolean| and is set to |v:true| when the function is called to get
+ a List of command-line completion matches for the |:find| command.
+ The function should return a List of strings.
- The expression may be evaluated for command-line completion as well,
- in which case the |v:cmdcomplete| variable will be set to |v:true|,
- otherwise it will be set to |v:false|.
+ The function is called only once per |:find| command invocation.
+ The function can process all the directories specified in 'path'.
- If a match is found, the expression should return a |List| containing
- one or more file names. If a match is not found, the expression
+ If a match is found, the function should return a |List| containing
+ one or more file names. If a match is not found, the function
should return an empty List.
- If any errors are encountered during the expression evaluation, an
+ If any errors are encountered during the function invocation, an
empty List is used as the return value.
- Using a function call without arguments is faster |expr-option-function|
-
It is not allowed to change text or jump to another window while
- evaluating 'findexpr' |textlock|.
+ executing the 'findfunc' |textlock|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -2942,27 +2946,28 @@ return {
Examples:
>vim
" Use glob()
- func FindExprGlob()
- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+ func FindFuncGlob(cmdarg, cmdcomplete)
+ let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
- set findexpr=FindExprGlob()
+ set findfunc=FindFuncGlob
" Use the 'git ls-files' output
- func FindGitFiles()
+ func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
- return fnames->filter('v:val =~? v:fname')
+ return fnames->filter('v:val =~? a:cmdarg')
endfunc
- set findexpr=FindGitFiles()
+ set findfunc=FindGitFiles
<
]=],
- full_name = 'findexpr',
+ full_name = 'findfunc',
+ func = true,
scope = { 'global', 'buffer' },
secure = true,
- short_desc = N_('expression used for :find'),
+ short_desc = N_('function called for :find'),
tags = { 'E1514' },
type = 'string',
- varname = 'p_fexpr',
+ varname = 'p_ffu',
},
{
abbreviation = 'fixeol',
@@ -3790,12 +3795,12 @@ return {
try to keep 'lines' and 'columns' the same when adding and
removing GUI components.
]=],
- enable_if = false,
full_name = 'guioptions',
list = 'flags',
scope = { 'global' },
short_desc = N_('GUI: Which components and options are used'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'gtl',
@@ -3815,13 +3820,13 @@ return {
present in 'guioptions'. For the non-GUI tab pages line 'tabline' is
used.
]=],
- enable_if = false,
full_name = 'guitablabel',
modelineexpr = true,
redraw = { 'current_window' },
scope = { 'global' },
short_desc = N_('GUI: custom label for a tab page'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'gtt',
@@ -3834,12 +3839,12 @@ return {
let &guitabtooltip = "line one\nline two"
<
]=],
- enable_if = false,
full_name = 'guitabtooltip',
redraw = { 'current_window' },
scope = { 'global' },
short_desc = N_('GUI: custom tooltip for a tab page'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'hf',
@@ -4081,11 +4086,11 @@ return {
English characters directly, e.g., when it's used to type accented
characters with dead keys.
]=],
- enable_if = false,
full_name = 'imcmdline',
scope = { 'global' },
short_desc = N_('use IM when starting to edit a command line'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'imd',
@@ -4099,11 +4104,11 @@ return {
Currently this option is on by default for SGI/IRIX machines. This
may change in later releases.
]=],
- enable_if = false,
full_name = 'imdisable',
scope = { 'global' },
short_desc = N_('do not use the IM in any mode'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'imi',
@@ -5235,7 +5240,7 @@ return {
scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'),
type = 'number',
- hidden = true,
+ varname = 'p_mco',
},
{
abbreviation = 'mfd',
@@ -5731,13 +5736,13 @@ return {
indicate no input when the hit-enter prompt is displayed (since
clicking the mouse has no effect in this state.)
]=],
- enable_if = false,
full_name = 'mouseshape',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('shape of the mouse pointer in different modes'),
tags = { 'E547' },
type = 'string',
+ immutable = true,
},
{
abbreviation = 'mouset',
@@ -5897,11 +5902,11 @@ return {
Note that on Windows editing "aux.h", "lpt1.txt" and the like also
result in editing a device.
]=],
- enable_if = false,
full_name = 'opendevice',
scope = { 'global' },
short_desc = N_('allow reading/writing devices on MS-Windows'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'opfunc',
@@ -5974,11 +5979,11 @@ return {
{
abbreviation = 'pt',
defaults = { if_true = '' },
- enable_if = false,
full_name = 'pastetoggle',
scope = { 'global' },
short_desc = N_('No description'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'pex',
@@ -7269,9 +7274,14 @@ return {
{
abbreviation = 'ssl',
cb = 'did_set_shellslash',
- defaults = { if_true = false },
+ defaults = {
+ condition = 'MSWIN',
+ if_true = false,
+ if_false = true,
+ doc = 'on, Windows: off',
+ },
desc = [=[
- only for MS-Windows
+ only modifiable in MS-Windows
When set, a forward slash is used when expanding file names. This is
useful when a Unix-like shell is used instead of cmd.exe. Backward
slashes can still be typed, but they are changed to forward slashes by
@@ -8884,11 +8894,11 @@ return {
{
abbreviation = 'tenc',
defaults = { if_true = '' },
- enable_if = false,
full_name = 'termencoding',
scope = { 'global' },
short_desc = N_('Terminal encoding'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'tgc',
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index c66849800c..bfb26a0be6 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -84,7 +84,7 @@ static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase",
"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", "blank", NULL };
-static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
+static char *(p_ff_values[]) = { "unix", "dos", "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()
@@ -233,9 +233,9 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_mp);
check_string_option(&buf->b_p_efm);
check_string_option(&buf->b_p_ep);
- check_string_option(&buf->b_p_fexpr);
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
+ check_string_option(&buf->b_p_ffu);
check_string_option(&buf->b_p_tfu);
check_string_option(&buf->b_p_tc);
check_string_option(&buf->b_p_dict);
@@ -655,6 +655,9 @@ const char *did_set_backupcopy(optset_T *args)
if (opt_flags & OPT_LOCAL) {
bkc = buf->b_p_bkc;
flags = &buf->b_bkc_flags;
+ } else if (!(opt_flags & OPT_GLOBAL)) {
+ // When using :set, clear the local flags.
+ buf->b_bkc_flags = 0;
}
if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
@@ -889,10 +892,11 @@ int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches)
}
/// The 'cinoptions' option is changed.
-const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_cinoptions(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
// TODO(vim): recognize errors
- parse_cino(curbuf);
+ parse_cino(buf);
return NULL;
}
@@ -1070,6 +1074,9 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
if (args->os_flags & OPT_LOCAL) {
cot = buf->b_p_cot;
flags = &buf->b_cot_flags;
+ } else if (!(args->os_flags & OPT_GLOBAL)) {
+ // When using :set, clear the local flags.
+ buf->b_cot_flags = 0;
}
if (check_opt_strings(cot, p_cot_values, true) != OK) {
@@ -1886,9 +1893,8 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
matches);
}
-/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr',
-/// 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr',
-/// 'patchexpr' and 'charconvert'.
+/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
+/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
const char *did_set_optexpr(optset_T *args)
{
char **varp = (char **)args->os_varp;
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 7c5293a8b8..2d17581bac 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -289,9 +289,10 @@ size_t input_enqueue(String keys)
unsigned new_size
= trans_special(&ptr, (size_t)(end - ptr), (char *)buf, FSK_KEYCODE, true, NULL);
- if (new_size) {
- new_size = handle_mouse_event(&ptr, buf, new_size);
- input_enqueue_raw((char *)buf, new_size);
+ if (new_size > 0) {
+ if ((new_size = handle_mouse_event(&ptr, buf, new_size)) > 0) {
+ input_enqueue_raw((char *)buf, new_size);
+ }
continue;
}
@@ -326,7 +327,7 @@ size_t input_enqueue(String keys)
return rv;
}
-static uint8_t check_multiclick(int code, int grid, int row, int col)
+static uint8_t check_multiclick(int code, int grid, int row, int col, bool *skip_event)
{
static int orig_num_clicks = 0;
static int orig_mouse_code = 0;
@@ -335,24 +336,29 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
static int orig_mouse_row = 0;
static uint64_t orig_mouse_time = 0; // time of previous mouse click
- if ((code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) || code == KE_MOUSEMOVE) {
+ if (code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) {
return 0;
}
- // For click events the number of clicks is updated.
- if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE
- || code == KE_X1MOUSE || code == KE_X2MOUSE) {
+ bool no_move = orig_mouse_grid == grid && orig_mouse_col == col && orig_mouse_row == row;
+
+ if (code == KE_MOUSEMOVE) {
+ if (no_move) {
+ *skip_event = true;
+ return 0;
+ }
+ } else if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE
+ || code == KE_X1MOUSE || code == KE_X2MOUSE) {
+ // For click events the number of clicks is updated.
uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
+ // Compute the time elapsed since the previous mouse click.
uint64_t timediff = mouse_time - orig_mouse_time;
+ // Convert 'mousetime' from ms to ns.
uint64_t mouset = (uint64_t)p_mouset * 1000000;
if (code == orig_mouse_code
+ && no_move
&& timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_grid == grid
- && orig_mouse_col == col
- && orig_mouse_row == row) {
+ && orig_num_clicks != 4) {
orig_num_clicks++;
} else {
orig_num_clicks = 1;
@@ -367,12 +373,14 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
orig_mouse_row = row;
uint8_t modifiers = 0;
- if (orig_num_clicks == 2) {
- modifiers |= MOD_MASK_2CLICK;
- } else if (orig_num_clicks == 3) {
- modifiers |= MOD_MASK_3CLICK;
- } else if (orig_num_clicks == 4) {
- modifiers |= MOD_MASK_4CLICK;
+ if (code != KE_MOUSEMOVE) {
+ if (orig_num_clicks == 2) {
+ modifiers |= MOD_MASK_2CLICK;
+ } else if (orig_num_clicks == 3) {
+ modifiers |= MOD_MASK_3CLICK;
+ } else if (orig_num_clicks == 4) {
+ modifiers |= MOD_MASK_4CLICK;
+ }
}
return modifiers;
}
@@ -399,7 +407,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
return bufsize;
}
- // a <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
+ // A <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
// global variables. This is ugly but its how the rest of the code expects to
// find mouse coordinates, and it would be too expensive to refactor this
// now.
@@ -421,8 +429,12 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
*ptr += advance;
}
+ bool skip_event = false;
uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
- mouse_row, mouse_col);
+ mouse_row, mouse_col, &skip_event);
+ if (skip_event) {
+ return 0;
+ }
if (modifiers) {
if (buf[1] != KS_MODIFIER) {
@@ -443,7 +455,11 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
void input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col)
{
- modifier |= check_multiclick(code, grid, row, col);
+ bool skip_event = false;
+ modifier |= check_multiclick(code, grid, row, col, &skip_event);
+ if (skip_event) {
+ return;
+ }
uint8_t buf[7];
uint8_t *p = buf;
if (modifier) {
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 529d65c5dc..7df6a1a5d7 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -657,11 +657,14 @@ void pum_redraw(void)
pum_align_order(order);
int basic_width = items_width_array[order[0]]; // first item width
bool last_isabbr = order[2] == CPT_ABBR;
+ int orig_attr = -1;
+
for (int j = 0; j < 3; j++) {
int item_type = order[j];
hlf = hlfs[item_type];
attr = win_hl_attr(curwin, (int)hlf);
- int orig_attr = attr;
+ attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
+ orig_attr = attr;
int user_abbr_hlattr = pum_array[idx].pum_user_abbr_hlattr;
int user_kind_hlattr = pum_array[idx].pum_user_kind_hlattr;
if (item_type == CPT_ABBR && user_abbr_hlattr > 0) {
@@ -670,7 +673,6 @@ void pum_redraw(void)
if (item_type == CPT_KIND && user_kind_hlattr > 0) {
attr = hl_combine_attr(attr, user_kind_hlattr);
}
- attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
int width = 0;
char *s = NULL;
p = pum_get_item(idx, item_type);
@@ -796,9 +798,9 @@ void pum_redraw(void)
}
if (pum_rl) {
- grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), attr);
+ grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), orig_attr);
} else {
- grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), attr);
+ grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), orig_attr);
}
if (pum_scrollbar > 0) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2a935f6878..5d3d3db3fe 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -113,7 +113,7 @@ static int last_idx = 0; // index in spats[] for RE_LAST
static uint8_t lastc[2] = { NUL, NUL }; // last character searched for
static Direction lastcdir = FORWARD; // last direction of character search
static bool last_t_cmd = true; // last search t_cmd
-static char lastc_bytes[MB_MAXBYTES + 1];
+static char lastc_bytes[MAX_SCHAR_SIZE + 1];
static int lastc_bytelen = 1; // >1 for multi-byte char
// copy of spats[], for keeping the search patterns while executing autocmds
@@ -1550,14 +1550,11 @@ int searchc(cmdarg_T *cap, bool t_cmd)
*lastc = (uint8_t)c;
set_csearch_direction(dir);
set_csearch_until(t_cmd);
- lastc_bytelen = utf_char2bytes(c, lastc_bytes);
- if (cap->ncharC1 != 0) {
- lastc_bytelen += utf_char2bytes(cap->ncharC1,
- lastc_bytes + lastc_bytelen);
- if (cap->ncharC2 != 0) {
- lastc_bytelen += utf_char2bytes(cap->ncharC2,
- lastc_bytes + lastc_bytelen);
- }
+ if (cap->nchar_len) {
+ lastc_bytelen = cap->nchar_len;
+ memcpy(lastc_bytes, cap->nchar_composing, (size_t)cap->nchar_len);
+ } else {
+ lastc_bytelen = utf_char2bytes(c, lastc_bytes);
}
}
} else { // repeat previous search
diff --git a/src/nvim/types_defs.h b/src/nvim/types_defs.h
index 2dd2b01adf..bec0950653 100644
--- a/src/nvim/types_defs.h
+++ b/src/nvim/types_defs.h
@@ -12,6 +12,10 @@ typedef int32_t sattr_T;
// must be at least as big as the biggest of schar_T, sattr_T, colnr_T
typedef int32_t sscratch_T;
+// Includes final NUL. MAX_MCO is no longer used, but at least 4*(MAX_MCO+1)+1=29
+// ensures we can fit all composed chars which did fit before.
+#define MAX_SCHAR_SIZE 32
+
// Opaque handle used by API clients to refer to various objects in vim
typedef int handle_T;
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index 6c6edd4ee2..ad139bbbfe 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -50,13 +50,6 @@ M.vars = {
can be used.
]=],
},
- cmdcomplete = {
- type = 'boolean',
- desc = [=[
- When evaluating 'findexpr': if 'findexpr' is used for cmdline
- completion the value is |v:true|, otherwise it is |v:false|.
- ]=],
- },
collate = {
type = 'string',
desc = [=[
@@ -291,8 +284,7 @@ M.vars = {
type = 'string',
desc = [=[
When evaluating 'includeexpr': the file name that was
- detected. When evaluating 'findexpr': the argument passed to
- the |:find| command. Empty otherwise.
+ detected. Empty otherwise.
]=],
},
fname_diff = {
diff --git a/test/functional/editor/defaults_spec.lua b/test/functional/editor/defaults_spec.lua
index 70f12ab475..9786bfeaac 100644
--- a/test/functional/editor/defaults_spec.lua
+++ b/test/functional/editor/defaults_spec.lua
@@ -152,6 +152,54 @@ describe('default', function()
]],
})
end)
+
+ describe('[<Space>', function()
+ it('adds an empty line above the current line', function()
+ n.clear({ args_rm = { '--cmd' } })
+ n.insert([[first line]])
+ n.feed('[<Space>')
+ n.expect([[
+
+ first line]])
+ end)
+
+ it('works with a count', function()
+ n.clear({ args_rm = { '--cmd' } })
+ n.insert([[first line]])
+ n.feed('5[<Space>')
+ n.expect([[
+
+
+
+
+
+ first line]])
+ end)
+ end)
+
+ describe(']<Space>', function()
+ it('adds an empty line below the current line', function()
+ n.clear({ args_rm = { '--cmd' } })
+ n.insert([[first line]])
+ n.feed(']<Space>')
+ n.expect([[
+ first line
+ ]])
+ end)
+
+ it('works with a count', function()
+ n.clear({ args_rm = { '--cmd' } })
+ n.insert([[first line]])
+ n.feed('5]<Space>')
+ n.expect([[
+ first line
+
+
+
+
+ ]])
+ end)
+ end)
end)
end)
end)
diff --git a/test/functional/editor/mode_normal_spec.lua b/test/functional/editor/mode_normal_spec.lua
index b3ef4866dc..cca244e06c 100644
--- a/test/functional/editor/mode_normal_spec.lua
+++ b/test/functional/editor/mode_normal_spec.lua
@@ -9,6 +9,7 @@ local feed = n.feed
local fn = n.fn
local command = n.command
local eq = t.eq
+local api = n.api
describe('Normal mode', function()
before_each(clear)
@@ -41,4 +42,23 @@ describe('Normal mode', function()
attr_ids = {},
})
end)
+
+ it('replacing with ZWJ emoji sequences', function()
+ local screen = Screen.new(30, 8)
+ screen:attach()
+ api.nvim_buf_set_lines(0, 0, -1, true, { 'abcdefg' })
+ feed('05r🧑‍🌾') -- ZWJ
+ screen:expect([[
+ 🧑‍🌾🧑‍🌾🧑‍🌾🧑‍🌾^🧑‍🌾fg |
+ {1:~ }|*6
+ |
+ ]])
+
+ feed('2r🏳️‍⚧️') -- ZWJ and variant selectors
+ screen:expect([[
+ 🧑‍🌾🧑‍🌾🧑‍🌾🧑‍🌾🏳️‍⚧️^🏳️‍⚧️g |
+ {1:~ }|*6
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua
index f63363d6d9..6705dff847 100644
--- a/test/functional/lua/runtime_spec.lua
+++ b/test/functional/lua/runtime_spec.lua
@@ -21,7 +21,9 @@ describe('runtime:', function()
exec('set rtp+=' .. plug_dir)
exec([[
set shell=doesnotexist
- set completeslash=slash
+ if exists('+completeslash')
+ set completeslash=slash
+ endif
set isfname+=(,)
]])
end)
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
index 8a2281e2a1..60b2f872fc 100644
--- a/test/functional/testnvim.lua
+++ b/test/functional/testnvim.lua
@@ -920,36 +920,38 @@ function M.exec_lua(code, ...)
return M.api.nvim_exec_lua(code, { ... })
end
- assert(session)
+ assert(session, 'no Nvim session')
if not session.exec_lua_setup then
- M.api.nvim_exec_lua(
- [[
- _G.__test_exec_lua = {
- get_upvalues = loadstring((select(1,...))),
- set_upvalues = loadstring((select(2,...))),
- handler = loadstring((select(3,...)))
- }
- setmetatable(_G.__test_exec_lua, { __index = _G.__test_exec_lua })
- ]],
- { string.dump(get_upvalues), string.dump(set_upvalues), string.dump(exec_lua_handler) }
+ assert(
+ session:request(
+ 'nvim_exec_lua',
+ [[
+ _G.__test_exec_lua = {
+ get_upvalues = loadstring((select(1,...))),
+ set_upvalues = loadstring((select(2,...))),
+ handler = loadstring((select(3,...)))
+ }
+ setmetatable(_G.__test_exec_lua, { __index = _G.__test_exec_lua })
+ ]],
+ { string.dump(get_upvalues), string.dump(set_upvalues), string.dump(exec_lua_handler) }
+ )
)
session.exec_lua_setup = true
end
+ local stat, rv = session:request(
+ 'nvim_exec_lua',
+ 'return { _G.__test_exec_lua:handler(...) }',
+ { string.dump(code), get_upvalues(code), ... }
+ )
+
+ if not stat then
+ error(rv[2])
+ end
+
--- @type any[], table<string,any>
- local ret, upvalues = unpack(M.api.nvim_exec_lua(
- [[
- return {
- _G.__test_exec_lua:handler(...)
- }
- ]],
- {
- string.dump(code),
- get_upvalues(code),
- ...,
- }
- ))
+ local ret, upvalues = unpack(rv)
-- Update upvalues
if next(upvalues) then
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index bc18680749..471ee70906 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -1908,19 +1908,40 @@ describe('ui/mouse/input', function()
eq(0, api.nvim_get_var('mouse_up2'))
end)
- it('<MouseMove> is not translated into multiclicks and can be mapped', function()
+ it('<MouseMove> to different locations can be mapped', function()
api.nvim_set_var('mouse_move', 0)
api.nvim_set_var('mouse_move2', 0)
command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
- feed('<MouseMove><0,0>')
- feed('<MouseMove><0,0>')
- api.nvim_input_mouse('move', '', '', 0, 0, 0)
- api.nvim_input_mouse('move', '', '', 0, 0, 0)
+ feed('<MouseMove><1,0>')
+ feed('<MouseMove><2,0>')
+ api.nvim_input_mouse('move', '', '', 0, 0, 3)
+ api.nvim_input_mouse('move', '', '', 0, 0, 4)
eq(4, api.nvim_get_var('mouse_move'))
eq(0, api.nvim_get_var('mouse_move2'))
end)
+ it('<MouseMove> to same location does not generate events #31103', function()
+ api.nvim_set_var('mouse_move', 0)
+ api.nvim_set_var('mouse_move2', 0)
+ command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
+ command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
+ api.nvim_input_mouse('move', '', '', 0, 0, 3)
+ eq(1, api.nvim_get_var('mouse_move'))
+ eq(0, api.nvim_get_var('mouse_move2'))
+ feed('<MouseMove><3,0>')
+ feed('<MouseMove><3,0>')
+ api.nvim_input_mouse('move', '', '', 0, 0, 3)
+ api.nvim_input_mouse('move', '', '', 0, 0, 3)
+ eq(1, api.nvim_get_var('mouse_move'))
+ eq(0, api.nvim_get_var('mouse_move2'))
+ eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
+ feed('<MouseMove><3,0><Insert>')
+ eq(1, api.nvim_get_var('mouse_move'))
+ eq(0, api.nvim_get_var('mouse_move2'))
+ eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
+ end)
+
it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function()
feed('<MouseMove>')
n.poke_eventloop()
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 295d677aa0..d096311703 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -5190,10 +5190,18 @@ describe('builtin popupmenu', function()
-- oldtest: Test_pum_user_abbr_hlgroup()
it('custom abbr_hlgroup override', function()
exec([[
- func CompleteFunc( findstart, base )
+ let s:var = 0
+ func CompleteFunc(findstart, base)
if a:findstart
return 0
endif
+ if s:var == 1
+ return {
+ \ 'words': [
+ \ { 'word': 'aword1', 'abbr_hlgroup': 'StrikeFake' },
+ \ { 'word': '你好', 'abbr_hlgroup': 'StrikeFake' },
+ \]}
+ endif
return {
\ 'words': [
\ { 'word': 'aword1', 'menu': 'extra text 1', 'kind': 'W', 'abbr_hlgroup': 'StrikeFake' },
@@ -5201,6 +5209,9 @@ describe('builtin popupmenu', function()
\ { 'word': '你好', 'menu': 'extra text 3', 'kind': 'W', 'abbr_hlgroup': 'StrikeFake' },
\]}
endfunc
+ func ChangeVar()
+ let s:var = 1
+ endfunc
set completeopt=menu
set completefunc=CompleteFunc
@@ -5243,6 +5254,17 @@ describe('builtin popupmenu', function()
{2:-- }{5:match 2 of 3} |
]])
feed('<C-E><Esc>')
+
+ command('call ChangeVar()')
+ feed('S<C-X><C-U>')
+ screen:expect([[
+ aword1^ |
+ {ds:aword1}{s: }{1: }|
+ {dn:你好}{n: }{1: }|
+ {1:~ }|*16
+ {2:-- }{5:match 1 of 2} |
+ ]])
+ feed('<C-E><Esc>')
end)
-- oldtest: Test_pum_user_kind_hlgroup()
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
index 51f260cc5e..532ec965d1 100644
--- a/test/old/testdir/gen_opt_test.vim
+++ b/test/old/testdir/gen_opt_test.vim
@@ -392,7 +392,7 @@ for option in options
let fullname = option.full_name
let shortname = get(option, 'abbreviation', fullname)
- if get(option, 'immutable', v:false)
+ if !exists('+' .. fullname)
continue
endif
diff --git a/test/old/testdir/shared.vim b/test/old/testdir/shared.vim
index fc7e6b643a..bb1a6c8f5b 100644
--- a/test/old/testdir/shared.vim
+++ b/test/old/testdir/shared.vim
@@ -35,12 +35,20 @@ func PythonProg()
if has('unix')
" We also need the job feature or the pkill command to make sure the server
" can be stopped.
- if !(executable('python') && (has('job') || executable('pkill')))
+ if !(has('job') || executable('pkill'))
return ''
endif
- let s:python = 'python'
+ if executable('python3')
+ let s:python = 'python3'
+ elseif executable('python')
+ let s:python = 'python'
+ else
+ return ''
+ end
elseif has('win32')
" Use Python Launcher for Windows (py.exe) if available.
+ " NOTE: if you get a "Python was not found" error, disable the Python
+ " shortcuts in "Windows menu / Settings / Manage App Execution Aliases".
if executable('py.exe')
let s:python = 'py.exe'
elseif executable('python.exe')
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
index c2bba8fafc..64599c869a 100644
--- a/test/old/testdir/test_autocmd.vim
+++ b/test/old/testdir/test_autocmd.vim
@@ -2003,7 +2003,10 @@ func Test_Cmdline()
au! CmdlineLeave
let save_shellslash = &shellslash
- set noshellslash
+ " Nvim doesn't allow setting value of a hidden option to non-default value
+ if exists('+shellslash')
+ set noshellslash
+ endif
au! CmdlineEnter / let g:entered = expand('<afile>')
au! CmdlineLeave / let g:left = expand('<afile>')
let g:entered = 0
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index 0ef2c33c03..8d405790e9 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -727,8 +727,8 @@ func Test_fullcommand()
\ ':5s': 'substitute',
\ "'<,'>s": 'substitute',
\ ":'<,'>s": 'substitute',
- \ 'CheckUni': 'CheckUnix',
- \ 'CheckUnix': 'CheckUnix',
+ \ 'CheckLin': 'CheckLinux',
+ \ 'CheckLinux': 'CheckLinux',
\ }
for [in, want] in items(tests)
@@ -2319,6 +2319,7 @@ endfunc
" Test for 'imcmdline' and 'imsearch'
" This test doesn't actually test the input method functionality.
func Test_cmdline_inputmethod()
+ throw 'Skipped: Nvim does not allow setting the value of a hidden option'
new
call setline(1, ['', 'abc', ''])
set imcmdline
diff --git a/test/old/testdir/test_compiler.vim b/test/old/testdir/test_compiler.vim
index 69420b4b7f..07b57b76d9 100644
--- a/test/old/testdir/test_compiler.vim
+++ b/test/old/testdir/test_compiler.vim
@@ -10,9 +10,12 @@ func Test_compiler()
let save_LC_ALL = $LC_ALL
let $LC_ALL= "C"
- " %:S does not work properly with 'shellslash' set
let save_shellslash = &shellslash
- set noshellslash
+ " Nvim doesn't allow setting value of a hidden option to non-default value
+ if exists('+shellslash')
+ " %:S does not work properly with 'shellslash' set
+ set noshellslash
+ endif
e Xfoo.pl
compiler perl
diff --git a/test/old/testdir/test_expr.vim b/test/old/testdir/test_expr.vim
index 58a385cba8..56a4c3bffa 100644
--- a/test/old/testdir/test_expr.vim
+++ b/test/old/testdir/test_expr.vim
@@ -799,10 +799,10 @@ func Test_expr_completion()
call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:)
" completion for options
- call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"echo &compatible', @:)
- call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"echo 1 && &compatible', @:)
+ "call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt')
+ "call assert_equal('"echo &compatible', @:)
+ "call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt')
+ "call assert_equal('"echo 1 && &compatible', @:)
call feedkeys(":echo &g:equala\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal('"echo &g:equalalways', @:)
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 6fc9f8e65c..f957eb23a1 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -358,12 +358,14 @@ func s:GetFilenameChecks() abort
\ 'ibasic': ['file.iba', 'file.ibi'],
\ 'icemenu': ['/.icewm/menu', 'any/.icewm/menu'],
\ 'icon': ['file.icn'],
+ \ 'idris2': ['file.idr'],
\ 'indent': ['.indent.pro', 'indentrc'],
\ 'inform': ['file.inf', 'file.INF'],
\ 'initng': ['/etc/initng/any/file.i', 'file.ii', 'any/etc/initng/any/file.i'],
\ 'inittab': ['inittab'],
\ 'inko': ['file.inko'],
\ 'ipfilter': ['ipf.conf', 'ipf6.conf', 'ipf.rules'],
+ \ 'ipkg': ['file.ipkg'],
\ 'iss': ['file.iss'],
\ 'ist': ['file.ist', 'file.mst'],
\ 'j': ['file.ijs'],
@@ -406,11 +408,13 @@ func s:GetFilenameChecks() abort
\ 'lean': ['file.lean'],
\ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'],
\ 'less': ['file.less'],
+ \ 'leo': ['file.leo'],
\ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],
\ 'lf': ['lfrc'],
\ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'],
\ 'lhaskell': ['file.lhs'],
\ 'libao': ['/etc/libao.conf', '/.libao', 'any/.libao', 'any/etc/libao.conf'],
+ \ 'lidris2': ['file.lidr'],
\ 'lifelines': ['file.ll'],
\ 'lilo': ['lilo.conf', 'lilo.conf-file'],
\ 'lilypond': ['file.ly', 'file.ily'],
@@ -726,6 +730,7 @@ func s:GetFilenameChecks() abort
\ 'svelte': ['file.svelte'],
\ 'svg': ['file.svg'],
\ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'],
+ \ 'sway': ['file.sw'],
\ 'swayconfig': ['/home/user/.sway/config', '/home/user/.config/sway/config', '/etc/sway/config', '/etc/xdg/sway/config'],
\ 'swift': ['file.swift', 'file.swiftinterface'],
\ 'swiftgyb': ['file.swift.gyb'],
diff --git a/test/old/testdir/test_findfile.vim b/test/old/testdir/test_findfile.vim
index d3fdcad045..539c7a661a 100644
--- a/test/old/testdir/test_findfile.vim
+++ b/test/old/testdir/test_findfile.vim
@@ -1,6 +1,7 @@
" Test findfile() and finddir()
source check.vim
+source vim9.vim
let s:files = [ 'Xfinddir1/foo',
\ 'Xfinddir1/bar',
@@ -288,223 +289,526 @@ func Test_find_non_existing_path()
let &path = save_path
endfunc
-" Test for 'findexpr'
-func Test_findexpr()
+" Test for 'findfunc'
+func Test_findfunc()
CheckUnix
- call assert_equal('', &findexpr)
- call writefile(['aFile'], 'Xfindexpr1.c', 'D')
- call writefile(['bFile'], 'Xfindexpr2.c', 'D')
- call writefile(['cFile'], 'Xfindexpr3.c', 'D')
+ call assert_equal('', &findfunc)
+ call writefile(['aFile'], 'Xfindfunc1.c', 'D')
+ call writefile(['bFile'], 'Xfindfunc2.c', 'D')
+ call writefile(['cFile'], 'Xfindfunc3.c', 'D')
" basic tests
- func FindExpr1()
- let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
- return fnames->copy()->filter('v:val =~? v:fname')
+ func FindFuncBasic(pat, cmdcomplete)
+ let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
+ return fnames->copy()->filter('v:val =~? a:pat')
endfunc
- set findexpr=FindExpr1()
- find Xfindexpr3
- call assert_match('Xfindexpr3.c', @%)
+ set findfunc=FindFuncBasic
+ find Xfindfunc3
+ call assert_match('Xfindfunc3.c', @%)
bw!
2find Xfind
- call assert_match('Xfindexpr2.c', @%)
+ call assert_match('Xfindfunc2.c', @%)
bw!
call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path')
call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path')
- sfind Xfindexpr2.c
- call assert_match('Xfindexpr2.c', @%)
+ sfind Xfindfunc2.c
+ call assert_match('Xfindfunc2.c', @%)
call assert_equal(2, winnr('$'))
%bw!
call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path')
- tabfind Xfindexpr3.c
- call assert_match('Xfindexpr3.c', @%)
+ tabfind Xfindfunc3.c
+ call assert_match('Xfindfunc3.c', @%)
call assert_equal(2, tabpagenr())
%bw!
call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path')
+ " Test garbage collection
+ call test_garbagecollect_now()
+ find Xfindfunc2
+ call assert_match('Xfindfunc2.c', @%)
+ bw!
+ delfunc FindFuncBasic
+ call test_garbagecollect_now()
+ call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic')
+
" Buffer-local option
- set findexpr=['abc']
+ func GlobalFindFunc(pat, cmdcomplete)
+ return ['global']
+ endfunc
+ func LocalFindFunc(pat, cmdcomplete)
+ return ['local']
+ endfunc
+ set findfunc=GlobalFindFunc
new
- setlocal findexpr=['def']
+ setlocal findfunc=LocalFindFunc
find xxxx
- call assert_equal('def', @%)
+ call assert_equal('local', @%)
wincmd w
find xxxx
- call assert_equal('abc', @%)
+ call assert_equal('global', @%)
aboveleft new
- call assert_equal("['abc']", &findexpr)
+ call assert_equal("GlobalFindFunc", &findfunc)
wincmd k
aboveleft new
- call assert_equal("['abc']", &findexpr)
+ call assert_equal("GlobalFindFunc", &findfunc)
%bw!
+ delfunc GlobalFindFunc
+ delfunc LocalFindFunc
- " Empty list
- set findexpr=[]
- call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path')
+ " Assign an expression
+ set findfunc=[]
+ call assert_fails('find xxxx', 'E117: Unknown function: []')
" Error cases
- " Syntax error in the expression
- set findexpr=FindExpr1{}
- call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression')
+ " Function that doesn't take any arguments
+ func FindFuncNoArg()
+ endfunc
+ set findfunc=FindFuncNoArg
+ call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg')
+ delfunc FindFuncNoArg
- " Find expression throws an error
- func FindExpr2()
+ " Syntax error in the function
+ func FindFuncSyntaxError(pat, cmdcomplete)
+ return l
+ endfunc
+ set findfunc=FindFuncSyntaxError
+ call assert_fails('find Xfindfunc1.c', 'E121: Undefined variable: l')
+ delfunc FindFuncSyntaxError
+
+ " Find function throws an error
+ func FindFuncWithThrow(pat, cmdcomplete)
throw 'find error'
endfunc
- set findexpr=FindExpr2()
- call assert_fails('find Xfindexpr1.c', 'find error')
+ set findfunc=FindFuncWithThrow
+ call assert_fails('find Xfindfunc1.c', 'find error')
+ delfunc FindFuncWithThrow
- " Try using a null List as the expression
- set findexpr=v:_null_list
- call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path')
+ " Try using a null function
+ "call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required')
- " Try to create a new window from the find expression
- func FindExpr3()
+ " Try to create a new window from the find function
+ func FindFuncNewWindow(pat, cmdexpand)
new
return ["foo"]
endfunc
- set findexpr=FindExpr3()
- call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
+ set findfunc=FindFuncNewWindow
+ call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
+ delfunc FindFuncNewWindow
- " Try to modify the current buffer from the find expression
- func FindExpr4()
+ " Try to modify the current buffer from the find function
+ func FindFuncModifyBuf(pat, cmdexpand)
call setline(1, ['abc'])
return ["foo"]
endfunc
- set findexpr=FindExpr4()
- call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
-
- " Expression returning a string
- set findexpr='abc'
- call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type")
-
- set findexpr&
- delfunc! FindExpr1
- delfunc! FindExpr2
- delfunc! FindExpr3
- delfunc! FindExpr4
+ set findfunc=FindFuncModifyBuf
+ call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
+ delfunc FindFuncModifyBuf
+
+ " Return the wrong type from the function
+ func FindFuncWrongRet(pat, cmdexpand)
+ return 'foo'
+ endfunc
+ set findfunc=FindFuncWrongRet
+ call assert_fails('find Xfindfunc1.c', "E1514: 'findfunc' did not return a List type")
+ delfunc FindFuncWrongRet
+
+ set findfunc&
endfunc
-" Test for using a script-local function for 'findexpr'
-func Test_findexpr_scriptlocal_func()
- func! s:FindExprScript()
- let g:FindExprArg = v:fname
+" Test for using a script-local function for 'findfunc'
+func Test_findfunc_scriptlocal_func()
+ func! s:FindFuncScript(pat, cmdexpand)
+ let g:FindFuncArg = a:pat
return ['xxx']
endfunc
- set findexpr=s:FindExprScript()
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ set findfunc=s:FindFuncScript
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ new | only
+ let g:FindFuncArg = ''
+ find abc
+ call assert_equal('abc', g:FindFuncArg)
+ bw!
+
+ set findfunc=<SID>FindFuncScript
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only
- let g:FindExprArg = ''
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
- set findexpr=<SID>FindExprScript()
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ let &findfunc = 's:FindFuncScript'
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only
- let g:FindExprArg = ''
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
- let &findexpr = 's:FindExprScript()'
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ let &findfunc = '<SID>FindFuncScript'
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only
- let g:FindExprArg = ''
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
- let &findexpr = '<SID>FindExprScript()'
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ set findfunc=
+ setglobal findfunc=s:FindFuncScript
+ setlocal findfunc=
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ call assert_equal('', &l:findfunc)
new | only
- let g:FindExprArg = ''
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
- set findexpr=
- setglobal findexpr=s:FindExprScript()
- setlocal findexpr=
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
- call assert_equal('', &l:findexpr)
new | only
- let g:FindExprArg = ''
+ set findfunc=
+ setglobal findfunc=
+ setlocal findfunc=s:FindFuncScript
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &l:findfunc)
+ call assert_equal('', &g:findfunc)
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
new | only
- set findexpr=
- setglobal findexpr=
- setlocal findexpr=s:FindExprScript()
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
- call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr)
- call assert_equal('', &g:findexpr)
- let g:FindExprArg = ''
+ set findfunc=
+ setlocal findfunc=NoSuchFunc
+ setglobal findfunc=s:FindFuncScript
+ call assert_equal('NoSuchFunc', &findfunc)
+ call assert_equal('NoSuchFunc', &l:findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ new | only
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ call assert_equal('', &l:findfunc)
+ let g:FindFuncArg = ''
find abc
- call assert_equal('abc', g:FindExprArg)
+ call assert_equal('abc', g:FindFuncArg)
bw!
- set findexpr=
- delfunc s:FindExprScript
+ new | only
+ set findfunc=
+ setlocal findfunc=NoSuchFunc
+ set findfunc=s:FindFuncScript
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ call assert_equal('', &l:findfunc)
+ let g:FindFuncArg = ''
+ find abc
+ call assert_equal('abc', g:FindFuncArg)
+ new | only
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+ call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+ call assert_equal('', &l:findfunc)
+ let g:FindFuncArg = ''
+ find abc
+ call assert_equal('abc', g:FindFuncArg)
+ bw!
+
+ set findfunc=
+ delfunc s:FindFuncScript
endfunc
-" Test for expanding the argument to the :find command using 'findexpr'
-func Test_findexpr_expand_arg()
- let s:fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
+" Test for expanding the argument to the :find command using 'findfunc'
+func Test_findfunc_expand_arg()
+ let s:fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
- " 'findexpr' that accepts a regular expression
- func FindExprRegexp()
- return s:fnames->copy()->filter('v:val =~? v:fname')
+ " 'findfunc' that accepts a regular expression
+ func FindFuncRegexp(pat, cmdcomplete)
+ return s:fnames->copy()->filter('v:val =~? a:pat')
endfunc
- " 'findexpr' that accepts a glob
- func FindExprGlob()
- let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname)
+ " 'findfunc' that accepts a glob
+ func FindFuncGlob(pat_arg, cmdcomplete)
+ let pat = glob2regpat(a:cmdcomplete ? $'*{a:pat_arg}*' : a:pat_arg)
return s:fnames->copy()->filter('v:val =~? pat')
endfunc
for regexp in [v:true, v:false]
- let &findexpr = regexp ? 'FindExprRegexp()' : 'FindExprGlob()'
+ let &findfunc = regexp ? 'FindFuncRegexp' : 'FindFuncGlob'
call feedkeys(":find \<Tab>\<C-B>\"\<CR>", "xt")
- call assert_equal('"find Xfindexpr1.c', @:)
+ call assert_equal('"find Xfindfunc1.c', @:)
call feedkeys(":find Xfind\<Tab>\<Tab>\<C-B>\"\<CR>", "xt")
- call assert_equal('"find Xfindexpr2.c', @:)
+ call assert_equal('"find Xfindfunc2.c', @:)
call assert_equal(s:fnames, getcompletion('find ', 'cmdline'))
call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline'))
let pat = regexp ? 'X.*1\.c' : 'X*1.c'
call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt")
- call assert_equal('"find Xfindexpr1.c', @:)
- call assert_equal(['Xfindexpr1.c'], getcompletion($'find {pat}', 'cmdline'))
+ call assert_equal('"find Xfindfunc1.c', @:)
+ call assert_equal(['Xfindfunc1.c'], getcompletion($'find {pat}', 'cmdline'))
call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt")
- call assert_equal('"find Xfindexpr3.c', @:)
- call assert_equal(['Xfindexpr3.c'], getcompletion($'find 3', 'cmdline'))
+ call assert_equal('"find Xfindfunc3.c', @:)
+ call assert_equal(['Xfindfunc3.c'], getcompletion($'find 3', 'cmdline'))
call feedkeys(":find Xfind\<C-A>\<C-B>\"\<CR>", "xt")
- call assert_equal('"find Xfindexpr1.c Xfindexpr2.c Xfindexpr3.c', @:)
+ call assert_equal('"find Xfindfunc1.c Xfindfunc2.c Xfindfunc3.c', @:)
call feedkeys(":find abc\<Tab>\<C-B>\"\<CR>", "xt")
call assert_equal('"find abc', @:)
call assert_equal([], getcompletion('find abc', 'cmdline'))
endfor
- set findexpr&
- delfunc! FindExprRegexp
- delfunc! FindExprGlob
+ set findfunc&
+ delfunc! FindFuncRegexp
+ delfunc! FindFuncGlob
unlet s:fnames
endfunc
+" Test for different ways of setting the 'findfunc' option
+func Test_findfunc_callback()
+ new
+ func FindFunc1(pat, cmdexpand)
+ let g:FindFunc1Args = [a:pat, a:cmdexpand]
+ return ['findfunc1']
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a function name
+ LET &findfunc = 'g:FindFunc1'
+ LET g:FindFunc1Args = []
+ find abc1
+ call assert_equal(['abc1', v:false], g:FindFunc1Args)
+
+ #" Test for using a function()
+ set findfunc=function('g:FindFunc1')
+ LET g:FindFunc1Args = []
+ find abc2
+ call assert_equal(['abc2', v:false], g:FindFunc1Args)
+
+ #" Using a funcref variable to set 'findfunc'
+ VAR Fn = function('g:FindFunc1')
+ LET &findfunc = Fn
+ LET g:FindFunc1Args = []
+ find abc3
+ call assert_equal(['abc3', v:false], g:FindFunc1Args)
+
+ #" Using a string(funcref_variable) to set 'findfunc'
+ LET Fn = function('g:FindFunc1')
+ LET &findfunc = string(Fn)
+ LET g:FindFunc1Args = []
+ find abc4
+ call assert_equal(['abc4', v:false], g:FindFunc1Args)
+
+ #" Test for using a funcref()
+ set findfunc=funcref('g:FindFunc1')
+ LET g:FindFunc1Args = []
+ find abc5
+ call assert_equal(['abc5', v:false], g:FindFunc1Args)
+
+ #" Using a funcref variable to set 'findfunc'
+ LET Fn = funcref('g:FindFunc1')
+ LET &findfunc = Fn
+ LET g:FindFunc1Args = []
+ find abc6
+ call assert_equal(['abc6', v:false], g:FindFunc1Args)
+
+ #" Using a string(funcref_variable) to set 'findfunc'
+ LET Fn = funcref('g:FindFunc1')
+ LET &findfunc = string(Fn)
+ LET g:FindFunc1Args = []
+ find abc7
+ call assert_equal(['abc7', v:false], g:FindFunc1Args)
+
+ #" Test for using a lambda function using set
+ VAR optval = "LSTART pat, cmdexpand LMIDDLE FindFunc1(pat, cmdexpand) LEND"
+ LET optval = substitute(optval, ' ', '\\ ', 'g')
+ exe "set findfunc=" .. optval
+ LET g:FindFunc1Args = []
+ find abc8
+ call assert_equal(['abc8', v:false], g:FindFunc1Args)
+
+ #" Test for using a lambda function using LET
+ LET &findfunc = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+ LET g:FindFunc1Args = []
+ find abc9
+ call assert_equal(['abc9', v:false], g:FindFunc1Args)
+
+ #" Set 'findfunc' to a string(lambda expression)
+ LET &findfunc = 'LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND'
+ LET g:FindFunc1Args = []
+ find abc10
+ call assert_equal(['abc10', v:false], g:FindFunc1Args)
+
+ #" Set 'findfunc' to a variable with a lambda expression
+ VAR Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+ LET &findfunc = Lambda
+ LET g:FindFunc1Args = []
+ find abc11
+ call assert_equal(['abc11', v:false], g:FindFunc1Args)
+
+ #" Set 'findfunc' to a string(variable with a lambda expression)
+ LET Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+ LET &findfunc = string(Lambda)
+ LET g:FindFunc1Args = []
+ find abc12
+ call assert_equal(['abc12', v:false], g:FindFunc1Args)
+
+ #" Try to use 'findfunc' after the function is deleted
+ func g:TmpFindFunc(pat, cmdexpand)
+ let g:TmpFindFunc1Args = [a:pat, a:cmdexpand]
+ endfunc
+ LET &findfunc = function('g:TmpFindFunc')
+ delfunc g:TmpFindFunc
+ call test_garbagecollect_now()
+ LET g:TmpFindFunc1Args = []
+ call assert_fails('find abc13', 'E117:')
+ call assert_equal([], g:TmpFindFunc1Args)
+
+ #" Try to use a function with three arguments for 'findfunc'
+ func g:TmpFindFunc2(x, y, z)
+ let g:TmpFindFunc2Args = [a:x, a:y, a:z]
+ endfunc
+ set findfunc=TmpFindFunc2
+ LET g:TmpFindFunc2Args = []
+ call assert_fails('find abc14', 'E119:')
+ call assert_equal([], g:TmpFindFunc2Args)
+ delfunc TmpFindFunc2
+
+ #" Try to use a function with zero arguments for 'findfunc'
+ func g:TmpFindFunc3()
+ let g:TmpFindFunc3Called = v:true
+ endfunc
+ set findfunc=TmpFindFunc3
+ LET g:TmpFindFunc3Called = v:false
+ call assert_fails('find abc15', 'E118:')
+ call assert_equal(v:false, g:TmpFindFunc3Called)
+ delfunc TmpFindFunc3
+
+ #" Try to use a lambda function with three arguments for 'findfunc'
+ LET &findfunc = LSTART a, b, c LMIDDLE FindFunc1(a, v:false) LEND
+ LET g:FindFunc1Args = []
+ call assert_fails('find abc16', 'E119:')
+ call assert_equal([], g:FindFunc1Args)
+
+ #" Test for clearing the 'findfunc' option
+ set findfunc=''
+ set findfunc&
+ call assert_fails("set findfunc=function('abc')", "E700:")
+ call assert_fails("set findfunc=funcref('abc')", "E700:")
+
+ #" set 'findfunc' to a non-existing function
+ LET &findfunc = function('g:FindFunc1')
+ call assert_fails("set findfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("LET &findfunc = function('NonExistingFunc')", 'E700:')
+ LET g:FindFunc1Args = []
+ find abc17
+ call assert_equal(['abc17', v:false], g:FindFunc1Args)
+ END
+ call CheckTransLegacySuccess(lines)
+
+ " Test for using a script-local function name
+ func s:FindFunc2(pat, cmdexpand)
+ let g:FindFunc2Args = [a:pat, a:cmdexpand]
+ return ['findfunc2']
+ endfunc
+ set findfunc=s:FindFunc2
+ let g:FindFunc2Args = []
+ find abc18
+ call assert_equal(['abc18', v:false], g:FindFunc2Args)
+
+ let &findfunc = 's:FindFunc2'
+ let g:FindFunc2Args = []
+ find abc19
+ call assert_equal(['abc19', v:false], g:FindFunc2Args)
+ delfunc s:FindFunc2
+
+ " Using Vim9 lambda expression in legacy context should fail
+ set findfunc=(pat,\ cmdexpand)\ =>\ FindFunc1(pat,\ v:false)
+ let g:FindFunc1Args = []
+ call assert_fails('find abc20', 'E117:')
+ call assert_equal([], g:FindFunc1Args)
+
+ " set 'findfunc' to a partial with dict.
+ func SetFindFunc()
+ let operator = {'execute': function('FindFuncExecute')}
+ let &findfunc = operator.execute
+ endfunc
+ func FindFuncExecute(pat, cmdexpand) dict
+ return ['findfuncexecute']
+ endfunc
+ call SetFindFunc()
+ call test_garbagecollect_now()
+ set findfunc=
+ delfunc SetFindFunc
+ delfunc FindFuncExecute
+
+ func FindFunc2(pat, cmdexpand)
+ let g:FindFunc2Args = [a:pat, a:cmdexpand]
+ return ['findfunc2']
+ endfunc
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def g:Vim9findFunc(pat: string, cmdexpand: bool): list<string>
+ g:FindFunc1Args = [pat, cmdexpand]
+ return ['vim9findfunc']
+ enddef
+
+ # Test for using a def function with findfunc
+ set findfunc=function('g:Vim9findFunc')
+ g:FindFunc1Args = []
+ find abc21
+ assert_equal(['abc21', false], g:FindFunc1Args)
+
+ # Test for using a global function name
+ &findfunc = g:FindFunc2
+ g:FindFunc2Args = []
+ find abc22
+ assert_equal(['abc22', false], g:FindFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def LocalFindFunc(pat: string, cmdexpand: bool): list<string>
+ g:LocalFindFuncArgs = [pat, cmdexpand]
+ return ['localfindfunc']
+ enddef
+ &findfunc = LocalFindFunc
+ g:LocalFindFuncArgs = []
+ find abc23
+ assert_equal(['abc23', false], g:LocalFindFuncArgs)
+ bw!
+ END
+ call CheckScriptSuccess(lines)
+
+ " setting 'findfunc' to a script local function outside of a script context
+ " should fail
+ let cleanup =<< trim END
+ call writefile([execute('messages')], 'Xtest.out')
+ qall
+ END
+ call writefile(cleanup, 'Xverify.vim', 'D')
+ call RunVim([], [], "-c \"set findfunc=s:abc\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call delete('Xtest.out')
+
+ " cleanup
+ set findfunc&
+ delfunc FindFunc1
+ delfunc FindFunc2
+ unlet g:FindFunc1Args g:FindFunc2Args
+ %bw!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
index 48319f5017..c02aa1db62 100644
--- a/test/old/testdir/test_ins_complete.vim
+++ b/test/old/testdir/test_ins_complete.vim
@@ -950,6 +950,46 @@ func Test_completeopt_buffer_local()
call assert_equal('menu', &completeopt)
call assert_equal('menu', &g:completeopt)
+ new | only
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ set completeopt&
+ setlocal completeopt=menu,fuzzy,noinsert
+ setglobal completeopt=menu,longest
+ call assert_equal('menu,fuzzy,noinsert', &completeopt)
+ call assert_equal('menu,fuzzy,noinsert', &l:completeopt)
+ call assert_equal('menu,longest', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>bz\<C-Y>", 'tnix')
+ call assert_equal('foobaz', getline('.'))
+ setlocal bufhidden=wipe
+ new | only!
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ call assert_equal('menu,longest', &completeopt)
+ call assert_equal('menu,longest', &g:completeopt)
+ call assert_equal('', &l:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-X>\<C-Z>", 'tnix')
+ call assert_equal('foo', getline('.'))
+ bwipe!
+
+ new | only
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ set completeopt&
+ setlocal completeopt=menu,fuzzy,noinsert
+ set completeopt=menu,longest
+ call assert_equal('menu,longest', &completeopt)
+ call assert_equal('menu,longest', &g:completeopt)
+ call assert_equal('', &l:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-X>\<C-Z>", 'tnix')
+ call assert_equal('foo', getline('.'))
+ setlocal bufhidden=wipe
+ new | only!
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ call assert_equal('menu,longest', &completeopt)
+ call assert_equal('menu,longest', &g:completeopt)
+ call assert_equal('', &l:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-X>\<C-Z>", 'tnix')
+ call assert_equal('foo', getline('.'))
+ bwipe!
+
set completeopt&
endfunc
@@ -1713,10 +1753,10 @@ func Test_completefunc_callback()
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
- " set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
+ set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
new | only
let g:CompleteFunc1Args = []
- " call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
+ call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
call assert_equal([], g:CompleteFunc1Args)
" set 'completefunc' to a partial with dict. This used to cause a crash.
@@ -1970,10 +2010,10 @@ func Test_omnifunc_callback()
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
- " set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
+ set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
new | only
let g:OmniFunc1Args = []
- " call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
+ call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
call assert_equal([], g:OmniFunc1Args)
" set 'omnifunc' to a partial with dict. This used to cause a crash.
@@ -2228,6 +2268,7 @@ func Test_thesaurusfunc_callback()
call add(g:TsrFunc3Args, [a:findstart, a:base])
return a:findstart ? 0 : []
endfunc
+
set tsrfu=s:TsrFunc3
new
call setline(1, 'script1')
@@ -2243,6 +2284,46 @@ func Test_thesaurusfunc_callback()
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args)
bw!
+
+ new | only
+ set thesaurusfunc=
+ setlocal thesaurusfunc=NoSuchFunc
+ setglobal thesaurusfunc=s:TsrFunc3
+ call assert_equal('NoSuchFunc', &thesaurusfunc)
+ call assert_equal('NoSuchFunc', &l:thesaurusfunc)
+ call assert_equal('s:TsrFunc3', &g:thesaurusfunc)
+ new | only
+ call assert_equal('s:TsrFunc3', &thesaurusfunc)
+ call assert_equal('s:TsrFunc3', &g:thesaurusfunc)
+ call assert_equal('', &l:thesaurusfunc)
+ call setline(1, 'script1')
+ let g:TsrFunc3Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args)
+ bw!
+
+ new | only
+ set thesaurusfunc=
+ setlocal thesaurusfunc=NoSuchFunc
+ set thesaurusfunc=s:TsrFunc3
+ call assert_equal('s:TsrFunc3', &thesaurusfunc)
+ call assert_equal('s:TsrFunc3', &g:thesaurusfunc)
+ call assert_equal('', &l:thesaurusfunc)
+ call setline(1, 'script1')
+ let g:TsrFunc3Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args)
+ setlocal bufhidden=wipe
+ new | only!
+ call assert_equal('s:TsrFunc3', &thesaurusfunc)
+ call assert_equal('s:TsrFunc3', &g:thesaurusfunc)
+ call assert_equal('', &l:thesaurusfunc)
+ call setline(1, 'script1')
+ let g:TsrFunc3Args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args)
+ bw!
+
delfunc s:TsrFunc3
" invalid return value
@@ -2250,10 +2331,10 @@ func Test_thesaurusfunc_callback()
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
- " set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
+ set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
new | only
let g:TsrFunc1Args = []
- " call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:')
+ call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:')
call assert_equal([], g:TsrFunc1Args)
bw!
diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim
index 7b7163d372..2cd9e49a12 100644
--- a/test/old/testdir/test_modeline.vim
+++ b/test/old/testdir/test_modeline.vim
@@ -217,7 +217,7 @@ func Test_modeline_fails_always()
call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
- call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:')
+ call s:modeline_fails('findfunc', 'findfunc=Something', 'E520:')
call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim
index 46fddd6c1a..faa93c4b61 100644
--- a/test/old/testdir/test_normal.vim
+++ b/test/old/testdir/test_normal.vim
@@ -692,9 +692,9 @@ func Test_opfunc_callback()
delfunc s:OpFunc3
" Using Vim9 lambda expression in legacy context should fail
- " set opfunc=(a)\ =>\ OpFunc1(24,\ a)
+ set opfunc=(a)\ =>\ OpFunc1(24,\ a)
let g:OpFunc1Args = []
- " call assert_fails('normal! g@l', 'E117:')
+ call assert_fails('normal! g@l', 'E117:')
call assert_equal([], g:OpFunc1Args)
" set 'operatorfunc' to a partial with dict. This used to cause a crash.
diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim
index 6902560518..540936e7ec 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -1559,7 +1559,7 @@ endfunc
" Test for changing options in a sandbox
func Test_opt_sandbox()
- for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr']
+ for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc']
call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
endfor
diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim
index d35d32b13d..601ba6c688 100644
--- a/test/old/testdir/test_popup.vim
+++ b/test/old/testdir/test_popup.vim
@@ -1509,10 +1509,18 @@ endfunc
func Test_pum_user_abbr_hlgroup()
CheckScreendump
let lines =<< trim END
- func CompleteFunc( findstart, base )
+ let s:var = 0
+ func CompleteFunc(findstart, base)
if a:findstart
return 0
endif
+ if s:var == 1
+ return {
+ \ 'words': [
+ \ { 'word': 'aword1', 'abbr_hlgroup': 'StrikeFake' },
+ \ { 'word': '你好', 'abbr_hlgroup': 'StrikeFake' },
+ \]}
+ endif
return {
\ 'words': [
\ { 'word': 'aword1', 'menu': 'extra text 1', 'kind': 'W', 'abbr_hlgroup': 'StrikeFake' },
@@ -1520,6 +1528,9 @@ func Test_pum_user_abbr_hlgroup()
\ { 'word': '你好', 'menu': 'extra text 3', 'kind': 'W', 'abbr_hlgroup': 'StrikeFake' },
\]}
endfunc
+ func ChangeVar()
+ let s:var = 1
+ endfunc
set completeopt=menu
set completefunc=CompleteFunc
@@ -1547,13 +1558,20 @@ func Test_pum_user_abbr_hlgroup()
call VerifyScreenDump(buf, 'Test_pum_highlights_14', {})
call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":call ChangeVar()\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "S\<C-X>\<C-U>")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_17', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
call StopVimInTerminal(buf)
endfunc
func Test_pum_user_kind_hlgroup()
CheckScreendump
let lines =<< trim END
- func CompleteFunc( findstart, base )
+ func CompleteFunc(findstart, base)
if a:findstart
return 0
endif
diff --git a/test/old/testdir/test_tagfunc.vim b/test/old/testdir/test_tagfunc.vim
index 812603a430..ec1f93e9be 100644
--- a/test/old/testdir/test_tagfunc.vim
+++ b/test/old/testdir/test_tagfunc.vim
@@ -291,10 +291,10 @@ func Test_tagfunc_callback()
call assert_fails("echo taglist('a')", "E987:")
" Using Vim9 lambda expression in legacy context should fail
- " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
+ set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
new
let g:TagFunc1Args = []
- " call assert_fails("tag a17", "E117:")
+ call assert_fails("tag a17", "E117:")
call assert_equal([], g:TagFunc1Args)
bw!
diff --git a/test/testutil.lua b/test/testutil.lua
index a920f658a1..00b30d74d5 100644
--- a/test/testutil.lua
+++ b/test/testutil.lua
@@ -392,9 +392,7 @@ function M.check_logs()
)
end
-local function sysname()
- return uv.os_uname().sysname:lower()
-end
+local sysname = uv.os_uname().sysname:lower()
--- @param s 'win'|'mac'|'freebsd'|'openbsd'|'bsd'
--- @return boolean
@@ -403,48 +401,27 @@ function M.is_os(s)
error('unknown platform: ' .. tostring(s))
end
return not not (
- (s == 'win' and (sysname():find('windows') or sysname():find('mingw')))
- or (s == 'mac' and sysname() == 'darwin')
- or (s == 'freebsd' and sysname() == 'freebsd')
- or (s == 'openbsd' and sysname() == 'openbsd')
- or (s == 'bsd' and sysname():find('bsd'))
+ (s == 'win' and (sysname:find('windows') or sysname:find('mingw')))
+ or (s == 'mac' and sysname == 'darwin')
+ or (s == 'freebsd' and sysname == 'freebsd')
+ or (s == 'openbsd' and sysname == 'openbsd')
+ or (s == 'bsd' and sysname:find('bsd'))
)
end
-local function tmpdir_get()
- return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP')
-end
-
---- Is temp directory `dir` defined local to the project workspace?
---- @param dir string?
---- @return boolean
-local function tmpdir_is_local(dir)
- return not not (dir and dir:find('Xtest'))
-end
-
local tmpname_id = 0
-local tmpdir = tmpdir_get()
+local tmpdir = os.getenv('TMPDIR') or os.getenv('TEMP')
+local tmpdir_is_local = not not (tmpdir and tmpdir:find('Xtest'))
---- Generates a unique filepath for use by tests, in a test-specific "…/Xtest_tmpdir/T42.7"
---- directory (which is cleaned up by the test runner), and writes the file unless `create=false`.
----
----@param create? boolean (default true) Write the file.
-function M.tmpname(create)
- if tmpdir_is_local(tmpdir) then
+local function get_tmpname()
+ if tmpdir_is_local then
-- Cannot control os.tmpname() dir, so hack our own tmpname() impl.
tmpname_id = tmpname_id + 1
-- "…/Xtest_tmpdir/T42.7"
- local fname = ('%s/%s.%d'):format(tmpdir, (_G._nvim_test_id or 'nvim-test'), tmpname_id)
- if create ~= false then
- io.open(fname, 'w'):close()
- end
- return fname
+ return ('%s/%s.%d'):format(tmpdir, (_G._nvim_test_id or 'nvim-test'), tmpname_id)
end
local fname = os.tmpname()
- if create == false then
- os.remove(fname)
- end
if M.is_os('win') and fname:sub(1, 2) == '\\s' then
-- In Windows tmpname() returns a filename starting with
@@ -454,7 +431,20 @@ function M.tmpname(create)
-- In OS X /tmp links to /private/tmp
return '/private' .. fname
end
+ return fname
+end
+--- Generates a unique filepath for use by tests, in a test-specific "…/Xtest_tmpdir/T42.7"
+--- directory (which is cleaned up by the test runner).
+---
+--- @param create? boolean (default true) Create the file.
+--- @return string
+function M.tmpname(create)
+ local fname = get_tmpname()
+ os.remove(fname)
+ if create ~= false then
+ assert(io.open(fname, 'w')):close()
+ end
return fname
end
@@ -479,11 +469,11 @@ function M.check_cores(app, force) -- luacheck: ignore
local random_skip = false
-- Workspace-local $TMPDIR, scrubbed and pattern-escaped.
-- "./Xtest-tmpdir/" => "Xtest%-tmpdir"
- local local_tmpdir = (
- tmpdir_is_local(tmpdir_get())
- and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
- or nil
- )
+ local local_tmpdir = nil
+ if tmpdir_is_local and tmpdir then
+ local_tmpdir = vim.pesc(relpath(tmpdir):gsub('^[ ./]+', ''):gsub('%/+$', ''))
+ end
+
local db_cmd --- @type string
local test_glob_dir = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
if test_glob_dir and test_glob_dir ~= '' then