aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/deprecated.txt3
-rw-r--r--runtime/doc/diagnostic.txt8
-rw-r--r--runtime/doc/lsp.txt1
-rw-r--r--runtime/doc/news.txt9
-rw-r--r--runtime/doc/options.txt61
-rw-r--r--runtime/doc/quickref.txt1
-rw-r--r--runtime/doc/treesitter.txt2
-rw-r--r--runtime/doc/vim_diff.txt7
-rw-r--r--runtime/doc/vvars.txt8
-rw-r--r--runtime/lua/man.lua2
-rw-r--r--runtime/lua/vim/_defaults.lua12
-rw-r--r--runtime/lua/vim/_editor.lua28
-rw-r--r--runtime/lua/vim/_meta/options.lua56
-rw-r--r--runtime/lua/vim/_meta/vvars.lua8
-rw-r--r--runtime/lua/vim/diagnostic.lua49
-rw-r--r--runtime/lua/vim/lsp.lua5
-rw-r--r--runtime/lua/vim/lsp/completion.lua2
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua24
-rw-r--r--runtime/lua/vim/lsp/health.lua23
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua14
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua14
-rw-r--r--runtime/lua/vim/lsp/sync.lua59
-rw-r--r--runtime/lua/vim/lsp/util.lua77
-rw-r--r--runtime/lua/vim/treesitter/query.lua2
-rw-r--r--runtime/syntax/awk.vim4
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor8
-rw-r--r--src/nvim/auevents.lua6
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/buffer_defs.h3
-rw-r--r--src/nvim/cmdexpand.c39
-rw-r--r--src/nvim/cmdexpand_defs.h1
-rw-r--r--src/nvim/errors.h6
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua76
-rw-r--r--src/nvim/eval/vars.c2
-rw-r--r--src/nvim/ex_docmd.c162
-rw-r--r--src/nvim/file_search.c8
-rw-r--r--src/nvim/generators/gen_options.lua124
-rw-r--r--src/nvim/globals.h6
-rw-r--r--src/nvim/lua/stdlib.c4
-rw-r--r--src/nvim/lua/treesitter.c2
-rw-r--r--src/nvim/mapping.c20
-rw-r--r--src/nvim/mapping.h7
-rw-r--r--src/nvim/option.c631
-rw-r--r--src/nvim/option.h8
-rw-r--r--src/nvim/option_defs.h36
-rw-r--r--src/nvim/option_vars.h54
-rw-r--r--src/nvim/options.lua168
-rw-r--r--src/nvim/optionstr.c15
-rw-r--r--src/nvim/popupmenu.c7
-rw-r--r--src/nvim/quickfix.c5
-rw-r--r--src/nvim/vvars.lua268
-rw-r--r--test/functional/lua/diagnostic_spec.lua34
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua4
-rw-r--r--test/functional/ui/popupmenu_spec.lua179
-rw-r--r--test/old/testdir/runtest.vim26
-rw-r--r--test/old/testdir/test_findfile.vim221
-rw-r--r--test/old/testdir/test_map_functions.vim19
-rw-r--r--test/old/testdir/test_modeline.vim1
-rw-r--r--test/old/testdir/test_options.vim36
-rw-r--r--test/old/testdir/test_undo.vim3
62 files changed, 1649 insertions, 1028 deletions
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index c768f30da2..9c973b20bd 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -44,7 +44,8 @@ TREESITTER
return the descendant itself.
LSP
-• *vim.lsp.util.jump_to_location*
+• *vim.lsp.util.jump_to_location* Use |vim.lsp.util.show_document()| with
+ `{focus=true}` instead.
• *vim.lsp.buf.execute_command* Use |Client:exec_cmd()| instead.
• *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead.
• vim.lsp.buf_request_all The `error` key has been renamed to `err` inside
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index 342947595e..9ccc3102b6 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -445,10 +445,10 @@ Lua module: vim.diagnostic *diagnostic-api*
updated on |InsertLeave|)
• {severity_sort}? (`boolean|{reverse?:boolean}`, default: `false`)
Sort diagnostics by severity. This affects the
- order in which signs and virtual text are
- displayed. When true, higher severities are
- displayed before lower severities (e.g. ERROR is
- displayed before WARN). Options:
+ order in which signs, virtual text, and
+ highlights are displayed. When true, higher
+ severities are displayed before lower severities
+ (e.g. ERROR is displayed before WARN). Options:
• {reverse}? (boolean) Reverse sort order
• {jump}? (`vim.diagnostic.Opts.Jump`) Default values for
|vim.diagnostic.jump()|. See
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 300a58b681..751a66771c 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -77,6 +77,7 @@ Some keymaps are created unconditionally when Nvim starts:
- "gra" is mapped in Normal and Visual mode to |vim.lsp.buf.code_action()|
- "grr" is mapped in Normal mode to |vim.lsp.buf.references()|
- "gri" is mapped in Normal mode to |vim.lsp.buf.implementation()|
+- "gO" is mapped in Normal mode to |vim.lsp.buf.document_symbol()|
- CTRL-S is mapped in Insert mode to |vim.lsp.buf.signature_help()|
If not wanted, these keymaps can be removed at any time using
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 508d47e59f..91d36accc7 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -55,6 +55,8 @@ DIAGNOSTICS
• |vim.diagnostic.config()| accepts a "jump" table to specify defaults for
|vim.diagnostic.jump()|.
+• The "underline" diagnostics handler sorts diagnostics by severity when using
+ the "severity_sort" option.
EDITOR
@@ -101,6 +103,10 @@ OPTIONS
changes according to related options. It takes care of alignment, 'number',
'relativenumber' and 'signcolumn' set to "number". The now redundant `%r` item
is no longer treated specially for 'statuscolumn'.
+• `:set {option}<` removes the local value for all |global-local| options instead
+ 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.
PLUGINS
@@ -151,6 +157,7 @@ DEFAULTS
• |grn| in Normal mode maps to |vim.lsp.buf.rename()|
• |grr| in Normal mode maps to |vim.lsp.buf.references()|
• |gri| in Normal mode maps to |vim.lsp.buf.implementation()|
+ • |gO| in Normal mode maps to |vim.lsp.buf.document_symbol()|
• |gra| in Normal and Visual mode maps to |vim.lsp.buf.code_action()|
• CTRL-S in Insert mode maps to |vim.lsp.buf.signature_help()|
• Mouse |popup-menu| includes an "Open in web browser" item when you right-click
@@ -201,6 +208,8 @@ LUA
• |vim.fs.rm()| can delete files and directories.
• |vim.validate()| now has a new signature which uses less tables,
is more peformant and easier to read.
+• |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures
+ supporting two new parameters, `encoding` and `strict_indexing`.
OPTIONS
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 2b81c408ed..7f95a19918 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -306,19 +306,13 @@ created, thus they behave slightly differently:
:se[t] {option}< Set the effective value of {option} to its global
value.
- For string |global-local| options, the local value is
- removed, so that the global value will be used.
+ For |global-local| options, the local value is removed,
+ so that the global value will be used.
For all other options, the global value is copied to
the local value.
:setl[ocal] {option}< Set the effective value of {option} to its global
- value.
- For number and boolean |global-local| options, the
- local value is removed, so that the global value will
- be used.
- For all other options, including string |global-local|
- options, the global value is copied to the local
- value.
+ value by copying the global value to the local value.
Note that the behaviour for |global-local| options is slightly different
between string and number-based options.
@@ -2604,6 +2598,55 @@ 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 "")
+ global or local to buffer |global-local|
+ Expression that is evaluated 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 expression is evaluated only once per |:find| command invocation.
+ The expression can process all the directories specified in 'path'.
+
+ 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|.
+
+ 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
+ should return an empty List.
+
+ If any errors are encountered during the expression evaluation, 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|.
+
+ This option cannot be set from a |modeline| or in the |sandbox|, for
+ security reasons.
+
+ Examples:
+ >vim
+ " Use glob()
+ func FindExprGlob()
+ let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+ return glob(pat, v:false, v:true)
+ endfunc
+ set findexpr=FindExprGlob()
+
+ " Use the 'git ls-files' output
+ func FindGitFiles()
+ let fnames = systemlist('git ls-files')
+ return fnames->filter('v:val =~? v:fname')
+ endfunc
+ set findexpr=FindGitFiles()
+<
+
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
'fixendofline' 'fixeol' boolean (default on)
local to buffer
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index d77750b485..f64865a031 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -705,6 +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|
'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/treesitter.txt b/runtime/doc/treesitter.txt
index f9a98250ea..e439e8fccb 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -1211,7 +1211,7 @@ add_predicate({name}, {handler}, {opts})
Parameters: ~
• {name} (`string`) Name of the predicate, without leading #
- • {handler} (`fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata)`)
+ • {handler} (`fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata): boolean?`)
• see |vim.treesitter.query.add_directive()| for argument
meanings
• {opts} (`table?`) A table with the following fields:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 94c0578872..10816ec358 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -151,6 +151,7 @@ of these in your config by simply removing the mapping, e.g. ":unmap Y".
- |grr|
- |gra|
- |gri|
+ - |gO|
- <C-S> |i_CTRL-S|
- ]d |]d-default|
- [d |[d-default|
@@ -339,10 +340,8 @@ Normal commands:
Options:
-Local values for global-local number/boolean options are unset when the option
-is set without a scope (e.g. by using |:set|), similarly to how global-local
-string options work.
-
+- `:set {option}<` removes local value for all |global-local| options.
+- `:setlocal {option}<` copies global value to local value for all options.
- 'autoread' works in the terminal (if it supports "focus" events)
- 'cpoptions' flags: |cpo-_|
- 'diffopt' "linematch" feature
diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt
index 15d836a83d..1c1d88c29c 100644
--- a/runtime/doc/vvars.txt
+++ b/runtime/doc/vvars.txt
@@ -48,6 +48,11 @@ 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
@@ -254,7 +259,8 @@ v:fcs_reason
*v:fname* *fname-variable*
v:fname
When evaluating 'includeexpr': the file name that was
- detected. Empty otherwise.
+ detected. When evaluating 'findexpr': the argument passed to
+ the |:find| command. Empty otherwise.
*v:fname_diff* *fname_diff-variable*
v:fname_diff
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index 6f60bf1cef..114c94f9e5 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -305,7 +305,7 @@ local function matchstr(text, pat_or_re)
return
end
- return text:sub(vim.str_utfindex(text, s) + 1, vim.str_utfindex(text, e))
+ return text:sub(vim.str_utfindex(text, 'utf-32', s) + 1, vim.str_utfindex(text, 'utf-32', e))
end
-- attempt to extract the name and sect out of 'name(sect)'
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 0f4cd08da0..03b2803f3e 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -157,7 +157,7 @@ do
--- client is attached. If no client is attached, or if a server does not support a capability, an
--- error message is displayed rather than exhibiting different behavior.
---
- --- See |grr|, |grn|, |gra|, |gri|, |i_CTRL-S|.
+ --- See |grr|, |grn|, |gra|, |gri|, |gO|, |i_CTRL-S|.
do
vim.keymap.set('n', 'grn', function()
vim.lsp.buf.rename()
@@ -175,6 +175,10 @@ do
vim.lsp.buf.implementation()
end, { desc = 'vim.lsp.buf.implementation()' })
+ vim.keymap.set('n', 'gO', function()
+ vim.lsp.buf.document_symbol()
+ end, { desc = 'vim.lsp.buf.document_symbol()' })
+
vim.keymap.set('i', '<C-S>', function()
vim.lsp.buf.signature_help()
end, { desc = 'vim.lsp.buf.signature_help()' })
@@ -234,6 +238,12 @@ do
create_unimpaired_mapping(']q', function()
vim.cmd.cnext({ count = vim.v.count1 })
end, ':cnext')
+ create_unimpaired_mapping('[Q', function()
+ vim.cmd.crewind({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, ':crewind')
+ create_unimpaired_mapping(']Q', function()
+ vim.cmd.clast({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, ':clast')
create_unimpaired_mapping('[<C-Q>', function()
vim.cmd.cpfile({ count = vim.v.count1 })
end, ':cpfile')
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 496bbf747c..c6aa303124 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -545,7 +545,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
-- TODO: handle double-width characters
if regtype:byte() == 22 then
local bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1]
- pos1[2] = vim.str_utfindex(bufline, pos1[2])
+ pos1[2] = vim.str_utfindex(bufline, 'utf-32', pos1[2])
end
local region = {}
@@ -557,14 +557,14 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
c2 = c1 + tonumber(regtype:sub(2))
-- and adjust for non-ASCII characters
local bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1]
- local utflen = vim.str_utfindex(bufline, #bufline)
+ local utflen = vim.str_utfindex(bufline, 'utf-32', #bufline)
if c1 <= utflen then
- c1 = assert(tonumber(vim.str_byteindex(bufline, c1)))
+ c1 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c1)))
else
c1 = #bufline + 1
end
if c2 <= utflen then
- c2 = assert(tonumber(vim.str_byteindex(bufline, c2)))
+ c2 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c2)))
else
c2 = #bufline + 1
end
@@ -740,9 +740,14 @@ function vim.str_byteindex(s, encoding, index, strict_indexing)
-- • {str} (`string`)
-- • {index} (`integer`)
-- • {use_utf16} (`boolean?`)
+ vim.deprecate(
+ 'vim.str_byteindex',
+ 'vim.str_byteindex(s, encoding, index, strict_indexing)',
+ '1.0'
+ )
local old_index = encoding
local use_utf16 = index or false
- return vim.__str_byteindex(s, old_index, use_utf16) or error('index out of range')
+ return vim._str_byteindex(s, old_index, use_utf16) or error('index out of range')
end
vim.validate('s', s, 'string')
@@ -769,7 +774,7 @@ function vim.str_byteindex(s, encoding, index, strict_indexing)
end
return index
end
- return vim.__str_byteindex(s, index, encoding == 'utf-16')
+ return vim._str_byteindex(s, index, encoding == 'utf-16')
or strict_indexing and error('index out of range')
or len
end
@@ -793,8 +798,13 @@ function vim.str_utfindex(s, encoding, index, strict_indexing)
-- Parameters: ~
-- • {str} (`string`)
-- • {index} (`integer?`)
+ vim.deprecate(
+ 'vim.str_utfindex',
+ 'vim.str_utfindex(s, encoding, index, strict_indexing)',
+ '1.0'
+ )
local old_index = encoding
- local col32, col16 = vim.__str_utfindex(s, old_index) --[[@as integer,integer]]
+ local col32, col16 = vim._str_utfindex(s, old_index) --[[@as integer,integer]]
if not col32 or not col16 then
error('index out of range')
end
@@ -828,7 +838,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing)
local len = #s
return index <= len and index or (strict_indexing and error('index out of range') or len)
end
- local col32, col16 = vim.__str_utfindex(s, index) --[[@as integer?,integer?]]
+ local col32, col16 = vim._str_utfindex(s, index) --[[@as integer?,integer?]]
local col = encoding == 'utf-16' and col16 or col32
if col then
return col
@@ -836,7 +846,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing)
if strict_indexing then
error('index out of range')
end
- local max32, max16 = vim.__str_utfindex(s)--[[@as integer integer]]
+ local max32, max16 = vim._str_utfindex(s)--[[@as integer integer]]
return encoding == 'utf-16' and max16 or max32
end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 00f7554832..710f82bf21 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -2294,6 +2294,62 @@ 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`
+--- 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 expression is evaluated only once per `:find` command invocation.
+--- The expression can process all the directories specified in 'path'.
+---
+--- 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`.
+---
+--- 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
+--- should return an empty List.
+---
+--- If any errors are encountered during the expression evaluation, 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`.
+---
+--- This option cannot be set from a `modeline` or in the `sandbox`, for
+--- security reasons.
+---
+--- Examples:
+---
+--- ```vim
+--- " Use glob()
+--- func FindExprGlob()
+--- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+--- return glob(pat, v:false, v:true)
+--- endfunc
+--- set findexpr=FindExprGlob()
+---
+--- " Use the 'git ls-files' output
+--- func FindGitFiles()
+--- let fnames = systemlist('git ls-files')
+--- return fnames->filter('v:val =~? v:fname')
+--- endfunc
+--- set findexpr=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
+
--- 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
--- preserve the situation from the original file.
diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index e00402ab3f..b104356334 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -44,6 +44,11 @@ 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
@@ -267,7 +272,8 @@ vim.v.fcs_choice = ...
vim.v.fcs_reason = ...
--- When evaluating 'includeexpr': the file name that was
---- detected. Empty otherwise.
+--- detected. When evaluating 'findexpr': the argument passed to
+--- the `:find` command. Empty otherwise.
--- @type string
vim.v.fname = ...
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 392db5b800..299f34c921 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -71,9 +71,9 @@ local M = {}
--- (default: `false`)
--- @field update_in_insert? boolean
---
---- Sort diagnostics by severity. This affects the order in which signs and
---- virtual text are displayed. When true, higher severities are displayed
---- before lower severities (e.g. ERROR is displayed before WARN).
+--- Sort diagnostics by severity. This affects the order in which signs,
+--- virtual text, and highlights are displayed. When true, higher severities are
+--- displayed before lower severities (e.g. ERROR is displayed before WARN).
--- Options:
--- - {reverse}? (boolean) Reverse sort order
--- (default: `false`)
@@ -657,6 +657,28 @@ local function save_extmarks(namespace, bufnr)
api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
end
+--- Create a function that converts a diagnostic severity to an extmark priority.
+--- @param priority integer Base priority
+--- @param opts vim.diagnostic.OptsResolved
+--- @return fun(severity: vim.diagnostic.Severity): integer
+local function severity_to_extmark_priority(priority, opts)
+ if opts.severity_sort then
+ if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
+ return function(severity)
+ return priority + (severity - vim.diagnostic.severity.ERROR)
+ end
+ end
+
+ return function(severity)
+ return priority + (vim.diagnostic.severity.HINT - severity)
+ end
+ end
+
+ return function()
+ return priority
+ end
+end
+
--- @type table<string,true>
local registered_autocmds = {}
@@ -1352,22 +1374,7 @@ M.handlers.signs = {
-- 10 is the default sign priority when none is explicitly specified
local priority = opts.signs and opts.signs.priority or 10
- local get_priority --- @type function
- if opts.severity_sort then
- if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
- get_priority = function(severity)
- return priority + (severity - vim.diagnostic.severity.ERROR)
- end
- else
- get_priority = function(severity)
- return priority + (vim.diagnostic.severity.HINT - severity)
- end
- end
- else
- get_priority = function()
- return priority
- end
- end
+ local get_priority = severity_to_extmark_priority(priority, opts)
local ns = M.get_namespace(namespace)
if not ns.user_data.sign_ns then
@@ -1478,6 +1485,8 @@ M.handlers.underline = {
end
local underline_ns = ns.user_data.underline_ns
+ local get_priority = severity_to_extmark_priority(vim.hl.priorities.diagnostics, opts)
+
for _, diagnostic in ipairs(diagnostics) do
--- @type string?
local higroup = underline_highlight_map[assert(diagnostic.severity)]
@@ -1504,7 +1513,7 @@ M.handlers.underline = {
higroup,
{ diagnostic.lnum, diagnostic.col },
{ diagnostic.end_lnum, diagnostic.end_col },
- { priority = vim.hl.priorities.diagnostics }
+ { priority = get_priority(diagnostic.severity) }
)
end
save_extmarks(underline_ns, bufnr)
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 685dcae851..a8ae283f64 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -189,9 +189,10 @@ local function reuse_client_default(client, config)
end
if config.root_dir then
+ local root = vim.uri_from_fname(config.root_dir)
for _, dir in ipairs(client.workspace_folders or {}) do
-- note: do not need to check client.root_dir since that should be client.workspace_folders[1]
- if config.root_dir == dir.name then
+ if root == dir.uri then
return true
end
end
@@ -1045,7 +1046,7 @@ function lsp.formatexpr(opts)
if client.supports_method(ms.textDocument_rangeFormatting) then
local params = util.make_formatting_params()
local end_line = vim.fn.getline(end_lnum) --[[@as string]]
- local end_col = util._str_utfindex_enc(end_line, nil, client.offset_encoding)
+ local end_col = vim.str_utfindex(end_line, client.offset_encoding)
--- @cast params +lsp.DocumentRangeFormattingParams
params.range = {
start = {
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index e36d329dc5..10086fa49e 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -315,7 +315,7 @@ local function adjust_start_col(lnum, line, items, encoding)
end
end
if min_start_char then
- return lsp.util._str_byteindex_enc(line, min_start_char, encoding)
+ return vim.str_byteindex(line, encoding, min_start_char, false)
else
return nil
end
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index bf72222536..c59e2db901 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -33,25 +33,6 @@ local function severity_vim_to_lsp(severity)
return severity
end
----@param lines string[]?
----@param lnum integer
----@param col integer
----@param offset_encoding string
----@return integer
-local function line_byte_from_position(lines, lnum, col, offset_encoding)
- if not lines or offset_encoding == 'utf-8' then
- return col
- end
-
- local line = lines[lnum + 1]
- local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16')
- if ok then
- return result --- @type integer
- end
-
- return col
-end
-
---@param bufnr integer
---@return string[]?
local function get_buf_lines(bufnr)
@@ -118,12 +99,13 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
)
message = diagnostic.message.value
end
+ local line = buf_lines and buf_lines[start.line + 1] or ''
--- @type vim.Diagnostic
return {
lnum = start.line,
- col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
+ col = vim.str_byteindex(line, offset_encoding, start.character, false),
end_lnum = _end.line,
- end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding),
+ end_col = vim.str_byteindex(line, offset_encoding, _end.character, false),
severity = severity_lsp_to_vim(diagnostic.severity),
message = message,
source = diagnostic.source,
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index 18066a84db..0d314108fe 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -39,12 +39,27 @@ local function check_active_clients()
elseif type(client.config.cmd) == 'function' then
cmd = tostring(client.config.cmd)
end
+ local dirs_info ---@type string
+ if client.workspace_folders and #client.workspace_folders > 1 then
+ dirs_info = string.format(
+ ' Workspace folders:\n %s',
+ vim
+ .iter(client.workspace_folders)
+ ---@param folder lsp.WorkspaceFolder
+ :map(function(folder)
+ return folder.name
+ end)
+ :join('\n ')
+ )
+ else
+ dirs_info = string.format(
+ ' Root directory: %s',
+ client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~')
+ ) or nil
+ end
report_info(table.concat({
string.format('%s (id: %d)', client.name, client.id),
- string.format(
- ' Root directory: %s',
- client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') or nil
- ),
+ dirs_info,
string.format(' Command: %s', cmd),
string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
string.format(
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index 61d119e653..e5892928cf 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -70,20 +70,12 @@ function M.on_inlayhint(err, result, ctx, _)
end
local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
- ---@param position lsp.Position
- ---@return integer
- local function pos_to_byte(position)
- local col = position.character
- if col > 0 then
- local line = lines[position.line + 1] or ''
- return util._str_byteindex_enc(line, col, client.offset_encoding)
- end
- return col
- end
for _, hint in ipairs(result) do
local lnum = hint.position.line
- hint.position.character = pos_to_byte(hint.position)
+ local line = lines and lines[lnum + 1] or ''
+ hint.position.character =
+ vim.str_byteindex(line, client.offset_encoding, hint.position.character, false)
table.insert(new_lnum_hints[lnum], hint)
end
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 0f6e45c330..d680522592 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -137,16 +137,10 @@ local function tokens_to_ranges(data, bufnr, client, request)
local token_type = token_types[data[i + 3] + 1]
local modifiers = modifiers_from_number(data[i + 4], token_modifiers)
- local function _get_byte_pos(col)
- if col > 0 then
- local buf_line = lines[line + 1] or ''
- return util._str_byteindex_enc(buf_line, col, client.offset_encoding)
- end
- return col
- end
-
- local start_col = _get_byte_pos(start_char)
- local end_col = _get_byte_pos(start_char + data[i + 2])
+ local end_char = start_char + data[i + 2]
+ local buf_line = lines and lines[line + 1] or ''
+ local start_col = vim.str_byteindex(buf_line, client.offset_encoding, start_char, false)
+ local end_col = vim.str_byteindex(buf_line, client.offset_encoding, end_char, false)
if token_type then
ranges[#ranges + 1] = {
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index bdfe8d51b8..3df45ebff0 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -48,45 +48,6 @@ local str_utfindex = vim.str_utfindex
local str_utf_start = vim.str_utf_start
local str_utf_end = vim.str_utf_end
--- Given a line, byte idx, and offset_encoding convert to the
--- utf-8, utf-16, or utf-32 index.
----@param line string the line to index into
----@param byte integer the byte idx
----@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8)
----@return integer utf_idx for the given encoding
-local function byte_to_utf(line, byte, offset_encoding)
- -- convert to 0 based indexing for str_utfindex
- byte = byte - 1
-
- local utf_idx, _ --- @type integer, integer
- -- Convert the byte range to utf-{8,16,32} and convert 1-based (lua) indexing to 0-based
- if offset_encoding == 'utf-16' then
- _, utf_idx = str_utfindex(line, byte)
- elseif offset_encoding == 'utf-32' then
- utf_idx, _ = str_utfindex(line, byte)
- else
- utf_idx = byte
- end
-
- -- convert to 1 based indexing
- return utf_idx + 1
-end
-
----@param line string
----@param offset_encoding string
----@return integer
-local function compute_line_length(line, offset_encoding)
- local length, _ --- @type integer, integer
- if offset_encoding == 'utf-16' then
- _, length = str_utfindex(line)
- elseif offset_encoding == 'utf-32' then
- length, _ = str_utfindex(line)
- else
- length = #line
- end
- return length
-end
-
-- Given a line, byte idx, alignment, and offset_encoding convert to the aligned
-- utf-8 index and either the utf-16, or utf-32 index.
---@param line string the line to index into
@@ -101,7 +62,7 @@ local function align_end_position(line, byte, offset_encoding)
char = byte
-- Called in the case of extending an empty line "" -> "a"
elseif byte == #line + 1 then
- char = compute_line_length(line, offset_encoding) + 1
+ char = str_utfindex(line, offset_encoding) + 1
else
-- Modifying line, find the nearest utf codepoint
local offset = str_utf_start(line, byte)
@@ -111,9 +72,10 @@ local function align_end_position(line, byte, offset_encoding)
byte = byte + str_utf_end(line, byte) + 1
end
if byte <= #line then
- char = byte_to_utf(line, byte, offset_encoding)
+ --- Convert to 0 based for input, and from 0 based for output
+ char = str_utfindex(line, offset_encoding, byte - 1) + 1
else
- char = compute_line_length(line, offset_encoding) + 1
+ char = str_utfindex(line, offset_encoding) + 1
end
-- Extending line, find the nearest utf codepoint for the last valid character
end
@@ -153,7 +115,7 @@ local function compute_start_range(
if line then
line_idx = firstline - 1
byte_idx = #line + 1
- char_idx = compute_line_length(line, offset_encoding) + 1
+ char_idx = str_utfindex(line, offset_encoding) + 1
else
line_idx = firstline
byte_idx = 1
@@ -190,10 +152,11 @@ local function compute_start_range(
char_idx = 1
elseif start_byte_idx == #prev_line + 1 then
byte_idx = start_byte_idx
- char_idx = compute_line_length(prev_line, offset_encoding) + 1
+ char_idx = str_utfindex(prev_line, offset_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
- char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding)
+ --- Convert to 0 based for input, and from 0 based for output
+ char_idx = vim.str_utfindex(prev_line, offset_encoding, byte_idx - 1) + 1
end
-- Return the start difference (shared for new and prev lines)
@@ -230,7 +193,7 @@ local function compute_end_range(
return {
line_idx = lastline - 1,
byte_idx = #prev_line + 1,
- char_idx = compute_line_length(prev_line, offset_encoding) + 1,
+ char_idx = str_utfindex(prev_line, offset_encoding) + 1,
}, { line_idx = 1, byte_idx = 1, char_idx = 1 }
end
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
@@ -376,7 +339,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi
local start_line = lines[start_range.line_idx]
local range_length --- @type integer
if start_line and #start_line > 0 then
- range_length = compute_line_length(start_line, offset_encoding)
+ range_length = str_utfindex(start_line, offset_encoding)
- start_range.char_idx
+ 1
+ line_ending_length
@@ -389,7 +352,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi
for idx = start_range.line_idx + 1, end_range.line_idx - 1 do
-- Length full line plus newline character
if #lines[idx] > 0 then
- range_length = range_length + compute_line_length(lines[idx], offset_encoding) + #line_ending
+ range_length = range_length + str_utfindex(lines[idx], offset_encoding) + #line_ending
else
range_length = range_length + line_ending_length
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 2e9c71cf38..41f93e5374 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -116,71 +116,6 @@ local function create_window_without_focus()
return new
end
---- Convert byte index to `encoding` index.
---- Convenience wrapper around vim.str_utfindex
----@param line string line to be indexed
----@param index integer? byte index (utf-8), or `nil` for length
----@param encoding 'utf-8'|'utf-16'|'utf-32'? defaults to utf-16
----@return integer `encoding` index of `index` in `line`
-function M._str_utfindex_enc(line, index, encoding)
- local len32, len16 = vim.str_utfindex(line)
- if not encoding then
- encoding = 'utf-16'
- end
- if encoding == 'utf-8' then
- if index then
- return index
- else
- return #line
- end
- elseif encoding == 'utf-16' then
- if not index or index > len16 then
- return len16
- end
- local _, col16 = vim.str_utfindex(line, index)
- return col16
- elseif encoding == 'utf-32' then
- if not index or index > len32 then
- return len32
- end
- local col32, _ = vim.str_utfindex(line, index)
- return col32
- else
- error('Invalid encoding: ' .. vim.inspect(encoding))
- end
-end
-
---- Convert UTF index to `encoding` index.
---- Convenience wrapper around vim.str_byteindex
----Alternative to vim.str_byteindex that takes an encoding.
----@param line string line to be indexed
----@param index integer UTF index
----@param encoding string utf-8|utf-16|utf-32| defaults to utf-16
----@return integer byte (utf-8) index of `encoding` index `index` in `line`
-function M._str_byteindex_enc(line, index, encoding)
- -- LSP spec: if character > line length, default to the line length.
- -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
- local len8 = #line
- if not encoding then
- encoding = 'utf-16'
- end
- if encoding == 'utf-8' then
- if index and index <= len8 then
- return index
- else
- return len8
- end
- end
- local len32, len16 = vim.str_utfindex(line)
- if encoding == 'utf-16' then
- return index <= len16 and vim.str_byteindex(line, index, true) or len8
- elseif encoding == 'utf-32' then
- return index <= len32 and vim.str_byteindex(line, index) or len8
- else
- error('Invalid encoding: ' .. vim.inspect(encoding))
- end
-end
-
--- Replaces text in a range with new text.
---
--- CAUTION: Changes in-place!
@@ -352,7 +287,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- character
if col > 0 then
local line = get_line(bufnr, position.line) or ''
- return M._str_byteindex_enc(line, col, offset_encoding)
+ return vim.str_byteindex(line, offset_encoding, col, false)
end
return col
end
@@ -1022,7 +957,7 @@ end
--- Jumps to a location.
---
----@deprecated
+---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead
---@param location lsp.Location|lsp.LocationLink
---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'?
---@param reuse_win boolean? Jump to existing window if buffer is already open.
@@ -1787,8 +1722,8 @@ function M.locations_to_items(locations, offset_encoding)
local end_row = end_pos.line
local line = lines[row] or ''
local end_line = lines[end_row] or ''
- local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
- local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding)
+ local col = vim.str_byteindex(line, offset_encoding, pos.character, false)
+ local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false)
items[#items + 1] = {
filename = filename,
@@ -1911,7 +1846,7 @@ local function make_position_param(window, offset_encoding)
return { line = 0, character = 0 }
end
- col = M._str_utfindex_enc(line, col, offset_encoding)
+ col = vim.str_utfindex(line, offset_encoding, col, false)
return { line = row, character = col }
end
@@ -2092,7 +2027,7 @@ function M.character_offset(buf, row, col, offset_encoding)
)
offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding
end
- return M._str_utfindex_enc(line, col, offset_encoding)
+ return vim.str_utfindex(line, offset_encoding, col, false)
end
--- Helper function to return nested values in language server settings
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 4614967799..1677e8d364 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -626,7 +626,7 @@ local directive_handlers = {
--- Adds a new predicate to be used in queries
---
---@param name string Name of the predicate, without leading #
----@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata)
+---@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata): boolean?
--- - see |vim.treesitter.query.add_directive()| for argument meanings
---@param opts? vim.treesitter.query.add_predicate.Opts
function M.add_predicate(name, handler, opts)
diff --git a/runtime/syntax/awk.vim b/runtime/syntax/awk.vim
index 3082c1cb5c..4e7c0d1e72 100644
--- a/runtime/syntax/awk.vim
+++ b/runtime/syntax/awk.vim
@@ -2,7 +2,7 @@
" Language: awk, nawk, gawk, mawk
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Antonio Colombo <azc100@gmail.com>
-" Last Change: 2020 Aug 18
+" Last Change: 2024 Oct 28
" AWK ref. is: Alfred V. Aho, Brian W. Kernighan, Peter J. Weinberger
" The AWK Programming Language, Addison-Wesley, 1988
@@ -92,7 +92,7 @@ syn match awkSpecialCharacter display contained "\\x[0-9A-Fa-f]\+"
syn match awkFieldVars "\$\d\+"
" catch errors caused by wrong parenthesis
-syn region awkParen transparent start="(" end=")" contains=ALLBUT,awkParenError,awkSpecialCharacter,awkArrayElement,awkArrayArray,awkTodo,awkRegExp,awkBrktRegExp,awkBrackets,awkCharClass,awkComment
+syn region awkParen transparent start="(" end=")" contains=ALLBUT,awkParenError,awkSpecialCharacter,awkArrayElement,awkArrayArray,awkTodo,awkRegExp,awkBrktRegExp,awkBrackets,awkCharClass
syn match awkParenError display ")"
"syn match awkInParen display contained "[{}]"
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index e6b81d63b9..95d4f4eafd 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -302,7 +302,7 @@ it would be easier to simply type two d's to delete a line.
3. Now move to the fourth line.
- 4. Type `2dd`{normal} to delete two lines.
+ 4. Type `2dd`{normal} to delete two lines, then press `u`{normal} twice to undo all three lines.
1) Roses are red,
2) Mud is fun,
@@ -689,7 +689,7 @@ NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move the c
1. Place the cursor just above this line.
NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move
- DOWN to see this lesson again.
+ DOWN to see this lesson again. Press `u`{normal} to undo after you are done.
2. Now retrieve your TEST file using the command
@@ -736,12 +736,12 @@ NOTE: You can also read the output of an external command. For example,
2. Type the lowercase letter `o`{normal} to [open](o) up a line BELOW the
cursor and place you in Insert mode.
- 3. Now type some text and press `<Esc>`{normal} to exit Insert mode.
+ 3. Now type some text and press `<Esc>`{normal} to exit Insert mode. Remove your opened lines after you're done.
After typing `o`{normal} the cursor is placed on the open line in Insert mode.
4. To open up a line ABOVE the cursor, simply type a [capital O](O), rather
- than a lowercase `o`{normal}. Try this on the line below.
+ than a lowercase `o`{normal}. Try this on the line below. Remove your opened lines after you're done.
Open up a line above this by typing O while the cursor is on this line.
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index e61f1a8ce2..84735c293a 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -73,10 +73,10 @@ return {
'InsertLeavePre', -- just before leaving Insert mode
'LspAttach', -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer
- 'LspRequest', -- after an LSP request is started, canceled, or completed
'LspNotify', -- after an LSP notice has been sent to the server
- 'LspTokenUpdate', -- after a visible LSP token is updated
'LspProgress', -- after a LSP progress update
+ 'LspRequest', -- after an LSP request is started, canceled, or completed
+ 'LspTokenUpdate', -- after a visible LSP token is updated
'MenuPopup', -- just before popup menu is displayed
'ModeChanged', -- after changing the mode
'OptionSet', -- after setting any option
@@ -160,8 +160,8 @@ return {
LspAttach = true,
LspDetach = true,
LspNotify = true,
- LspRequest = true,
LspProgress = true,
+ LspRequest = true,
LspTokenUpdate = true,
RecordingEnter = true,
RecordingLeave = true,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 2142b5b298..42cc745fe6 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -2049,6 +2049,7 @@ 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);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 1fe5512708..88e8d59faa 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -608,6 +608,7 @@ 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
@@ -1249,7 +1250,7 @@ struct window_S {
// transform a pointer to a "onebuf" option into a "allbuf" option
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
- // A few options have local flags for P_INSECURE.
+ // A few options have local flags for kOptFlagInsecure.
uint32_t w_p_stl_flags; // flags for 'statusline'
uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 250d705ee6..aeaed536fc 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -109,6 +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_HELP
&& xp->xp_context != EXPAND_KEYMAP
&& xp->xp_context != EXPAND_LUA
@@ -1228,7 +1229,8 @@ 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_HELP
+ if (context == EXPAND_FINDEXPR
+ || context == EXPAND_HELP
|| context == EXPAND_COLORS
|| context == EXPAND_COMPILER
|| context == EXPAND_OWNSYNTAX
@@ -1350,7 +1352,7 @@ char *addstar(char *fname, size_t len, int context)
/// it.
/// EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
/// EXPAND_FILES After command with EX_XFILE set, or after setting
-/// with P_EXPAND set. eg :e ^I, :w>>^I
+/// with kOptFlagExpand set. eg :e ^I, :w>>^I
/// EXPAND_DIRECTORIES In some cases this is used instead of the latter
/// when we know only directories are of interest.
/// E.g. :set dir=^I and :cd ^I
@@ -1827,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 = EXPAND_FILES_IN_PATH;
+ xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR : EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
@@ -2497,21 +2499,25 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
}
- if (xp->xp_context == EXPAND_FILES) {
- flags |= EW_FILE;
- } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
- flags |= (EW_FILE | EW_PATH);
- } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
- flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE;
+ int ret = FAIL;
+ if (xp->xp_context == EXPAND_FINDEXPR) {
+ ret = expand_findexpr(pat, matches, numMatches);
} else {
- flags = (flags | EW_DIR) & ~EW_FILE;
- }
- if (options & WILD_ICASE) {
- flags |= EW_ICASE;
+ if (xp->xp_context == EXPAND_FILES) {
+ flags |= EW_FILE;
+ } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
+ flags |= (EW_FILE | EW_PATH);
+ } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
+ flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE;
+ } else {
+ flags = (flags | EW_DIR) & ~EW_FILE;
+ }
+ if (options & WILD_ICASE) {
+ flags |= EW_ICASE;
+ }
+ // Expand wildcards, supporting %:h and the like.
+ ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
}
-
- // Expand wildcards, supporting %:h and the like.
- int ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
if (free_pat) {
xfree(pat);
}
@@ -2716,6 +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_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 86725eafd6..ce51f30bec 100644
--- a/src/nvim/cmdexpand_defs.h
+++ b/src/nvim/cmdexpand_defs.h
@@ -107,6 +107,7 @@ enum {
EXPAND_KEYMAP,
EXPAND_DIRS_IN_CDPATH,
EXPAND_SHELLCMDLINE,
+ EXPAND_FINDEXPR,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
index 39095db952..6682a42d61 100644
--- a/src/nvim/errors.h
+++ b/src/nvim/errors.h
@@ -156,6 +156,11 @@ EXTERN const char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called i
EXTERN const char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain"));
EXTERN const char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float"));
+EXTERN const char e_cant_find_directory_str_in_cdpath[] INIT(= N_("E344: Can't find directory \"%s\" in cdpath"));
+EXTERN const char e_cant_find_file_str_in_path[] INIT(= N_("E345: Can't find file \"%s\" in path"));
+EXTERN const char e_no_more_directory_str_found_in_cdpath[] INIT(= N_("E346: No more directory \"%s\" found in cdpath"));
+EXTERN const char e_no_more_file_str_found_in_path[] INIT(= N_("E347: No more file \"%s\" found in path"));
+
EXTERN const char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
EXTERN const char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long"));
@@ -181,6 +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_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 93cff80bd4..bf85ed1646 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -270,6 +270,7 @@ 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),
@@ -460,6 +461,9 @@ void eval_init(void)
set_vim_var_nr(VV_SEARCHFORWARD, 1);
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);
set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
@@ -475,7 +479,6 @@ void eval_init(void)
set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX);
set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN);
set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
- set_vim_var_special(VV_EXITING, kSpecialVarNull);
set_vim_var_nr(VV_MAXCOL, MAXCOL);
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
@@ -2631,7 +2634,7 @@ static int may_call_simple_func(const char *arg, typval_T *rettv)
/// Handle zero level expression with optimization for a simple function call.
/// Same arguments and return value as eval0().
-static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
+int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int r = may_call_simple_func(arg, rettv);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index bb9b00abc7..b5605bb644 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -167,6 +167,7 @@ typedef enum {
VV_COLLATE,
VV_EXITING,
VV_MAXCOL,
+ VV_CMDCOMPLETE,
// Nvim
VV_STDERR,
VV_MSGPACK_TYPES,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 5125bd0b88..34045c7c9d 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -11015,6 +11015,44 @@ M.funcs = {
params = { { 'expr', 'number' } },
signature = 'srand([{expr}])',
},
+ state = {
+ args = { 0, 1 },
+ base = 1,
+ desc = [=[
+ Return a string which contains characters indicating the
+ current state. Mostly useful in callbacks that want to do
+ work that may not always be safe. Roughly this works like:
+ - callback uses state() to check if work is safe to do.
+ Yes: then do it right away.
+ No: add to work queue and add a |SafeState| autocommand.
+ - When SafeState is triggered and executes your autocommand,
+ check with `state()` if the work can be done now, and if yes
+ remove it from the queue and execute.
+ Remove the autocommand if the queue is now empty.
+ Also see |mode()|.
+
+ When {what} is given only characters in this string will be
+ added. E.g, this checks if the screen has scrolled: >vim
+ if state('s') == ''
+ " screen has not scrolled
+ <
+ These characters indicate the state, generally indicating that
+ something is busy:
+ m halfway a mapping, :normal command, feedkeys() or
+ stuffed command
+ o operator pending, e.g. after |d|
+ a Insert mode autocomplete active
+ x executing an autocommand
+ S not triggering SafeState, e.g. after |f| or a count
+ c callback invoked, including timer (repeats for
+ recursiveness up to "ccc")
+ s screen has scrolled for messages
+ ]=],
+ fast = true,
+ name = 'state',
+ params = { { 'what', 'string' } },
+ signature = 'state([{what}])',
+ },
stdioopen = {
args = 1,
desc = [=[
@@ -11073,44 +11111,6 @@ M.funcs = {
returns = 'string|string[]',
signature = 'stdpath({what})',
},
- state = {
- args = { 0, 1 },
- base = 1,
- desc = [=[
- Return a string which contains characters indicating the
- current state. Mostly useful in callbacks that want to do
- work that may not always be safe. Roughly this works like:
- - callback uses state() to check if work is safe to do.
- Yes: then do it right away.
- No: add to work queue and add a |SafeState| autocommand.
- - When SafeState is triggered and executes your autocommand,
- check with `state()` if the work can be done now, and if yes
- remove it from the queue and execute.
- Remove the autocommand if the queue is now empty.
- Also see |mode()|.
-
- When {what} is given only characters in this string will be
- added. E.g, this checks if the screen has scrolled: >vim
- if state('s') == ''
- " screen has not scrolled
- <
- These characters indicate the state, generally indicating that
- something is busy:
- m halfway a mapping, :normal command, feedkeys() or
- stuffed command
- o operator pending, e.g. after |d|
- a Insert mode autocomplete active
- x executing an autocommand
- S not triggering SafeState, e.g. after |f| or a count
- c callback invoked, including timer (repeats for
- recursiveness up to "ccc")
- s screen has scrolled for messages
- ]=],
- fast = true,
- name = 'state',
- params = { { 'what', 'string' } },
- signature = 'state([{what}])',
- },
str2float = {
args = 1,
base = 1,
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index d002bff321..d3f836f7f4 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -1908,7 +1908,7 @@ static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, b
const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber);
const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString);
- if (!is_tty_opt && (get_option(opt_idx)->flags & P_FUNC) && tv_is_func(*tv)) {
+ if (!is_tty_opt && (get_option(opt_idx)->flags & kOptFlagFunc) && tv_is_func(*tv)) {
// If the option can be set to a function reference or a lambda
// and the passed value is a function reference, then convert it to
// the name (string) of the function reference.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 293aaac036..74ba19b30a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5165,6 +5165,115 @@ 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)
+{
+ const sctx_T saved_sctx = current_sctx;
+
+ char *findexpr = get_findexpr();
+
+ 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;
+
+ char *arg = skipwhite(findexpr);
+
+ 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());
+ } else {
+ emsg(_(e_invalid_return_type_from_findexpr));
+ }
+ 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;
+
+ return retlist;
+}
+
+/// Find file names matching "pat" using 'findexpr' 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)
+{
+ *numMatches = 0;
+ *files = NULL;
+
+ list_T *l = eval_findexpr(pat, true);
+ if (l == NULL) {
+ return FAIL;
+ }
+
+ int len = tv_list_len(l);
+ if (len == 0) { // empty List
+ return FAIL;
+ }
+
+ *files = xmalloc(sizeof(char *) * (size_t)len);
+
+ // Copy all the List items
+ int idx = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
+ (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ idx++;
+ }
+ });
+
+ *numMatches = idx;
+ tv_list_free(l);
+
+ return OK;
+}
+
+/// Use 'findexpr' 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)
+{
+ char *ret_fname = NULL;
+
+ const char cc = findarg[findarg_len];
+ findarg[findarg_len] = NUL;
+
+ list_T *fname_list = eval_findexpr(findarg, false);
+ int fname_count = tv_list_len(fname_list);
+
+ if (fname_count == 0) {
+ semsg(_(e_cant_find_file_str_in_path), findarg);
+ } else {
+ if (count > fname_count) {
+ semsg(_(e_no_more_file_str_found_in_path), findarg);
+ } else {
+ listitem_T *li = tv_list_find(fname_list, count - 1);
+ if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
+ ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ }
+ }
+ }
+
+ if (fname_list != NULL) {
+ tv_list_free(fname_list);
+ }
+
+ findarg[findarg_len] = cc;
+
+ return ret_fname;
+}
+
/// :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
@@ -5196,13 +5305,17 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
- char *file_to_find = NULL;
- char *search_ctx = NULL;
- fname = find_file_in_path(eap->arg, strlen(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname,
- &file_to_find, &search_ctx);
- xfree(file_to_find);
- vim_findfile_cleanup(search_ctx);
+ if (*get_findexpr() != NUL) {
+ fname = findexpr_find_file(eap->arg, strlen(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+ } else {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+ fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
+ }
if (fname == NULL) {
goto theend;
}
@@ -5398,23 +5511,28 @@ static void ex_find(exarg_T *eap)
return;
}
- char *file_to_find = NULL;
- char *search_ctx = NULL;
- char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname,
- &file_to_find, &search_ctx);
- if (eap->addr_count > 0) {
- // Repeat finding the file "count" times. This matters when it appears
- // several times in the path.
- linenr_T count = eap->line2;
- while (fname != NULL && --count > 0) {
- xfree(fname);
- fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname,
- &file_to_find, &search_ctx);
+ char *fname = NULL;
+ if (*get_findexpr() != NUL) {
+ fname = findexpr_find_file(eap->arg, strlen(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+ } else {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+ fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ if (eap->addr_count > 0) {
+ // Repeat finding the file "count" times. This matters when it appears
+ // several times in the path.
+ linenr_T count = eap->line2;
+ while (fname != NULL && --count > 0) {
+ xfree(fname);
+ fname = find_file_in_path(NULL, 0, FNAME_MESS, false,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ }
}
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
}
- xfree(file_to_find);
- vim_findfile_cleanup(search_ctx);
if (fname == NULL) {
return;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index cdfd281718..aeaf448a05 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1489,15 +1489,15 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
if (file_name == NULL && (options & FNAME_MESS)) {
if (first == true) {
if (find_what == FINDFILE_DIR) {
- semsg(_("E344: Can't find directory \"%s\" in cdpath"), *file_to_find);
+ semsg(_(e_cant_find_directory_str_in_cdpath), *file_to_find);
} else {
- semsg(_("E345: Can't find file \"%s\" in path"), *file_to_find);
+ semsg(_(e_cant_find_file_str_in_path), *file_to_find);
}
} else {
if (find_what == FINDFILE_DIR) {
- semsg(_("E346: No more directory \"%s\" found in cdpath"), *file_to_find);
+ semsg(_(e_no_more_directory_str_found_in_cdpath), *file_to_find);
} else {
- semsg(_("E347: No more file \"%s\" found in path"), *file_to_find);
+ semsg(_(e_no_more_file_str_found_in_path), *file_to_find);
}
}
}
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 591a6b93df..0cb5fa8e95 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -16,23 +16,23 @@ local options = require('options')
local cstr = options.cstr
local redraw_flags = {
- ui_option = 'P_UI_OPTION',
- tabline = 'P_RTABL',
- statuslines = 'P_RSTAT',
- current_window = 'P_RWIN',
- current_buffer = 'P_RBUF',
- all_windows = 'P_RALL',
- curswant = 'P_CURSWANT',
- highlight_only = 'P_HLONLY',
+ ui_option = 'kOptFlagUIOption',
+ tabline = 'kOptFlagRedrTabl',
+ statuslines = 'kOptFlagRedrStat',
+ current_window = 'kOptFlagRedrWin',
+ current_buffer = 'kOptFlagRedrBuf',
+ all_windows = 'kOptFlagRedrAll',
+ curswant = 'kOptFlagCurswant',
+ highlight_only = 'kOptFlagHLOnly',
}
local list_flags = {
- comma = 'P_COMMA',
- onecomma = 'P_ONECOMMA',
- commacolon = 'P_COMMA|P_COLON',
- onecommacolon = 'P_ONECOMMA|P_COLON',
- flags = 'P_FLAGLIST',
- flagscomma = 'P_COMMA|P_FLAGLIST',
+ comma = 'kOptFlagComma',
+ onecomma = 'kOptFlagOneComma',
+ commacolon = 'kOptFlagComma|kOptFlagColon',
+ onecommacolon = 'kOptFlagOneComma|kOptFlagColon',
+ flags = 'kOptFlagFlagList',
+ flagscomma = 'kOptFlagComma|kOptFlagFlagList',
}
--- @param s string
@@ -61,28 +61,27 @@ local function get_flags(o)
end
end
if o.expand then
- add_flag('P_EXPAND')
+ add_flag('kOptFlagExpand')
if o.expand == 'nodefault' then
- add_flag('P_NO_DEF_EXP')
+ add_flag('kOptFlagNoDefExp')
end
end
for _, flag_desc in ipairs({
- { 'alloced' },
- { 'nodefault' },
- { 'no_mkrc' },
+ { 'nodefault', 'NoDefault' },
+ { 'no_mkrc', 'NoMkrc' },
{ 'secure' },
{ 'gettext' },
- { 'noglob' },
- { 'normal_fname_chars', 'P_NFNAME' },
- { 'normal_dname_chars', 'P_NDNAME' },
- { 'pri_mkrc' },
- { 'deny_in_modelines', 'P_NO_ML' },
- { 'deny_duplicates', 'P_NODUP' },
- { 'modelineexpr', 'P_MLE' },
+ { 'noglob', 'NoGlob' },
+ { 'normal_fname_chars', 'NFname' },
+ { 'normal_dname_chars', 'NDname' },
+ { 'pri_mkrc', 'PriMkrc' },
+ { 'deny_in_modelines', 'NoML' },
+ { 'deny_duplicates', 'NoDup' },
+ { 'modelineexpr', 'MLE' },
{ 'func' },
}) do
local key_name = flag_desc[1]
- local def_name = flag_desc[2] or ('P_' .. key_name:upper())
+ local def_name = 'kOptFlag' .. (flag_desc[2] or lowercase_to_titlecase(key_name))
if o[key_name] then
add_flag(def_name)
end
@@ -90,6 +89,12 @@ local function get_flags(o)
return flags
end
+--- @param opt_type vim.option_type
+--- @return string
+local function opt_type_enum(opt_type)
+ return ('kOptValType%s'):format(lowercase_to_titlecase(opt_type))
+end
+
--- @param o vim.option_meta
--- @return string
local function get_type_flags(o)
@@ -99,7 +104,7 @@ local function get_type_flags(o)
for _, opt_type in ipairs(opt_types) do
assert(type(opt_type) == 'string')
- type_flags = ('%s | (1 << kOptValType%s)'):format(type_flags, lowercase_to_titlecase(opt_type))
+ type_flags = ('%s | (1 << %s)'):format(type_flags, opt_type_enum(opt_type))
end
return type_flags
@@ -125,27 +130,48 @@ local function get_cond(c, base_string)
return cond_string
end
+--- @param s string
+--- @return string
+local static_cstr_as_string = function(s)
+ return ('{ .data = %s, .size = sizeof(%s) - 1 }'):format(s, s)
+end
+
+--- @param v vim.option_value|function
+--- @return string
+local get_opt_val = function(v)
+ --- @type vim.option_type
+ local v_type
+
+ if type(v) == 'function' then
+ v, v_type = v() --[[ @as string, vim.option_type ]]
+
+ if v_type == 'string' then
+ v = static_cstr_as_string(v)
+ end
+ else
+ v_type = type(v) --[[ @as vim.option_type ]]
+
+ if v_type == 'boolean' then
+ v = v and 'true' or 'false'
+ elseif v_type == 'number' then
+ v = ('%iL'):format(v)
+ elseif v_type == 'string' then
+ v = static_cstr_as_string(cstr(v))
+ end
+ end
+
+ return ('{ .type = %s, .data.%s = %s }'):format(opt_type_enum(v_type), v_type, v)
+end
+
+--- @param d vim.option_value|function
+--- @param n string
+--- @return string
+
local get_defaults = function(d, n)
if d == nil then
error("option '" .. n .. "' should have a default value")
end
-
- local value_dumpers = {
- ['function'] = function(v)
- return v()
- end,
- string = function(v)
- return '.string=' .. cstr(v)
- end,
- boolean = function(v)
- return '.boolean=' .. (v and 'true' or 'false')
- end,
- number = function(v)
- return ('.number=%iL'):format(v)
- end,
- }
-
- return value_dumpers[type(d)](d)
+ return get_opt_val(d)
end
--- @type [string,string][]
@@ -173,7 +199,7 @@ local function dump_option(i, o)
w(' .var=&' .. o.varname)
elseif o.hidden or o.immutable then
-- Hidden and immutable options can directly point to the default value.
- w((' .var=&options[%u].def_val'):format(i - 1))
+ 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
@@ -219,14 +245,16 @@ local function dump_option(i, o)
if o.defaults.condition then
w(get_cond(o.defaults.condition))
end
- w(' .def_val' .. get_defaults(o.defaults.if_true, o.full_name))
+ w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
if o.defaults.condition then
if o.defaults.if_false then
w('#else')
- w(' .def_val' .. get_defaults(o.defaults.if_false, o.full_name))
+ w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
end
w('#endif')
end
+ else
+ w(' .def_val=NIL_OPTVAL')
end
w(' },')
end
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 45a31ce6b0..1cdb38f5f8 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -713,12 +713,6 @@ EXTERN char *escape_chars INIT( = " \t\\\"|"); // need backslash in cmd line
EXTERN bool keep_help_flag INIT( = false); // doing :ta from help file
-// When a string option is NULL (which only happens in out-of-memory situations), it is set to
-// empty_string_option, to avoid having to check for NULL everywhere.
-//
-// TODO(famiu): Remove this when refcounted strings are used for string options.
-EXTERN char *empty_string_option INIT( = "");
-
EXTERN bool redir_off INIT( = false); // no redirection for a moment
EXTERN FILE *redir_fd INIT( = NULL); // message redirection file
EXTERN int redir_reg INIT( = 0); // message redirection register
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index bf8b085458..e719d99640 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -699,10 +699,10 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
lua_setfield(lstate, -2, "stricmp");
// str_utfindex
lua_pushcfunction(lstate, &nlua_str_utfindex);
- lua_setfield(lstate, -2, "__str_utfindex");
+ lua_setfield(lstate, -2, "_str_utfindex");
// str_byteindex
lua_pushcfunction(lstate, &nlua_str_byteindex);
- lua_setfield(lstate, -2, "__str_byteindex");
+ lua_setfield(lstate, -2, "_str_byteindex");
// str_utf_pos
lua_pushcfunction(lstate, &nlua_str_utf_pos);
lua_setfield(lstate, -2, "str_utf_pos");
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 1ebf835eb5..9ea55dbd0c 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -1151,7 +1151,7 @@ static int __has_ancestor(lua_State *L)
int const pred_len = (int)lua_objlen(L, 2);
TSNode node = ts_tree_root_node(descendant.tree);
- while (node.id != descendant.id) {
+ while (node.id != descendant.id && !ts_node_is_null(node)) {
char const *node_type = ts_node_type(node);
size_t node_type_len = strlen(node_type);
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index e055ebc2fa..23efd2a841 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -568,6 +568,12 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
mapblock_T *mp_result[2] = { NULL, NULL };
+ bool unmap_lhs_only = false;
+ if (maptype == MAPTYPE_UNMAP_LHS) {
+ unmap_lhs_only = true;
+ maptype = MAPTYPE_UNMAP;
+ }
+
// For ":noremap" don't remap, otherwise do remap.
int noremap = args->script ? REMAP_SCRIPT
: maptype == MAPTYPE_NOREMAP ? REMAP_NONE : REMAP_YES;
@@ -720,8 +726,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
// entry with a matching 'to' part. This was done to allow ":ab foo bar"
// to be unmapped by typing ":unab foo", where "foo" will be replaced by
// "bar" because of the abbreviation.
- for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
- && !did_it && !got_int; round++) {
+ const int num_rounds = maptype == MAPTYPE_UNMAP && !unmap_lhs_only ? 2 : 1;
+ for (int round = 0; round < num_rounds && !did_it && !got_int; round++) {
int hash_start, hash_end;
if ((round == 0 && has_lhs) || is_abbrev) {
// just use one hash
@@ -935,9 +941,11 @@ theend:
/// for :cabbr mode is MODE_CMDLINE
/// ```
///
-/// @param maptype MAPTYPE_MAP for |:map|
-/// MAPTYPE_UNMAP for |:unmap|
-/// MAPTYPE_NOREMAP for |:noremap|.
+/// @param maptype MAPTYPE_MAP for |:map| or |:abbr|
+/// MAPTYPE_UNMAP for |:unmap| or |:unabbr|
+/// MAPTYPE_NOREMAP for |:noremap| or |:noreabbr|
+/// MAPTYPE_UNMAP_LHS is like MAPTYPE_UNMAP, but doesn't try to match
+/// with {rhs} if there is no match with {lhs}.
/// @param arg C-string containing the arguments of the map/abbrev
/// command, i.e. everything except the initial `:[X][nore]map`.
/// - Cannot be a read-only string; it will be modified.
@@ -2348,7 +2356,7 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
MapArguments unmap_args = MAP_ARGUMENTS_INIT;
set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, p_cpo, &unmap_args);
unmap_args.buffer = buffer;
- buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, is_abbr, curbuf);
+ buf_do_map(MAPTYPE_UNMAP_LHS, &unmap_args, mode, is_abbr, curbuf);
xfree(unmap_args.rhs);
xfree(unmap_args.orig_rhs);
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index b82117ea86..fc120b683c 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -19,9 +19,10 @@
/// Used for the first argument of do_map()
enum {
- MAPTYPE_MAP = 0,
- MAPTYPE_UNMAP = 1,
- MAPTYPE_NOREMAP = 2,
+ MAPTYPE_MAP = 0,
+ MAPTYPE_UNMAP = 1,
+ MAPTYPE_NOREMAP = 2,
+ MAPTYPE_UNMAP_LHS = 3,
};
/// Adjust chars in a language according to 'langmap' option.
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 5f0115b46c..65f03ca77f 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -176,7 +176,7 @@ static int p_paste_dep_opts[] = {
void set_init_tablocal(void)
{
// susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal!
- p_ch = options[kOptCmdheight].def_val.number;
+ p_ch = options[kOptCmdheight].def_val.data.number;
}
/// Initialize the 'shell' option to a default value.
@@ -291,8 +291,8 @@ static void set_init_default_cdpath(void)
}
}
buf[j] = NUL;
- options[kOptCdpath].def_val.string = buf;
- options[kOptCdpath].flags |= P_DEF_ALLOCED;
+ change_option_default(kOptCdpath, CSTR_AS_OPTVAL(buf));
+
xfree(cdpath);
}
@@ -301,29 +301,22 @@ static void set_init_default_cdpath(void)
/// only happen for non-indirect options.
/// Also set the default to the expanded value, so ":set" does not list
/// them.
-/// Don't set the P_ALLOCED flag, because we don't want to free the
-/// default.
static void set_init_expand_env(void)
{
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
vimoption_T *opt = &options[opt_idx];
- if (opt->flags & P_NO_DEF_EXP) {
+ if (opt->flags & kOptFlagNoDefExp) {
continue;
}
char *p;
- if ((opt->flags & P_GETTEXT) && opt->var != NULL) {
+ if ((opt->flags & kOptFlagGettext) && opt->var != NULL) {
p = _(*(char **)opt->var);
} else {
p = option_expand(opt_idx, NULL);
}
if (p != NULL) {
- p = xstrdup(p);
- *(char **)opt->var = p;
- if (opt->flags & P_DEF_ALLOCED) {
- xfree(opt->def_val.string);
- }
- opt->def_val.string = p;
- opt->flags |= P_DEF_ALLOCED;
+ set_option_varp(opt_idx, opt->var, CSTR_TO_OPTVAL(p), true);
+ change_option_default(opt_idx, CSTR_TO_OPTVAL(p));
}
}
}
@@ -354,6 +347,9 @@ void set_init_1(bool clean_arg)
{
langmap_init();
+ // Allocate the default option values.
+ alloc_options_default();
+
set_init_default_shell();
set_init_default_backupskip();
set_init_default_cdpath();
@@ -430,71 +426,73 @@ void set_init_1(bool clean_arg)
set_helplang_default(get_mess_lang());
}
-/// Set an option to its default value.
-/// This does not take care of side effects!
+/// Get default value for option, based on the option's type and scope.
///
-/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL.
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags.
///
-/// TODO(famiu): Refactor this when def_val uses OptVal.
-static void set_option_default(const OptIndex opt_idx, int opt_flags)
+/// @return Default value of option for the scope specified in opt_flags.
+static OptVal get_option_default(const OptIndex opt_idx, int opt_flags)
{
- bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
-
- // pointer to variable for current option
vimoption_T *opt = &options[opt_idx];
- void *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags);
- uint32_t flags = opt->flags;
- if (varp != NULL) { // skip hidden option, nothing to do for it
- if (option_has_type(opt_idx, kOptValTypeString)) {
- // Use set_option_direct() for local options to handle freeing and allocating the value.
- if (opt->indir != PV_NONE) {
- set_option_direct(opt_idx, CSTR_AS_OPTVAL(opt->def_val.string), opt_flags, 0);
- } else {
- if (flags & P_ALLOCED) {
- free_string_option(*(char **)(varp));
- }
- *(char **)varp = opt->def_val.string;
- opt->flags &= ~P_ALLOCED;
- }
- } else if (option_has_type(opt_idx, kOptValTypeNumber)) {
- if (opt->indir == PV_SCROLL) {
- win_comp_scroll(curwin);
- } else {
- OptInt def_val = opt->def_val.number;
- if ((OptInt *)varp == &curwin->w_p_so
- || (OptInt *)varp == &curwin->w_p_siso) {
- // 'scrolloff' and 'sidescrolloff' local values have a
- // different default value than the global default.
- *(OptInt *)varp = -1;
- } else {
- *(OptInt *)varp = def_val;
- }
- // May also set global value for local option.
- if (both) {
- *(OptInt *)get_varp_scope(opt, OPT_GLOBAL) = def_val;
- }
- }
- } else { // boolean
- *(int *)varp = opt->def_val.boolean;
+ bool is_global_local_option = opt->indir & PV_BOTH;
+
#ifdef UNIX
- // 'modeline' defaults to off for root
- if (opt->indir == PV_ML && getuid() == ROOT_UID) {
- *(int *)varp = false;
- }
+ if (opt_idx == kOptModeline && getuid() == ROOT_UID) {
+ // 'modeline' defaults to off for root.
+ return BOOLEAN_OPTVAL(false);
+ }
#endif
- // May also set global value for local option.
- if (both) {
- *(int *)get_varp_scope(opt, OPT_GLOBAL) =
- *(int *)varp;
- }
- }
- // The default value is not insecure.
- uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
- *flagsp = *flagsp & ~P_INSECURE;
+ if ((opt_flags & OPT_LOCAL) && is_global_local_option) {
+ // Use unset local value instead of default value for local scope of global-local options.
+ return get_option_unset_value(opt_idx);
+ } else if (option_has_type(opt_idx, kOptValTypeString) && !(opt->flags & kOptFlagNoDefExp)) {
+ // For string options, expand environment variables and ~ since the default value was already
+ // expanded, only required when an environment variable was set later.
+ char *s = option_expand(opt_idx, opt->def_val.data.string.data);
+ return s == NULL ? opt->def_val : CSTR_AS_OPTVAL(s);
+ } else {
+ return opt->def_val;
}
+}
- set_option_sctx(opt_idx, opt_flags, current_sctx);
+/// Allocate the default values for all options by copying them from the stack.
+/// This ensures that we don't need to always check if the option default is allocated or not.
+static void alloc_options_default(void)
+{
+ for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ options[opt_idx].def_val = optval_copy(options[opt_idx].def_val);
+ }
+}
+
+/// Change the default value for an option.
+///
+/// @param opt_idx Option index in options[] table.
+/// @param value New default value. Must be allocated.
+static void change_option_default(const OptIndex opt_idx, OptVal value)
+{
+ optval_free(options[opt_idx].def_val);
+ options[opt_idx].def_val = value;
+}
+
+/// Set an option to its default value.
+/// This does not take care of side effects!
+///
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags.
+static void set_option_default(const OptIndex opt_idx, int opt_flags)
+{
+ OptVal def_val = get_option_default(opt_idx, opt_flags);
+ set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid);
+
+ if (opt_idx == kOptScroll) {
+ win_comp_scroll(curwin);
+ }
+
+ // The default value is not insecure.
+ uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
+ *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure;
}
/// Set all options (except terminal options) to their default value.
@@ -503,7 +501,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags)
static void set_options_default(int opt_flags)
{
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
- if (!(options[opt_idx].flags & P_NODEFAULT)) {
+ if (!(options[opt_idx].flags & kOptFlagNoDefault)) {
set_option_default(opt_idx, opt_flags);
}
}
@@ -522,20 +520,13 @@ static void set_options_default(int opt_flags)
/// @param opt_idx Option index in options[] table.
/// @param val The value of the option.
/// @param allocated If true, do not copy default as it was already allocated.
+///
+/// TODO(famiu): Remove this.
static void set_string_default(OptIndex opt_idx, char *val, bool allocated)
FUNC_ATTR_NONNULL_ALL
{
- if (opt_idx == kOptInvalid) {
- return;
- }
-
- vimoption_T *opt = &options[opt_idx];
- if (opt->flags & P_DEF_ALLOCED) {
- xfree(opt->def_val.string);
- }
-
- opt->def_val.string = allocated ? val : xstrdup(val);
- opt->flags |= P_DEF_ALLOCED;
+ assert(opt_idx != kOptInvalid);
+ change_option_default(opt_idx, CSTR_AS_OPTVAL(allocated ? val : xstrdup(val)));
}
/// For an option value that contains comma separated items, find "newval" in
@@ -551,9 +542,9 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
int bs = 0;
for (char *s = origval; *s != NUL; s++) {
- if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1)))
+ if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1)))
&& strncmp(s, newval, newvallen) == 0
- && (!(flags & P_COMMA) || s[newvallen] == ',' || s[newvallen] == NUL)) {
+ && (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) {
return s;
}
// Count backslashes. Only a comma with an even number of backslashes
@@ -569,15 +560,6 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
return NULL;
}
-/// Set the Vi-default value of a number option.
-/// Used for 'lines' and 'columns'.
-void set_number_default(OptIndex opt_idx, OptInt val)
-{
- if (opt_idx != kOptInvalid) {
- options[opt_idx].def_val.number = val;
- }
-}
-
#if defined(EXITFREE)
/// Free all options.
void free_all_options(void)
@@ -585,16 +567,14 @@ void free_all_options(void)
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
if (options[opt_idx].indir == PV_NONE) {
// global option: free value and default value.
- if ((options[opt_idx].flags & P_ALLOCED) && options[opt_idx].var != NULL) {
+ if (options[opt_idx].var != NULL) {
optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
}
- if (options[opt_idx].flags & P_DEF_ALLOCED) {
- optval_free(optval_from_varp(opt_idx, &options[opt_idx].def_val));
- }
} else if (options[opt_idx].var != VAR_WIN) {
// 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();
@@ -612,7 +592,7 @@ void set_init_2(bool headless)
// 'scroll' defaults to half the window height. The stored default is zero,
// which results in the actual value computed from the window height.
- if (!(options[kOptScroll].flags & P_WAS_SET)) {
+ if (!(options[kOptScroll].flags & kOptFlagWasSet)) {
set_option_default(kOptScroll, OPT_LOCAL);
}
comp_col();
@@ -622,7 +602,7 @@ void set_init_2(bool headless)
if (!option_was_set(kOptWindow)) {
p_window = Rows - 1;
}
- set_number_default(kOptWindow, Rows - 1);
+ change_option_default(kOptWindow, NUMBER_OPTVAL(Rows - 1));
}
/// Initialize the options, part three: After reading the .vimrc
@@ -633,56 +613,42 @@ void set_init_3(void)
// Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
// This is done after other initializations, where 'shell' might have been
// set, but only if they have not been set before.
- bool do_srr = !(options[kOptShellredir].flags & P_WAS_SET);
- bool do_sp = !(options[kOptShellpipe].flags & P_WAS_SET);
+ bool do_srr = !(options[kOptShellredir].flags & kOptFlagWasSet);
+ bool do_sp = !(options[kOptShellpipe].flags & kOptFlagWasSet);
size_t len = 0;
char *p = (char *)invocation_path_tail(p_sh, &len);
p = xmemdupz(p, len);
- {
- //
- // Default for p_sp is "| tee", for p_srr is ">".
- // For known shells it is changed here to include stderr.
- //
- if (path_fnamecmp(p, "csh") == 0
- || path_fnamecmp(p, "tcsh") == 0) {
- if (do_sp) {
- p_sp = "|& tee";
- options[kOptShellpipe].def_val.string = p_sp;
- }
- if (do_srr) {
- p_srr = ">&";
- options[kOptShellredir].def_val.string = p_srr;
- }
- } else if (path_fnamecmp(p, "sh") == 0
- || path_fnamecmp(p, "ksh") == 0
- || path_fnamecmp(p, "mksh") == 0
- || path_fnamecmp(p, "pdksh") == 0
- || path_fnamecmp(p, "zsh") == 0
- || path_fnamecmp(p, "zsh-beta") == 0
- || path_fnamecmp(p, "bash") == 0
- || path_fnamecmp(p, "fish") == 0
- || path_fnamecmp(p, "ash") == 0
- || path_fnamecmp(p, "dash") == 0) {
- // Always use POSIX shell style redirection if we reach this
- if (do_sp) {
- p_sp = "2>&1| tee";
- options[kOptShellpipe].def_val.string = p_sp;
- }
- if (do_srr) {
- p_srr = ">%s 2>&1";
- options[kOptShellredir].def_val.string = p_srr;
- }
+ bool is_csh = path_fnamecmp(p, "csh") == 0 || path_fnamecmp(p, "tcsh") == 0;
+ bool is_known_shell = path_fnamecmp(p, "sh") == 0 || path_fnamecmp(p, "ksh") == 0
+ || path_fnamecmp(p, "mksh") == 0 || path_fnamecmp(p, "pdksh") == 0
+ || path_fnamecmp(p, "zsh") == 0 || path_fnamecmp(p, "zsh-beta") == 0
+ || path_fnamecmp(p, "bash") == 0 || path_fnamecmp(p, "fish") == 0
+ || path_fnamecmp(p, "ash") == 0 || path_fnamecmp(p, "dash") == 0;
+
+ // Default for p_sp is "| tee", for p_srr is ">".
+ // For known shells it is changed here to include stderr.
+ if (is_csh || is_known_shell) {
+ if (do_sp) {
+ const OptVal sp =
+ is_csh ? STATIC_CSTR_AS_OPTVAL("|& tee") : STATIC_CSTR_AS_OPTVAL("2>&1| tee");
+ set_option_direct(kOptShellpipe, sp, 0, SID_NONE);
+ change_option_default(kOptShellpipe, optval_copy(sp));
+ }
+ if (do_srr) {
+ const OptVal srr = is_csh ? STATIC_CSTR_AS_OPTVAL(">&") : STATIC_CSTR_AS_OPTVAL(">%s 2>&1");
+ set_option_direct(kOptShellredir, srr, 0, SID_NONE);
+ change_option_default(kOptShellredir, optval_copy(srr));
}
- xfree(p);
}
+ 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 & P_WAS_SET)) {
+ if (idx_ffs >= 0 && (options[idx_ffs].flags & kOptFlagWasSet)) {
set_fileformat(default_fileformat(), OPT_LOCAL);
}
}
@@ -702,13 +668,11 @@ void set_helplang_default(const char *lang)
if (lang_len < 2) { // safety check
return;
}
- if (options[kOptHelplang].flags & P_WAS_SET) {
+ if (options[kOptHelplang].flags & kOptFlagWasSet) {
return;
}
- if (options[kOptHelplang].flags & P_ALLOCED) {
- free_string_option(p_hlg);
- }
+ free_string_option(p_hlg);
p_hlg = xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
if (STRNICMP(p_hlg, "zh_", 3) == 0 && lang_len >= 5) {
@@ -720,7 +684,6 @@ void set_helplang_default(const char *lang)
p_hlg[1] = 'n';
}
p_hlg[2] = NUL;
- options[kOptHelplang].flags |= P_ALLOCED;
}
/// 'title' and 'icon' only default to true if they have not been set or reset
@@ -733,13 +696,13 @@ void set_title_defaults(void)
// If GUI is (going to be) used, we can always set the window title and
// icon name. Saves a bit of time, because the X11 display server does
// not need to be contacted.
- if (!(options[kOptTitle].flags & P_WAS_SET)) {
- options[kOptTitle].def_val.boolean = false;
- p_title = false;
+ if (!(options[kOptTitle].flags & kOptFlagWasSet)) {
+ change_option_default(kOptTitle, BOOLEAN_OPTVAL(false));
+ p_title = 0;
}
- if (!(options[kOptIcon].flags & P_WAS_SET)) {
- options[kOptIcon].def_val.boolean = false;
- p_icon = false;
+ if (!(options[kOptIcon].flags & kOptFlagWasSet)) {
+ change_option_default(kOptIcon, BOOLEAN_OPTVAL(false));
+ p_icon = 0;
}
}
@@ -758,27 +721,6 @@ void ex_set(exarg_T *eap)
do_set(eap->arg, flags);
}
-/// Get the default value for a string option.
-static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags)
-{
- char *newval = options[opt_idx].def_val.string;
- // expand environment variables and ~ since the default value was
- // already expanded, only required when an environment variable was set
- // later
- if (newval == NULL) {
- newval = empty_string_option;
- } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
- char *s = option_expand(opt_idx, newval);
- if (s == NULL) {
- s = newval;
- }
- newval = xstrdup(s);
- } else {
- newval = xstrdup(newval);
- }
- return newval;
-}
-
/// Copy the new string value into allocated memory for the option.
/// Can't use set_option_direct(), because we need to remove the backslashes.
static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
@@ -802,7 +744,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
while (*arg != NUL && !ascii_iswhite(*arg)) {
if (*arg == '\\' && arg[1] != NUL
#ifdef BACKSLASH_IN_FILENAME
- && !((flags & P_EXPAND)
+ && !((flags & kOptFlagExpand)
&& vim_isfilec((uint8_t)arg[1])
&& !ascii_iswhite(arg[1])
&& (arg[1] != '\\'
@@ -851,12 +793,12 @@ static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval,
static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, uint32_t flags)
{
int len = 0;
- int comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL);
+ int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL);
if (op == OP_ADDING) {
len = (int)strlen(origval);
// Strip a trailing comma, would get 2.
if (comma && len > 1
- && (flags & P_ONECOMMA) == P_ONECOMMA
+ && (flags & kOptFlagOneComma) == kOptFlagOneComma
&& origval[len - 1] == ','
&& origval[len - 2] != '\\') {
len--;
@@ -881,7 +823,7 @@ static void stropt_remove_val(char *origval, char *newval, uint32_t flags, char
STRCPY(newval, origval);
if (*strval) {
// may need to remove a comma
- if (flags & P_COMMA) {
+ if (flags & kOptFlagComma) {
if (strval == origval) {
// include comma after string
if (strval[len] == ',') {
@@ -903,8 +845,8 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
char *s = newval;
// Remove flags that appear twice.
for (s = newval; *s;) {
- // if options have P_FLAGLIST and P_ONECOMMA such as 'whichwrap'
- if (flags & P_ONECOMMA) {
+ // if options have kOptFlagFlagList and kOptFlagOneComma such as 'whichwrap'
+ if (flags & kOptFlagOneComma) {
if (*s != ',' && *(s + 1) == ','
&& vim_strchr(s + 2, (uint8_t)(*s)) != NULL) {
// Remove the duplicated value and the next comma.
@@ -912,7 +854,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
continue;
}
} else {
- if ((!(flags & P_COMMA) || *s != ',')
+ if ((!(flags & kOptFlagComma) || *s != ',')
&& vim_strchr(s + 1, (uint8_t)(*s)) != NULL) {
STRMOVE(s, s + 1);
continue;
@@ -922,10 +864,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
}
}
-/// Get the string value specified for a ":set" command. The following set
-/// options are supported:
-/// set {opt}&
-/// set {opt}<
+/// Get the string value specified for a ":set" command. The following set options are supported:
/// set {opt}={val}
/// set {opt}:{val}
static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp,
@@ -936,61 +875,56 @@ static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void
char *save_arg = NULL;
char *newval;
char *s = NULL;
- if (nextchar == '&') { // set to default val
- newval = stropt_get_default_val(opt_idx, flags);
- } else if (nextchar == '<') { // set to global val
- newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
- } else {
- arg++; // jump to after the '=' or ':'
- // Set 'keywordprg' to ":help" if an empty
- // value was passed to :set by the user.
- if (varp == &p_kp && (*arg == NUL || *arg == ' ')) {
- save_arg = arg;
- arg = ":help";
- }
+ arg++; // jump to after the '=' or ':'
- // Copy the new string into allocated memory.
- newval = stropt_copy_value(origval, &arg, op, flags);
+ // Set 'keywordprg' to ":help" if an empty
+ // value was passed to :set by the user.
+ if (varp == &p_kp && (*arg == NUL || *arg == ' ')) {
+ save_arg = arg;
+ arg = ":help";
+ }
- // Expand environment variables and ~.
- // Don't do it when adding without inserting a comma.
- if (op == OP_NONE || (flags & P_COMMA)) {
- newval = stropt_expand_envvar(opt_idx, origval, newval, op);
- }
+ // Copy the new string into allocated memory.
+ newval = stropt_copy_value(origval, &arg, op, flags);
- // locate newval[] in origval[] when removing it
- // and when adding to avoid duplicates
- int len = 0;
- if (op == OP_REMOVING || (flags & P_NODUP)) {
- len = (int)strlen(newval);
- s = find_dup_item(origval, newval, (size_t)len, flags);
+ // Expand environment variables and ~.
+ // Don't do it when adding without inserting a comma.
+ if (op == OP_NONE || (flags & kOptFlagComma)) {
+ newval = stropt_expand_envvar(opt_idx, origval, newval, op);
+ }
- // do not add if already there
- if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
- op = OP_NONE;
- STRCPY(newval, origval);
- }
+ // locate newval[] in origval[] when removing it
+ // and when adding to avoid duplicates
+ int len = 0;
+ if (op == OP_REMOVING || (flags & kOptFlagNoDup)) {
+ len = (int)strlen(newval);
+ s = find_dup_item(origval, newval, (size_t)len, flags);
- // if no duplicate, move pointer to end of original value
- if (s == NULL) {
- s = origval + (int)strlen(origval);
- }
+ // do not add if already there
+ if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
+ op = OP_NONE;
+ STRCPY(newval, origval);
}
- // concatenate the two strings; add a ',' if needed
- if (op == OP_ADDING || op == OP_PREPENDING) {
- stropt_concat_with_comma(origval, newval, op, flags);
- } else if (op == OP_REMOVING) {
- // Remove newval[] from origval[]. (Note: "len" has been set above
- // and is used here).
- stropt_remove_val(origval, newval, flags, s, len);
+ // if no duplicate, move pointer to end of original value
+ if (s == NULL) {
+ s = origval + (int)strlen(origval);
}
+ }
- if (flags & P_FLAGLIST) {
- // Remove flags that appear twice.
- stropt_remove_dupflags(newval, flags);
- }
+ // concatenate the two strings; add a ',' if needed
+ if (op == OP_ADDING || op == OP_PREPENDING) {
+ stropt_concat_with_comma(origval, newval, op, flags);
+ } else if (op == OP_REMOVING) {
+ // Remove newval[] from origval[]. (Note: "len" has been set above
+ // and is used here).
+ stropt_remove_val(origval, newval, flags, s, len);
+ }
+
+ if (flags & kOptFlagFlagList) {
+ // Remove flags that appear twice.
+ stropt_remove_dupflags(newval, flags);
}
if (save_arg != NULL) {
@@ -1052,11 +986,11 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_
// Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
- if (flags & (P_SECURE | P_NO_ML)) {
+ if (flags & (kOptFlagSecure | kOptFlagNoML)) {
*errmsg = e_not_allowed_in_modeline;
return FAIL;
}
- if ((flags & P_MLE) && !p_mle) {
+ if ((flags & kOptFlagMLE) && !p_mle) {
*errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
return FAIL;
}
@@ -1072,7 +1006,7 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_
}
// Disallow changing some options in the sandbox
- if (sandbox != 0 && (flags & P_SECURE)) {
+ if (sandbox != 0 && (flags & kOptFlagSecure)) {
*errmsg = e_sandbox;
return FAIL;
}
@@ -1152,6 +1086,7 @@ const char *find_option_end(const char *arg, OptIndex *opt_idxp)
}
/// Get new option value from argp. Allocated OptVal must be freed by caller.
+/// Can unset local value of an option when ":set {option}<" is used.
static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp,
int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
const size_t errbuflen, const char **errmsg)
@@ -1166,6 +1101,20 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp);
OptVal newval = NIL_OPTVAL;
+ if (nextchar == '&') {
+ // ":set opt&": Reset to default value.
+ // NOTE: Use OPT_GLOBAL instead of opt_flags to ensure we don't use the unset local value for
+ // global-local options when OPT_LOCAL is used.
+ return optval_copy(get_option_default(opt_idx, OPT_GLOBAL));
+ } else if (nextchar == '<') {
+ // ":set opt<": Reset to global value.
+ // ":setlocal opt<": Copy global value to local value.
+ if (option_is_global_local(opt_idx) && !(opt_flags & OPT_LOCAL)) {
+ unset_option_local_value(opt_idx);
+ }
+ return get_option_value(opt_idx, OPT_GLOBAL);
+ }
+
switch (oldval.type) {
case kOptValTypeNil:
abort();
@@ -1173,8 +1122,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
TriState newval_bool;
// ":set opt!": invert
- // ":set opt&": reset to default value
- // ":set opt<": reset to global value
if (nextchar == '!') {
switch (oldval.data.boolean) {
case kNone:
@@ -1187,15 +1134,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
newval_bool = kTrue;
break;
}
- } else if (nextchar == '&') {
- newval_bool = TRISTATE_FROM_INT(options[opt_idx].def_val.boolean);
- } else if (nextchar == '<') {
- // For 'autoread', kNone means to use global value.
- if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) {
- newval_bool = kNone;
- } else {
- newval_bool = TRISTATE_FROM_INT(*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
- }
} else {
// ":set invopt": invert
// ":set opt" or ":set noopt": set or reset
@@ -1214,31 +1152,15 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
OptInt newval_num;
// Different ways to set a number option:
- // & set to default value
- // < set to global value
// <xx> accept special key codes for 'wildchar' or 'wildcharm'
// ^x accept ctrl key codes for 'wildchar' or 'wildcharm'
// c accept any non-digit for 'wildchar' or 'wildcharm'
// [-]0-9 set number
// other error
arg++;
- if (nextchar == '&') {
- newval_num = options[opt_idx].def_val.number;
- } else if (nextchar == '<') {
- if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
- // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global newval_num
- newval_num = NO_LOCAL_UNDOLEVEL;
- } else if (opt_flags == OPT_LOCAL
- && ((OptInt *)varp == &curwin->w_p_siso || (OptInt *)varp == &curwin->w_p_so)) {
- // for 'scrolloff'/'sidescrolloff' -1 means using the global newval_num
- newval_num = -1;
- } else {
- newval_num = *(OptInt *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
- } else if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
- && (*arg == '<' || *arg == '^'
- || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
- && !ascii_isdigit(*arg)))) {
+ if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
+ && (*arg == '<' || *arg == '^'
+ || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1])) && !ascii_isdigit(*arg)))) {
newval_num = string_to_key(arg);
if (newval_num == 0) {
*errmsg = e_invarg;
@@ -1664,7 +1586,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 & P_EXPAND) || options[opt_idx].var == NULL) {
+ if (!(options[opt_idx].flags & kOptFlagExpand) || options[opt_idx].var == NULL) {
return NULL;
}
@@ -1755,10 +1677,10 @@ int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags)
assert(opt_idx != kOptInvalid);
uint32_t *flagp = insecure_flag(wp, opt_idx, opt_flags);
- return (*flagp & P_INSECURE) != 0;
+ return (*flagp & kOptFlagInsecure) != 0;
}
-/// Get a pointer to the flags used for the P_INSECURE flag of option
+/// Get a pointer to the flags used for the kOptFlagInsecure flag of option
/// "opt_idx". For some local options a local flags field is used.
/// NOTE: Caller must make sure that "wp" is set to the window from which
/// the option is used.
@@ -3054,25 +2976,25 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new
/// Called after an option changed: check if something needs to be redrawn.
void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags)
{
- // Careful: P_RALL is a combination of other P_ flags
- bool all = (flags & P_RALL) == P_RALL;
+ // Careful: kOptFlagRedrAll is a combination of other redraw flags
+ bool all = (flags & kOptFlagRedrAll) == kOptFlagRedrAll;
- if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty
+ if ((flags & kOptFlagRedrStat) || all) { // mark all status lines and window bars dirty
status_redraw_all();
}
- if ((flags & P_RTABL) || all) { // mark tablines dirty
+ if ((flags & kOptFlagRedrTabl) || all) { // mark tablines dirty
redraw_tabline = true;
}
- if ((flags & P_RBUF) || (flags & P_RWIN) || all) {
- if (flags & P_HLONLY) {
+ if ((flags & kOptFlagRedrBuf) || (flags & kOptFlagRedrWin) || all) {
+ if (flags & kOptFlagHLOnly) {
redraw_later(win, UPD_NOT_VALID);
} else {
changed_window_setting(win);
}
}
- if (flags & P_RBUF) {
+ if (flags & kOptFlagRedrBuf) {
redraw_buf_later(buf, UPD_NOT_VALID);
}
if (all) {
@@ -3218,6 +3140,19 @@ bool optval_equal(OptVal o1, OptVal o2)
UNREACHABLE;
}
+/// Get type of option. Does not support multitype options.
+static OptValType option_get_type(const OptIndex opt_idx)
+{
+ assert(!option_is_multitype(opt_idx));
+
+ // If the option only supports a single type, it means that the index of the option's type flag
+ // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
+ // use that as the option's type.
+ OptValType type = xctz(options[opt_idx].type_flags);
+ assert(type > kOptValTypeNil && type < kOptValTypeSize);
+ return type;
+}
+
/// Create OptVal from var pointer.
///
/// @param opt_idx Option index in options[] table.
@@ -3237,11 +3172,7 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp;
}
- // If the option only supports a single type, it means that the index of the option's type flag
- // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
- // use that as the option's type.
- OptValType type = xctz(options[opt_idx].type_flags);
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
+ OptValType type = option_get_type(opt_idx);
switch (type) {
case kOptValTypeNil:
@@ -3390,6 +3321,11 @@ bool is_option_hidden(OptIndex opt_idx)
return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL;
}
+static inline bool option_is_global_local(OptIndex opt_idx)
+{
+ return opt_idx == kOptInvalid ? false : (options[opt_idx].indir & PV_BOTH);
+}
+
/// Get option flags.
///
/// @param opt_idx Option index in options[] table.
@@ -3504,7 +3440,6 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
vimoption_T *opt = &options[opt_idx];
const char *errmsg = NULL;
bool restore_chartab = false;
- bool free_oldval = (opt->flags & P_ALLOCED);
bool value_changed = false;
bool value_checked = false;
@@ -3531,7 +3466,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
errmsg = e_unsupportedoption;
}
// Disallow changing some options from secure mode.
- else if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) {
+ else if ((secure || sandbox != 0) && (opt->flags & kOptFlagSecure)) {
errmsg = e_secure;
}
// Check for a "normal" directory or file name in some string options.
@@ -3580,18 +3515,12 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
set_option_sctx(opt_idx, opt_flags, script_ctx);
}
- // Free options that are in allocated memory.
- // Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()).
- if (free_oldval) {
- optval_free(old_value);
- }
- opt->flags |= P_ALLOCED;
+ optval_free(old_value);
const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- const bool opt_is_global_local = opt->indir & PV_BOTH;
if (scope_both) {
- if (opt_is_global_local) {
+ if (option_is_global_local(opt_idx)) {
// Global option with local value set to use global value.
// Free the local value and clear it.
void *varp_local = get_varp_scope(opt, OPT_LOCAL);
@@ -3638,7 +3567,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
}
if (curwin->w_curswant != MAXCOL
- && (opt->flags & (P_CURSWANT | P_RALL)) != 0 && (opt->flags & P_HLONLY) == 0) {
+ && (opt->flags & (kOptFlagCurswant | kOptFlagRedrAll)) != 0
+ && (opt->flags & kOptFlagHLOnly) == 0) {
curwin->w_set_curswant = true;
}
@@ -3646,14 +3576,14 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
if (errmsg == NULL) {
uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- opt->flags |= P_WAS_SET;
+ opt->flags |= kOptFlagWasSet;
- // When an option is set in the sandbox, from a modeline or in secure mode set the P_INSECURE
+ // When an option is set in the sandbox, from a modeline or in secure mode set the kOptFlagInsecure
// flag. Otherwise, if a new value is stored reset the flag.
if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) {
- *p |= P_INSECURE;
+ *p |= kOptFlagInsecure;
} else if (value_replaced) {
- *p &= ~P_INSECURE;
+ *p &= ~(unsigned)kOptFlagInsecure;
}
}
@@ -3678,7 +3608,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
if (opt_flags == OPT_GLOBAL) {
errmsg = _("Cannot unset global option value");
} else {
- optval_free(*newval);
*newval = optval_copy(get_option_unset_value(opt_idx));
}
} else if (!option_has_type(opt_idx, newval->type)) {
@@ -3719,25 +3648,28 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
{
assert(opt_idx != kOptInvalid);
- const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
+ const char *errmsg = NULL;
- if (errmsg != NULL) {
- optval_free(value);
- return errmsg;
+ if (!direct) {
+ errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
+
+ if (errmsg != NULL) {
+ optval_free(value);
+ return errmsg;
+ }
}
vimoption_T *opt = &options[opt_idx];
const bool scope_local = opt_flags & OPT_LOCAL;
const bool scope_global = opt_flags & OPT_GLOBAL;
const bool scope_both = !scope_local && !scope_global;
- const bool opt_is_global_local = opt->indir & PV_BOTH;
// Whether local value of global-local option is unset.
- // NOTE: When this is true, it also implies that opt_is_global_local is true.
+ // NOTE: When this is true, it also implies that the option is global-local.
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 && opt_is_global_local) {
+ if (scope_both && option_is_global_local(opt_idx)) {
varp = opt->var;
}
@@ -3769,9 +3701,9 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
const int secure_saved = secure;
// When an option is set in the sandbox, from a modeline or in secure mode, then deal with side
- // effects in secure mode. Also when the value was set with the P_INSECURE flag and is not
+ // effects in secure mode. Also when the value was set with the kOptFlagInsecure flag and is not
// completely replaced.
- if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & P_INSECURE))) {
+ if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & kOptFlagInsecure))) {
secure = 1;
}
@@ -3788,7 +3720,7 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value,
saved_old_local_value, saved_new_value, errmsg);
}
- if (opt->flags & P_UI_OPTION) {
+ if (opt->flags & kOptFlagUIOption) {
ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value));
}
}
@@ -3823,8 +3755,10 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags);
- set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true, errbuf,
- sizeof(errbuf));
+ const char *errmsg = set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true,
+ errbuf, sizeof(errbuf));
+ assert(errmsg == NULL);
+ (void)errmsg; // ignore unused warning
}
/// Set option value directly for buffer / window, without processing any side effects.
@@ -3879,7 +3813,7 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt
uint32_t flags = options[opt_idx].flags;
// Disallow changing some options in the sandbox
- if (sandbox > 0 && (flags & P_SECURE)) {
+ if (sandbox > 0 && (flags & kOptFlagSecure)) {
return _(e_sandbox);
}
@@ -3893,6 +3827,17 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt
sizeof(errbuf));
}
+/// Unset the local value of a global-local option.
+///
+/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
+///
+/// @return NULL on success, an untranslated error message on error.
+static inline const char *unset_option_local_value(const OptIndex opt_idx)
+{
+ assert(option_is_global_local(opt_idx));
+ return set_option_value(opt_idx, get_option_unset_value(opt_idx), OPT_LOCAL);
+}
+
/// Set the value of an option. Supports TTY options, unlike set_option_value().
///
/// @param name Option name. Used for error messages and for setting TTY options.
@@ -4106,7 +4051,7 @@ OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *fr
/// Get option value for buffer / window.
///
/// @param opt_idx Option index in options[] table.
-/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL).
+/// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL).
/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param[out] hidden Whether option is hidden.
/// @param req_scope Requested option scope. See OptReqScope in option.h.
@@ -4273,7 +4218,7 @@ static int optval_default(OptIndex opt_idx, void *varp)
}
OptVal current_val = optval_from_varp(opt_idx, varp);
- OptVal default_val = optval_from_varp(opt_idx, &opt->def_val);
+ OptVal default_val = opt->def_val;
return optval_equal(current_val, default_val);
}
@@ -4283,7 +4228,7 @@ void ui_refresh_options(void)
{
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
uint32_t flags = options[opt_idx].flags;
- if (!(flags & P_UI_OPTION)) {
+ if (!(flags & kOptFlagUIOption)) {
continue;
}
String name = cstr_as_string(options[opt_idx].fullname);
@@ -4358,14 +4303,14 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// - Hidden options.
//
// Do the loop over "options[]" twice: once for options with the
- // P_PRI_MKRC flag and once without.
+ // kOptFlagPriMkrc flag and once without.
for (int pri = 1; pri >= 0; pri--) {
vimoption_T *opt;
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
opt = &options[opt_idx];
- if (!(opt->flags & P_NO_MKRC)
- && ((pri == 1) == ((opt->flags & P_PRI_MKRC) != 0))) {
+ if (!(opt->flags & kOptFlagNoMkrc)
+ && ((pri == 1) == ((opt->flags & kOptFlagPriMkrc) != 0))) {
// skip global option when only doing locals
if (opt->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) {
continue;
@@ -4373,7 +4318,7 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// Do not store options like 'bufhidden' and 'syntax' in a vimrc
// file, they are always buffer-specific.
- if ((opt_flags & OPT_GLOBAL) && (opt->flags & P_NOGLOB)) {
+ if ((opt_flags & OPT_GLOBAL) && (opt->flags & kOptFlagNoGlob)) {
continue;
}
@@ -4488,7 +4433,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_
char *part = NULL;
if (*valuep != NULL) {
- if ((flags & P_EXPAND) != 0) {
+ if ((flags & kOptFlagExpand) != 0) {
size_t size = (size_t)strlen(*valuep) + 1;
// replace home directory in the whole option value into "buf"
@@ -4498,7 +4443,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_
// If the option value is longer than MAXPATHL, we need to append
// each comma separated part of the option separately, so that it
// can be expanded when read back.
- if (size >= MAXPATHL && (flags & P_COMMA) != 0
+ if (size >= MAXPATHL && (flags & kOptFlagComma) != 0
&& vim_strchr(*valuep, ',') != NULL) {
part = xmalloc(size);
@@ -4585,6 +4530,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_EFM:
return &(buf->b_p_efm);
case PV_GP:
@@ -4706,6 +4653,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_EFM:
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
case PV_GP:
@@ -4977,6 +4926,15 @@ 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)
+{
+ if (*curbuf->b_p_fexpr == NUL) {
+ return p_fexpr;
+ }
+ return curbuf->b_p_fexpr;
+}
+
/// Copy options from one window to another.
/// Used when splitting a window.
void win_copy_options(win_T *wp_from, win_T *wp_to)
@@ -5375,6 +5333,8 @@ 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_kp = empty_string_option;
buf->b_p_path = empty_string_option;
buf->b_p_tags = empty_string_option;
@@ -5447,7 +5407,7 @@ void reset_modifiable(void)
{
curbuf->b_p_ma = false;
p_ma = false;
- options[kOptModifiable].def_val.boolean = false;
+ change_option_default(kOptModifiable, BOOLEAN_OPTVAL(false));
}
/// Set the global value for 'iminsert' to the local value.
@@ -5630,8 +5590,8 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// Only string options below
- // Options that have P_EXPAND are considered to all use file/dir expansion.
- if (flags & P_EXPAND) {
+ // Options that have kOptFlagExpand are considered to all use file/dir expansion.
+ if (flags & kOptFlagExpand) {
p = options[opt_idx].var;
if (p == (char *)&p_bdir
|| p == (char *)&p_dir
@@ -5655,7 +5615,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
xp->xp_backslash = XP_BS_ONE;
}
}
- if (flags & P_COMMA) {
+ if (flags & kOptFlagComma) {
xp->xp_backslash |= XP_BS_COMMA;
}
}
@@ -5665,21 +5625,21 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// pattern, while accounting for backslash-escaped space/commas/colons.
// Triple-backslashed escaped file names (e.g. 'path') can also be
// delimited by space.
- if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON)) {
+ if ((flags & kOptFlagExpand) || (flags & kOptFlagComma) || (flags & kOptFlagColon)) {
for (p = argend - 1; p > xp->xp_pattern; p--) {
// count number of backslashes before ' ' or ','
- if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) {
+ if (*p == ' ' || *p == ',' || (*p == ':' && (flags & kOptFlagColon))) {
char *s = p;
while (s > xp->xp_pattern && *(s - 1) == '\\') {
s--;
}
if ((*p == ' ' && ((xp->xp_backslash & XP_BS_THREE) && (p - s) < 3))
#if defined(BACKSLASH_IN_FILENAME)
- || (*p == ',' && (flags & P_COMMA) && (p - s) < 1)
+ || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 1)
#else
- || (*p == ',' && (flags & P_COMMA) && (p - s) < 2)
+ || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 2)
#endif
- || (*p == ':' && (flags & P_COLON))) {
+ || (*p == ':' && (flags & kOptFlagColon))) {
xp->xp_pattern = p + 1;
break;
}
@@ -5689,7 +5649,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// An option that is a list of single-character flags should always start
// at the end as we don't complete words.
- if (flags & P_FLAGLIST) {
+ if (flags & kOptFlagFlagList) {
xp->xp_pattern = argend;
}
@@ -5842,7 +5802,7 @@ static char *escape_option_str_cmdline(char *var)
for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
if (var[0] == '\\' && var[1] == '\\'
&& expand_option_idx != kOptInvalid
- && (options[expand_option_idx].flags & P_EXPAND)
+ && (options[expand_option_idx].flags & kOptFlagExpand)
&& vim_isfilec((uint8_t)var[2])
&& (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
STRMOVE(var, var + 1);
@@ -5928,11 +5888,11 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
if (option_has_type(expand_option_idx, kOptValTypeNumber)) {
return ExpandOldSetting(numMatches, matches);
- } else if (option_flags & P_COMMA) {
+ } else if (option_flags & kOptFlagComma) {
// Split the option by comma, then present each option to the user if
// it matches the pattern.
// This condition needs to go first, because 'whichwrap' has both
- // P_COMMA and P_FLAGLIST.
+ // kOptFlagComma and kOptFlagFlagList.
if (*option_val == NUL) {
return FAIL;
@@ -5979,7 +5939,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
*matches = ga.ga_data;
*numMatches = ga.ga_len;
return OK;
- } else if (option_flags & P_FLAGLIST) {
+ } else if (option_flags & kOptFlagFlagList) {
// Only present the flags that are set on the option as the other flags
// are not meaningful to do set-= on.
@@ -6044,7 +6004,7 @@ static void option_value2string(vimoption_T *opt, int scope)
varp = *(char **)(varp);
if (varp == NULL) { // Just in case.
NameBuff[0] = NUL;
- } else if (opt->flags & P_EXPAND) {
+ } else if (opt->flags & kOptFlagExpand) {
home_replace(NULL, varp, NameBuff, MAXPATHL, false);
} else {
xstrlcpy(NameBuff, varp, MAXPATHL);
@@ -6105,7 +6065,7 @@ void vimrc_found(char *fname, char *envname)
bool option_was_set(OptIndex opt_idx)
{
assert(opt_idx != kOptInvalid);
- return options[opt_idx].flags & P_WAS_SET;
+ return options[opt_idx].flags & kOptFlagWasSet;
}
/// Reset the flag indicating option "name" was set.
@@ -6114,7 +6074,7 @@ bool option_was_set(OptIndex opt_idx)
void reset_option_was_set(OptIndex opt_idx)
{
assert(opt_idx != kOptInvalid);
- options[opt_idx].flags &= ~P_WAS_SET;
+ options[opt_idx].flags &= ~(unsigned)kOptFlagWasSet;
}
/// fill_culopt_flags() -- called when 'culopt' changes value
@@ -6512,10 +6472,10 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
// welcome to the jungle
PUT_C(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH));
- PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA));
- PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST));
+ PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & kOptFlagComma));
+ PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & kOptFlagFlagList));
- PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET));
+ PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet));
LastSet last_set = { .channel_id = 0 };
if (req_scope == OPT_GLOBAL) {
@@ -6537,12 +6497,9 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
PUT_C(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum));
PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id));
- // TODO(bfredl): do you even nocp?
- OptVal def = optval_from_varp(get_opt_idx(opt), &opt->def_val);
-
- PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(def.type)));
- PUT_C(dict, "default", optval_as_object(def));
- PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP)));
+ PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(option_get_type(get_opt_idx(opt)))));
+ PUT_C(dict, "default", optval_as_object(opt->def_val));
+ PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & kOptFlagNoDup)));
return dict;
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 19764c0121..9b74429467 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -60,13 +60,7 @@ typedef struct {
/// cmdline. Only useful for string options.
opt_expand_cb_T opt_expand_cb;
- // TODO(famiu): Use OptVal for def_val.
- union {
- int boolean;
- OptInt number;
- char *string;
- } def_val; ///< default value for variable
-
+ OptVal def_val; ///< default value
LastSet last_set; ///< script in which the option was last set
} vimoption_T;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index ae9ccd371c..e32edbf727 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -11,6 +11,40 @@
# include "options_enum.generated.h"
#endif
+/// Option flags.
+typedef enum {
+ kOptFlagExpand = 1 << 0, ///< Environment expansion.
+ ///< NOTE: kOptFlagExpand can never be used for local or hidden options.
+ kOptFlagNoDefExp = 1 << 1, ///< Don't expand default value.
+ kOptFlagNoDefault = 1 << 2, ///< Don't set to default value.
+ kOptFlagWasSet = 1 << 3, ///< Option has been set/reset.
+ kOptFlagNoMkrc = 1 << 4, ///< Don't include in :mkvimrc output.
+ kOptFlagUIOption = 1 << 5, ///< Send option to remote UI.
+ kOptFlagRedrTabl = 1 << 6, ///< Redraw tabline.
+ kOptFlagRedrStat = 1 << 7, ///< Redraw status lines.
+ kOptFlagRedrWin = 1 << 8, ///< Redraw current window and recompute text.
+ kOptFlagRedrBuf = 1 << 9, ///< Redraw current buffer and recompute text.
+ kOptFlagRedrAll = kOptFlagRedrBuf | kOptFlagRedrWin, ///< Redraw all windows and recompute text.
+ kOptFlagRedrClear = kOptFlagRedrAll | kOptFlagRedrStat, ///< Clear and redraw all and recompute text.
+ kOptFlagComma = 1 << 10, ///< Comma-separated list.
+ kOptFlagOneComma = (1 << 11) | kOptFlagComma, ///< Comma-separated list that cannot have two consecutive commas.
+ kOptFlagNoDup = 1 << 12, ///< Don't allow duplicate strings.
+ kOptFlagFlagList = 1 << 13, ///< List of single-char flags.
+ kOptFlagSecure = 1 << 14, ///< Cannot change in modeline or secure mode.
+ kOptFlagGettext = 1 << 15, ///< Expand default value with _().
+ kOptFlagNoGlob = 1 << 16, ///< Do not use local value for global vimrc.
+ kOptFlagNFname = 1 << 17, ///< Only normal file name chars allowed.
+ kOptFlagInsecure = 1 << 18, ///< Option was set from a modeline.
+ kOptFlagPriMkrc = 1 << 19, ///< Priority for :mkvimrc (setting option has side effects).
+ kOptFlagNoML = 1 << 20, ///< Not allowed in modeline.
+ kOptFlagCurswant = 1 << 21, ///< Update curswant required; not needed when there is a redraw flag.
+ kOptFlagNDname = 1 << 22, ///< Only normal directory name chars allowed.
+ kOptFlagHLOnly = 1 << 23, ///< Option only changes highlight, not text.
+ kOptFlagMLE = 1 << 24, ///< Under control of 'modelineexpr'.
+ kOptFlagFunc = 1 << 25, ///< Accept a function reference or a lambda.
+ kOptFlagColon = 1 << 26, ///< Values use colons to create sublists.
+} OptFlags;
+
/// Option value type.
/// These types are also used as type flags by using the type value as an index for the type_flags
/// bit field (@see option_has_type()).
@@ -62,7 +96,7 @@ typedef struct {
/// New value of the option.
OptValData os_newval;
- /// Option value was checked to be safe, no need to set P_INSECURE
+ /// Option value was checked to be safe, no need to set kOptFlagInsecure
/// Used for the 'keymap', 'filetype' and 'syntax' options.
bool os_value_checked;
/// Option value changed. Used for the 'filetype' and 'syntax' options.
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index bfdaa11ed9..a88b51dae7 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -7,51 +7,6 @@
// option_vars.h: definition of global variables for settable options
-// Option Flags
-#define P_ALLOCED 0x01U ///< the option is in allocated memory,
- ///< must use free_string_option() when
- ///< assigning new value. Not set if default is
- ///< the same.
-#define P_EXPAND 0x02U ///< environment expansion. NOTE: P_EXPAND can
- ///< never be used for local or hidden options
-#define P_NO_DEF_EXP 0x04U ///< do not expand default value
-#define P_NODEFAULT 0x08U ///< don't set to default value
-#define P_DEF_ALLOCED 0x10U ///< default value is in allocated memory, must
- ///< use free() when assigning new value
-#define P_WAS_SET 0x20U ///< option has been set/reset
-#define P_NO_MKRC 0x40U ///< don't include in :mkvimrc output
-
-// when option changed, what to display:
-#define P_UI_OPTION 0x80U ///< send option to remote UI
-#define P_RTABL 0x100U ///< redraw tabline
-#define P_RSTAT 0x200U ///< redraw status lines
-#define P_RWIN 0x400U ///< redraw current window and recompute text
-#define P_RBUF 0x800U ///< redraw current buffer and recompute text
-#define P_RALL 0xC00U ///< redraw all windows and recompute text
-#define P_RCLR 0xE00U ///< clear and redraw all and recompute text
-
-#define P_COMMA 0x1000U ///< comma separated list
-#define P_ONECOMMA 0x3000U ///< P_COMMA and cannot have two consecutive
- ///< commas
-#define P_NODUP 0x4000U ///< don't allow duplicate strings
-#define P_FLAGLIST 0x8000U ///< list of single-char flags
-
-#define P_SECURE 0x10000U ///< cannot change in modeline or secure mode
-#define P_GETTEXT 0x20000U ///< expand default value with _()
-#define P_NOGLOB 0x40000U ///< do not use local value for global vimrc
-#define P_NFNAME 0x80000U ///< only normal file name chars allowed
-#define P_INSECURE 0x100000U ///< option was set from a modeline
-#define P_PRI_MKRC 0x200000U ///< priority for :mkvimrc (setting option
- ///< has side effects)
-#define P_NO_ML 0x400000U ///< not allowed in modeline
-#define P_CURSWANT 0x800000U ///< update curswant required; not needed
- ///< when there is a redraw flag
-#define P_NDNAME 0x1000000U ///< only normal dir name chars allowed
-#define P_HLONLY 0x2000000U ///< option only changes highlight, not text
-#define P_MLE 0x4000000U ///< under control of 'modelineexpr'
-#define P_FUNC 0x8000000U ///< accept a function reference or a lambda
-#define P_COLON 0x10000000U ///< values use colons to create sublists
-
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
"i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
@@ -348,6 +303,12 @@ enum {
#define LISPWORD_VALUE \
"defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object"
+// When a string option is NULL, it is set to empty_string_option,
+// to avoid having to check for NULL everywhere.
+//
+// TODO(famiu): Remove this when refcounted strings are used for string options.
+EXTERN char empty_string_option[] INIT( = "");
+
// The following are actual variables for the options
EXTERN char *p_ambw; ///< 'ambiwidth'
@@ -490,6 +451,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 int p_fixeol; ///< 'fixendofline'
EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
@@ -770,7 +732,7 @@ EXTERN unsigned ve_flags;
#define VE_NONEU 32U // "NONE"
EXTERN OptInt p_verbose; ///< 'verbose'
#ifdef IN_OPTION_C
-char *p_vfile = ""; ///< used before options are initialized
+char *p_vfile = empty_string_option; ///< used before options are initialized
#else
extern char *p_vfile; ///< 'verbosefile'
#endif
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 734d9a4a02..d59958eaf2 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -8,8 +8,7 @@
--- @field short_desc? string|fun(): string
--- @field varname? string
--- @field pv_name? string
---- @field type 'boolean'|'number'|'string'
---- @field hidden? boolean
+--- @field type vim.option_type|vim.option_type[]
--- @field immutable? boolean
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
@@ -43,6 +42,8 @@
--- @field meta? integer|boolean|string Default to use in Lua meta files
--- @alias vim.option_scope 'global'|'buffer'|'window'
+--- @alias vim.option_type 'boolean'|'number'|'string'
+--- @alias vim.option_value boolean|number|string
--- @alias vim.option_redraw
--- |'statuslines'
@@ -61,18 +62,11 @@ local function cstr(s)
end
--- @param s string
---- @return fun(): string
-local function macros(s)
- return function()
- return '.string=' .. s
- end
-end
-
---- @param s string
---- @return fun(): string
-local function imacros(s)
+--- @param t vim.option_type
+--- @return fun(): string, vim.option_type
+local function macros(s, t)
return function()
- return '.number=' .. s
+ return s, t
end
end
@@ -741,7 +735,6 @@ return {
},
{
abbreviation = 'briopt',
- alloced = true,
cb = 'did_set_breakindentopt',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -804,7 +797,6 @@ return {
},
{
abbreviation = 'bh',
- alloced = true,
cb = 'did_set_bufhidden',
defaults = { if_true = '' },
desc = [=[
@@ -857,7 +849,6 @@ return {
},
{
abbreviation = 'bt',
- alloced = true,
cb = 'did_set_buftype',
defaults = { if_true = '' },
desc = [=[
@@ -994,7 +985,7 @@ return {
{
cb = 'did_set_cedit',
defaults = {
- if_true = macros('CTRL_F_STR'),
+ if_true = macros('CTRL_F_STR', 'string'),
doc = 'CTRL-F',
},
desc = [=[
@@ -1109,7 +1100,6 @@ return {
},
{
abbreviation = 'cink',
- alloced = true,
defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
deny_duplicates = true,
desc = [=[
@@ -1128,7 +1118,6 @@ return {
},
{
abbreviation = 'cino',
- alloced = true,
cb = 'did_set_cinoptions',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -1146,7 +1135,6 @@ return {
},
{
abbreviation = 'cinsd',
- alloced = true,
defaults = { if_true = 'public,protected,private' },
deny_duplicates = true,
desc = [=[
@@ -1165,7 +1153,6 @@ return {
},
{
abbreviation = 'cinw',
- alloced = true,
defaults = { if_true = 'if,else,while,do,for,switch' },
deny_duplicates = true,
desc = [=[
@@ -1288,7 +1275,7 @@ return {
abbreviation = 'co',
cb = 'did_set_lines_or_columns',
defaults = {
- if_true = imacros('DFLT_COLS'),
+ if_true = macros('DFLT_COLS', 'number'),
doc = '80 or terminal width',
},
desc = [=[
@@ -1315,7 +1302,6 @@ return {
},
{
abbreviation = 'com',
- alloced = true,
cb = 'did_set_comments',
defaults = { if_true = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•' },
deny_duplicates = true,
@@ -1334,7 +1320,6 @@ return {
},
{
abbreviation = 'cms',
- alloced = true,
cb = 'did_set_commentstring',
defaults = { if_true = '' },
desc = [=[
@@ -1360,7 +1345,6 @@ return {
},
{
abbreviation = 'cpt',
- alloced = true,
cb = 'did_set_complete',
defaults = { if_true = '.,w,b,u,t' },
deny_duplicates = true,
@@ -1409,7 +1393,6 @@ return {
},
{
abbreviation = 'cfu',
- alloced = true,
cb = 'did_set_completefunc',
defaults = { if_true = '' },
desc = [=[
@@ -1531,7 +1514,6 @@ return {
},
{
abbreviation = 'cocu',
- alloced = true,
cb = 'did_set_concealcursor',
defaults = { if_true = '' },
desc = [=[
@@ -1630,7 +1612,7 @@ return {
{
abbreviation = 'cpo',
cb = 'did_set_cpoptions',
- defaults = { if_true = macros('CPO_VIM') },
+ defaults = { if_true = macros('CPO_VIM', 'string') },
desc = [=[
A sequence of single character flags. When a character is present
this indicates Vi-compatible behavior. This is used for things where
@@ -1974,7 +1956,6 @@ return {
},
{
abbreviation = 'def',
- alloced = true,
defaults = { if_true = '' },
desc = [=[
Pattern to be used to find a macro definition. It is a search
@@ -2094,7 +2075,6 @@ return {
},
{
abbreviation = 'dip',
- alloced = true,
cb = 'did_set_diffopt',
defaults = { if_true = 'internal,filler,closeoff' },
deny_duplicates = true,
@@ -2368,7 +2348,7 @@ return {
{
abbreviation = 'enc',
cb = 'did_set_encoding',
- defaults = { if_true = macros('ENC_DFLT') },
+ defaults = { if_true = macros('ENC_DFLT', 'string') },
deny_in_modelines = true,
desc = [=[
String-encoding used internally and for |RPC| communication.
@@ -2492,7 +2472,7 @@ return {
},
{
abbreviation = 'ef',
- defaults = { if_true = macros('DFLT_ERRORFILE') },
+ defaults = { if_true = macros('DFLT_ERRORFILE', 'string') },
desc = [=[
Name of the errorfile for the QuickFix mode (see |:cf|).
When the "-q" command-line argument is used, 'errorfile' is set to the
@@ -2514,7 +2494,7 @@ return {
{
abbreviation = 'efm',
defaults = {
- if_true = macros('DFLT_EFM'),
+ if_true = macros('DFLT_EFM', 'string'),
doc = 'is very long',
},
deny_duplicates = true,
@@ -2589,7 +2569,6 @@ return {
},
{
abbreviation = 'fenc',
- alloced = true,
cb = 'did_set_encoding',
defaults = { if_true = '' },
desc = [=[
@@ -2703,10 +2682,9 @@ return {
},
{
abbreviation = 'ff',
- alloced = true,
cb = 'did_set_fileformat',
defaults = {
- if_true = macros('DFLT_FF'),
+ if_true = macros('DFLT_FF', 'string'),
doc = 'Windows: "dos", Unix: "unix"',
},
desc = [=[
@@ -2739,7 +2717,7 @@ return {
abbreviation = 'ffs',
cb = 'did_set_fileformats',
defaults = {
- if_true = macros('DFLT_FFS_VIM'),
+ if_true = macros('DFLT_FFS_VIM', 'string'),
doc = 'Windows: "dos,unix", Unix: "unix,dos"',
},
deny_duplicates = true,
@@ -2819,7 +2797,6 @@ return {
},
{
abbreviation = 'ft',
- alloced = true,
cb = 'did_set_filetype_or_syntax',
defaults = { if_true = '' },
desc = [=[
@@ -2855,7 +2832,6 @@ return {
},
{
abbreviation = 'fcs',
- alloced = true,
cb = 'did_set_chars_option',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -2930,6 +2906,65 @@ return {
varname = 'p_fcs',
},
{
+ abbreviation = 'fexpr',
+ cb = 'did_set_optexpr',
+ defaults = { if_true = '' },
+ desc = [=[
+ Expression that is evaluated 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 expression is evaluated only once per |:find| command invocation.
+ The expression can process all the directories specified in 'path'.
+
+ 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|.
+
+ 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
+ should return an empty List.
+
+ If any errors are encountered during the expression evaluation, 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|.
+
+ This option cannot be set from a |modeline| or in the |sandbox|, for
+ security reasons.
+
+ Examples:
+ >vim
+ " Use glob()
+ func FindExprGlob()
+ let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
+ return glob(pat, v:false, v:true)
+ endfunc
+ set findexpr=FindExprGlob()
+
+ " Use the 'git ls-files' output
+ func FindGitFiles()
+ let fnames = systemlist('git ls-files')
+ return fnames->filter('v:val =~? v:fname')
+ endfunc
+ set findexpr=FindGitFiles()
+ <
+ ]=],
+ full_name = 'findexpr',
+ scope = { 'global', 'buffer' },
+ secure = true,
+ short_desc = N_('expression used for :find'),
+ tags = { 'E1514' },
+ type = 'string',
+ varname = 'p_fexpr',
+ },
+ {
abbreviation = 'fixeol',
cb = 'did_set_eof_eol_fixeol_bomb',
defaults = { if_true = true },
@@ -2970,7 +3005,6 @@ return {
},
{
abbreviation = 'fdc',
- alloced = true,
cb = 'did_set_foldcolumn',
defaults = { if_true = '0' },
desc = [=[
@@ -3009,7 +3043,6 @@ return {
},
{
abbreviation = 'fde',
- alloced = true,
cb = 'did_set_foldexpr',
defaults = { if_true = '0' },
desc = [=[
@@ -3035,7 +3068,6 @@ return {
},
{
abbreviation = 'fdi',
- alloced = true,
cb = 'did_set_foldignore',
defaults = { if_true = '#' },
desc = [=[
@@ -3090,7 +3122,6 @@ return {
},
{
abbreviation = 'fmr',
- alloced = true,
cb = 'did_set_foldmarker',
defaults = { if_true = '{{{,}}}' },
deny_duplicates = true,
@@ -3110,7 +3141,6 @@ return {
},
{
abbreviation = 'fdm',
- alloced = true,
cb = 'did_set_foldmethod',
defaults = { if_true = 'manual' },
desc = [=[
@@ -3211,7 +3241,6 @@ return {
},
{
abbreviation = 'fdt',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = 'foldtext()' },
desc = [=[
@@ -3239,7 +3268,6 @@ return {
},
{
abbreviation = 'fex',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -3293,7 +3321,6 @@ return {
},
{
abbreviation = 'flp',
- alloced = true,
defaults = { if_true = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*' },
desc = [=[
A pattern that is used to recognize a list header. This is used for
@@ -3314,9 +3341,8 @@ return {
},
{
abbreviation = 'fo',
- alloced = true,
cb = 'did_set_formatoptions',
- defaults = { if_true = macros('DFLT_FO_VIM') },
+ defaults = { if_true = macros('DFLT_FO_VIM', 'string') },
desc = [=[
This is a sequence of letters which describes how automatic
formatting is to be done.
@@ -3409,7 +3435,7 @@ return {
},
{
abbreviation = 'gfm',
- defaults = { if_true = macros('DFLT_GREPFORMAT') },
+ defaults = { if_true = macros('DFLT_GREPFORMAT', 'string') },
deny_duplicates = true,
desc = [=[
Format to recognize for the ":grep" command output.
@@ -3773,6 +3799,7 @@ return {
},
{
abbreviation = 'gtl',
+ defaults = { if_true = '' },
desc = [=[
When non-empty describes the text to use in a label of the GUI tab
pages line. When empty and when the result is empty Vim will use a
@@ -3798,6 +3825,7 @@ return {
},
{
abbreviation = 'gtt',
+ defaults = { if_true = '' },
desc = [=[
When non-empty describes the text to use in a tooltip for the GUI tab
pages line. When empty Vim will use a default tooltip.
@@ -3817,7 +3845,7 @@ return {
abbreviation = 'hf',
cb = 'did_set_helpfile',
defaults = {
- if_true = macros('DFLT_HELPFILE'),
+ if_true = macros('DFLT_HELPFILE', 'string'),
doc = [[(MS-Windows) "$VIMRUNTIME\doc\help.txt"
(others) "$VIMRUNTIME/doc/help.txt"]],
},
@@ -3914,7 +3942,7 @@ return {
{
abbreviation = 'hl',
cb = 'did_set_highlight',
- defaults = { if_true = macros('HIGHLIGHT_INIT') },
+ defaults = { if_true = macros('HIGHLIGHT_INIT', 'string') },
deny_duplicates = true,
full_name = 'highlight',
list = 'onecomma',
@@ -4080,7 +4108,7 @@ return {
{
abbreviation = 'imi',
cb = 'did_set_iminsert',
- defaults = { if_true = imacros('B_IMODE_NONE') },
+ defaults = { if_true = macros('B_IMODE_NONE', 'number') },
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used in
Insert mode. Valid values:
@@ -4106,7 +4134,7 @@ return {
},
{
abbreviation = 'ims',
- defaults = { if_true = imacros('B_IMODE_USE_INSERT') },
+ defaults = { if_true = macros('B_IMODE_USE_INSERT', 'number') },
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used when
entering a search pattern. Valid values:
@@ -4155,7 +4183,6 @@ return {
},
{
abbreviation = 'inc',
- alloced = true,
defaults = { if_true = '' },
desc = [=[
Pattern to be used to find an include command. It is a search
@@ -4177,7 +4204,6 @@ return {
},
{
abbreviation = 'inex',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -4262,7 +4288,6 @@ return {
},
{
abbreviation = 'inde',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -4316,7 +4341,6 @@ return {
},
{
abbreviation = 'indk',
- alloced = true,
defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
deny_duplicates = true,
desc = [=[
@@ -4458,7 +4482,6 @@ return {
},
{
abbreviation = 'isk',
- alloced = true,
cb = 'did_set_iskeyword',
defaults = { if_true = '@,48-57,_,192-255' },
deny_duplicates = true,
@@ -4567,7 +4590,6 @@ return {
},
{
abbreviation = 'kmp',
- alloced = true,
cb = 'did_set_keymap',
defaults = { if_true = '' },
desc = [=[
@@ -4812,7 +4834,7 @@ return {
{
cb = 'did_set_lines_or_columns',
defaults = {
- if_true = imacros('DFLT_ROWS'),
+ if_true = macros('DFLT_ROWS', 'number'),
doc = '24 or terminal height',
},
desc = [=[
@@ -4900,7 +4922,7 @@ return {
{
abbreviation = 'lw',
defaults = {
- if_true = macros('LISPWORD_VALUE'),
+ if_true = macros('LISPWORD_VALUE', 'string'),
doc = 'is very long',
},
deny_duplicates = true,
@@ -4944,7 +4966,6 @@ return {
},
{
abbreviation = 'lcs',
- alloced = true,
cb = 'did_set_chars_option',
defaults = { if_true = 'tab:> ,trail:-,nbsp:+' },
deny_duplicates = true,
@@ -5166,7 +5187,6 @@ return {
},
{
abbreviation = 'mps',
- alloced = true,
cb = 'did_set_matchpairs',
defaults = { if_true = '(:),{:},[:]' },
deny_duplicates = true,
@@ -5210,7 +5230,7 @@ return {
},
{
abbreviation = 'mco',
- defaults = { if_true = imacros('MAX_MCO') },
+ defaults = { if_true = macros('MAX_MCO', 'number') },
full_name = 'maxcombine',
scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'),
@@ -5734,7 +5754,6 @@ return {
},
{
abbreviation = 'nf',
- alloced = true,
cb = 'did_set_nrformats',
defaults = { if_true = 'bin,hex' },
deny_duplicates = true,
@@ -5845,7 +5864,6 @@ return {
},
{
abbreviation = 'ofu',
- alloced = true,
cb = 'did_set_omnifunc',
defaults = { if_true = '' },
desc = [=[
@@ -6229,7 +6247,6 @@ return {
},
{
abbreviation = 'qe',
- alloced = true,
defaults = { if_true = '\\' },
desc = [=[
The characters that are used to escape quotes in a string. Used for
@@ -6440,7 +6457,6 @@ return {
},
{
abbreviation = 'rlc',
- alloced = true,
cb = 'did_set_rightleftcmd',
defaults = { if_true = 'search' },
desc = [=[
@@ -6495,7 +6511,6 @@ return {
},
{
abbreviation = 'ruf',
- alloced = true,
cb = 'did_set_rulerformat',
defaults = { if_true = '' },
desc = [=[
@@ -7644,7 +7659,6 @@ return {
},
{
abbreviation = 'scl',
- alloced = true,
cb = 'did_set_signcolumn',
defaults = { if_true = 'auto' },
desc = [=[
@@ -7801,7 +7815,6 @@ return {
},
{
abbreviation = 'spc',
- alloced = true,
cb = 'did_set_spellcapcheck',
defaults = { if_true = '[.?!]\\_[\\])\'"\\t ]\\+' },
desc = [=[
@@ -7824,7 +7837,6 @@ return {
},
{
abbreviation = 'spf',
- alloced = true,
cb = 'did_set_spellfile',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -7862,7 +7874,6 @@ return {
},
{
abbreviation = 'spl',
- alloced = true,
cb = 'did_set_spelllang',
defaults = { if_true = 'en' },
deny_duplicates = true,
@@ -8096,7 +8107,6 @@ return {
},
{
abbreviation = 'stc',
- alloced = true,
cb = 'did_set_statuscolumn',
defaults = { if_true = '' },
desc = [=[
@@ -8162,7 +8172,6 @@ return {
},
{
abbreviation = 'stl',
- alloced = true,
cb = 'did_set_statusline',
defaults = { if_true = '' },
desc = [=[
@@ -8411,7 +8420,6 @@ return {
},
{
abbreviation = 'sua',
- alloced = true,
defaults = { if_true = '' },
deny_duplicates = true,
desc = [=[
@@ -8520,7 +8528,6 @@ return {
},
{
abbreviation = 'syn',
- alloced = true,
cb = 'did_set_filetype_or_syntax',
defaults = { if_true = '' },
desc = [=[
@@ -9007,7 +9014,6 @@ return {
},
{
abbreviation = 'tsrfu',
- alloced = true,
cb = 'did_set_thesaurusfunc',
defaults = { if_true = '' },
desc = [=[
@@ -9613,7 +9619,7 @@ return {
abbreviation = 'wc',
cb = 'did_set_wildchar',
defaults = {
- if_true = imacros('TAB'),
+ if_true = macros('TAB', 'number'),
doc = '<Tab>',
},
desc = [=[
@@ -9867,7 +9873,6 @@ return {
},
{
abbreviation = 'wbr',
- alloced = true,
cb = 'did_set_winbar',
defaults = { if_true = '' },
desc = [=[
@@ -10010,7 +10015,6 @@ return {
},
{
abbreviation = 'winhl',
- alloced = true,
cb = 'did_set_winhighlight',
defaults = { if_true = '' },
deny_duplicates = true,
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 21a3a3877b..c66849800c 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -233,6 +233,7 @@ 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_tfu);
@@ -250,7 +251,6 @@ void check_buf_options(buf_T *buf)
/// Free the string allocated for an option.
/// Checks for the string being empty_string_option. This may happen if we're out of memory,
/// xstrdup() returned NULL, which was replaced by empty_string_option by check_options().
-/// Does NOT check for P_ALLOCED flag!
void free_string_option(char *p)
{
if (p != empty_string_option) {
@@ -420,9 +420,9 @@ const char *check_stl_option(char *s)
/// often illegal in a file name. Be more permissive if "secure" is off.
bool check_illegal_path_names(char *val, uint32_t flags)
{
- return (((flags & P_NFNAME)
+ return (((flags & kOptFlagNFname)
&& strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
- || ((flags & P_NDNAME)
+ || ((flags & kOptFlagNDname)
&& strpbrk(val, "*?[|;&<>\r\n") != NULL));
}
@@ -1378,7 +1378,7 @@ const char *did_set_filetype_or_syntax(optset_T *args)
args->os_value_changed = strcmp(args->os_oldval.string.data, *varp) != 0;
- // Since we check the value, there is no need to set P_INSECURE,
+ // Since we check the value, there is no need to set kOptFlagInsecure,
// even when the value comes from a modeline.
args->os_value_checked = true;
@@ -1659,7 +1659,7 @@ const char *did_set_keymap(optset_T *args)
secure = secure_save;
- // Since we check the value, there is no need to set P_INSECURE,
+ // Since we check the value, there is no need to set kOptFlagInsecure,
// even when the value comes from a modeline.
args->os_value_checked = true;
@@ -1886,8 +1886,9 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
matches);
}
-/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
-/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
+/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr',
+/// '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/popupmenu.c b/src/nvim/popupmenu.c
index 87ef6a27ad..529d65c5dc 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -1358,14 +1358,15 @@ static void pum_select_mouse_pos(void)
if (mouse_grid == pum_grid.handle) {
pum_selected = mouse_row;
return;
- } else if (mouse_grid != pum_anchor_grid) {
+ } else if (mouse_grid != pum_anchor_grid || mouse_col < pum_grid.comp_col
+ || mouse_col >= pum_grid.comp_col + pum_grid.comp_width) {
pum_selected = -1;
return;
}
- int idx = mouse_row - pum_row;
+ int idx = mouse_row - pum_grid.comp_row;
- if (idx < 0 || idx >= pum_height) {
+ if (idx < 0 || idx >= pum_grid.comp_height) {
pum_selected = -1;
} else if (*pum_array[idx].pum_text != NUL) {
pum_selected = idx;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 5bd81ce469..f037d5d924 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -7348,7 +7348,6 @@ void ex_helpgrep(exarg_T *eap)
bool updated = false;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *const save_cpo = p_cpo;
- const bool save_cpo_allocated = (get_option(kOptCpoptions)->flags & P_ALLOCED);
p_cpo = empty_string_option;
bool new_qi = false;
@@ -7388,9 +7387,7 @@ void ex_helpgrep(exarg_T *eap)
if (*p_cpo == NUL) {
set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0);
}
- if (save_cpo_allocated) {
- free_string_option(save_cpo);
- }
+ free_string_option(save_cpo);
}
if (updated) {
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index 2f43f8b32b..6c6edd4ee2 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -41,6 +41,22 @@ M.vars = {
included here, because it will be executed anyway.
]=],
},
+ cmdbang = {
+ type = 'integer',
+ desc = [=[
+ Set like v:cmdarg for a file read/write command. When a "!"
+ was used the value is 1, otherwise it is 0. Note that this
+ can only be used in autocommands. For user commands |<bang>|
+ 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 = [=[
@@ -53,15 +69,6 @@ M.vars = {
See |multi-lang|.
]=],
},
- cmdbang = {
- type = 'integer',
- desc = [=[
- Set like v:cmdarg for a file read/write command. When a "!"
- was used the value is 1, otherwise it is 0. Note that this
- can only be used in autocommands. For user commands |<bang>|
- can be used.
- ]=],
- },
completed_item = {
desc = [=[
Dictionary containing the |complete-items| for the most
@@ -118,15 +125,6 @@ M.vars = {
VimLeave autocommands will not be executed.
]=],
},
- exiting = {
- desc = [=[
- Exit code, or |v:null| before invoking the |VimLeavePre|
- and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
- Example: >vim
- :au VimLeave * echo "Exit value is " .. v:exiting
- <
- ]=],
- },
echospace = {
type = 'integer',
desc = [=[
@@ -243,18 +241,13 @@ M.vars = {
or |expr7| when used with numeric operators). Read-only.
]=],
},
- fcs_reason = {
- type = 'string',
+ exiting = {
desc = [=[
- The reason why the |FileChangedShell| event was triggered.
- Can be used in an autocommand to decide what to do and/or what
- to set v:fcs_choice to. Possible values:
- deleted file no longer exists
- conflict file contents, mode or timestamp was
- changed and buffer is modified
- changed file contents has changed
- mode mode of file changed
- time only file timestamp changed
+ Exit code, or |v:null| before invoking the |VimLeavePre|
+ and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
+ Example: >vim
+ :au VimLeave * echo "Exit value is " .. v:exiting
+ <
]=],
},
fcs_choice = {
@@ -280,11 +273,33 @@ M.vars = {
Vim behaves like it is empty, there is no warning message.
]=],
},
+ fcs_reason = {
+ type = 'string',
+ desc = [=[
+ The reason why the |FileChangedShell| event was triggered.
+ Can be used in an autocommand to decide what to do and/or what
+ to set v:fcs_choice to. Possible values:
+ deleted file no longer exists
+ conflict file contents, mode or timestamp was
+ changed and buffer is modified
+ changed file contents has changed
+ mode mode of file changed
+ time only file timestamp changed
+ ]=],
+ },
fname = {
type = 'string',
desc = [=[
When evaluating 'includeexpr': the file name that was
- detected. Empty otherwise.
+ detected. When evaluating 'findexpr': the argument passed to
+ the |:find| command. Empty otherwise.
+ ]=],
+ },
+ fname_diff = {
+ type = 'string',
+ desc = [=[
+ The name of the diff (patch) file. Only valid while
+ evaluating 'patchexpr'.
]=],
},
fname_in = {
@@ -298,6 +313,13 @@ M.vars = {
And set to the swap file name for |SwapExists|.
]=],
},
+ fname_new = {
+ type = 'string',
+ desc = [=[
+ The name of the new version of the file. Only valid while
+ evaluating 'diffexpr'.
+ ]=],
+ },
fname_out = {
type = 'string',
desc = [=[
@@ -313,20 +335,6 @@ M.vars = {
file and different from v:fname_in.
]=],
},
- fname_new = {
- type = 'string',
- desc = [=[
- The name of the new version of the file. Only valid while
- evaluating 'diffexpr'.
- ]=],
- },
- fname_diff = {
- type = 'string',
- desc = [=[
- The name of the diff (patch) file. Only valid while
- evaluating 'patchexpr'.
- ]=],
- },
folddashes = {
type = 'string',
desc = [=[
@@ -335,17 +343,17 @@ M.vars = {
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
- foldlevel = {
+ foldend = {
type = 'integer',
desc = [=[
- Used for 'foldtext': foldlevel of closed fold.
+ Used for 'foldtext': last line of closed fold.
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
- foldend = {
+ foldlevel = {
type = 'integer',
desc = [=[
- Used for 'foldtext': last line of closed fold.
+ Used for 'foldtext': foldlevel of closed fold.
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
@@ -435,19 +443,12 @@ M.vars = {
2147483647 on all systems.
]=],
},
- mouse_win = {
- type = 'integer',
- desc = [=[
- Window number for a mouse click obtained with |getchar()|.
- First window has number 1, like with |winnr()|. The value is
- zero when there was no mouse button click.
- ]=],
- },
- mouse_winid = {
+ mouse_col = {
type = 'integer',
desc = [=[
- |window-ID| for a mouse click obtained with |getchar()|.
- The value is zero when there was no mouse button click.
+ Column number for a mouse click obtained with |getchar()|.
+ This is the screen column number, like with |virtcol()|. The
+ value is zero when there was no mouse button click.
]=],
},
mouse_lnum = {
@@ -458,12 +459,19 @@ M.vars = {
value is zero when there was no mouse button click.
]=],
},
- mouse_col = {
+ mouse_win = {
type = 'integer',
desc = [=[
- Column number for a mouse click obtained with |getchar()|.
- This is the screen column number, like with |virtcol()|. The
- value is zero when there was no mouse button click.
+ Window number for a mouse click obtained with |getchar()|.
+ First window has number 1, like with |winnr()|. The value is
+ zero when there was no mouse button click.
+ ]=],
+ },
+ mouse_winid = {
+ type = 'integer',
+ desc = [=[
+ |window-ID| for a mouse click obtained with |getchar()|.
+ The value is zero when there was no mouse button click.
]=],
},
msgpack_types = {
@@ -516,6 +524,35 @@ M.vars = {
than String this will cause trouble.
]=],
},
+ operator = {
+ type = 'string',
+ desc = [=[
+ The last operator given in Normal mode. This is a single
+ character except for commands starting with <g> or <z>,
+ in which case it is two characters. Best used alongside
+ |v:prevcount| and |v:register|. Useful if you want to cancel
+ Operator-pending mode and then use the operator, e.g.: >vim
+ :omap O <Esc>:call MyMotion(v:operator)<CR>
+ <
+ The value remains set until another operator is entered, thus
+ don't expect it to be empty.
+ v:operator is not set for |:delete|, |:yank| or other Ex
+ commands.
+ Read-only.
+ ]=],
+ },
+ option_command = {
+ type = 'string',
+ desc = [=[
+ Command used to set the option. Valid while executing an
+ |OptionSet| autocommand.
+ value option was set via ~
+ "setlocal" |:setlocal| or `:let l:xxx`
+ "setglobal" |:setglobal| or `:let g:xxx`
+ "set" |:set| or |:let|
+ "modeline" |modeline|
+ ]=],
+ },
option_new = {
desc = [=[
New value of the option. Valid while executing an |OptionSet|
@@ -530,15 +567,15 @@ M.vars = {
global old value.
]=],
},
- option_oldlocal = {
+ option_oldglobal = {
desc = [=[
- Old local value of the option. Valid while executing an
+ Old global value of the option. Valid while executing an
|OptionSet| autocommand.
]=],
},
- option_oldglobal = {
+ option_oldlocal = {
desc = [=[
- Old global value of the option. Valid while executing an
+ Old local value of the option. Valid while executing an
|OptionSet| autocommand.
]=],
},
@@ -549,35 +586,6 @@ M.vars = {
|OptionSet| autocommand. Can be either "global" or "local"
]=],
},
- option_command = {
- type = 'string',
- desc = [=[
- Command used to set the option. Valid while executing an
- |OptionSet| autocommand.
- value option was set via ~
- "setlocal" |:setlocal| or `:let l:xxx`
- "setglobal" |:setglobal| or `:let g:xxx`
- "set" |:set| or |:let|
- "modeline" |modeline|
- ]=],
- },
- operator = {
- type = 'string',
- desc = [=[
- The last operator given in Normal mode. This is a single
- character except for commands starting with <g> or <z>,
- in which case it is two characters. Best used alongside
- |v:prevcount| and |v:register|. Useful if you want to cancel
- Operator-pending mode and then use the operator, e.g.: >vim
- :omap O <Esc>:call MyMotion(v:operator)<CR>
- <
- The value remains set until another operator is entered, thus
- don't expect it to be empty.
- v:operator is not set for |:delete|, |:yank| or other Ex
- commands.
- Read-only.
- ]=],
- },
prevcount = {
type = 'integer',
desc = [=[
@@ -641,6 +649,17 @@ M.vars = {
hit-enter prompt.
]=],
},
+ searchforward = {
+ type = 'integer',
+ desc = [=[
+ Search direction: 1 after a forward search, 0 after a
+ backward search. It is reset to forward when directly setting
+ the last search pattern, see |quote/|.
+ Note that the value is restored when returning from a
+ function. |function-search-undo|.
+ Read-write.
+ ]=],
+ },
servername = {
type = 'string',
desc = [=[
@@ -664,17 +683,6 @@ M.vars = {
Note the contents of $NVIM may change in the future.
]=],
},
- searchforward = {
- type = 'integer',
- desc = [=[
- Search direction: 1 after a forward search, 0 after a
- backward search. It is reset to forward when directly setting
- the last search pattern, see |quote/|.
- Note that the value is restored when returning from a
- function. |function-search-undo|.
- Read-write.
- ]=],
- },
shell_error = {
type = 'integer',
desc = [=[
@@ -709,14 +717,6 @@ M.vars = {
<
]=],
},
- swapname = {
- type = 'string',
- desc = [=[
- Name of the swapfile found.
- Only valid during |SwapExists| event.
- Read-only.
- ]=],
- },
swapchoice = {
type = 'string',
desc = [=[
@@ -743,6 +743,14 @@ M.vars = {
For ":edit +cmd file" the value is ":cmd\r".
]=],
},
+ swapname = {
+ type = 'string',
+ desc = [=[
+ Name of the swapfile found.
+ Only valid during |SwapExists| event.
+ Read-only.
+ ]=],
+ },
t_blob = {
type = 'integer',
tags = { 'v:t_TYPE' },
@@ -776,22 +784,22 @@ M.vars = {
type = 'integer',
desc = 'Value of |String| type. Read-only. See: |type()|',
},
- termresponse = {
+ termrequest = {
type = 'string',
desc = [=[
The value of the most recent OSC or DCS control sequence
- received by Nvim from the terminal. This can be read in a
- |TermResponse| event handler after querying the terminal using
- another escape sequence.
+ sent from a process running in the embedded |terminal|.
+ This can be read in a |TermRequest| event handler to respond
+ to queries from embedded applications.
]=],
},
- termrequest = {
+ termresponse = {
type = 'string',
desc = [=[
The value of the most recent OSC or DCS control sequence
- sent from a process running in the embedded |terminal|.
- This can be read in a |TermRequest| event handler to respond
- to queries from embedded applications.
+ received by Nvim from the terminal. This can be read in a
+ |TermResponse| event handler after querying the terminal using
+ another escape sequence.
]=],
},
testing = {
@@ -849,20 +857,20 @@ M.vars = {
<
]=],
},
- virtnum = {
+ vim_did_enter = {
type = 'integer',
desc = [=[
- Virtual line number for the 'statuscolumn' expression.
- Negative when drawing the status column for virtual lines, zero
- when drawing an actual buffer line, and positive when drawing
- the wrapped part of a buffer line.
+ 0 during startup, 1 just before |VimEnter|.
Read-only.
]=],
},
- vim_did_enter = {
+ virtnum = {
type = 'integer',
desc = [=[
- 0 during startup, 1 just before |VimEnter|.
+ Virtual line number for the 'statuscolumn' expression.
+ Negative when drawing the status column for virtual lines, zero
+ when drawing an actual buffer line, and positive when drawing
+ the wrapped part of a buffer line.
Read-only.
]=],
},
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index 4ae1146703..eb1ac3e6a1 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -111,6 +111,19 @@ describe('vim.diagnostic', function()
{ details = true }
)
end
+
+ ---@param ns integer
+ function _G.get_underline_extmarks(ns)
+ ---@type integer
+ local underline_ns = vim.diagnostic.get_namespace(ns).user_data.underline_ns
+ return vim.api.nvim_buf_get_extmarks(
+ _G.diagnostic_bufnr,
+ underline_ns,
+ 0,
+ -1,
+ { details = true }
+ )
+ end
end)
exec_lua(function()
@@ -1813,6 +1826,21 @@ describe('vim.diagnostic', function()
_G.make_info('Info', 4, 4, 4, 4),
})
+ function _G.get_highest_underline_hl(severity_sort)
+ vim.diagnostic.config({
+ underline = true,
+ severity_sort = severity_sort,
+ })
+
+ local extmarks = _G.get_underline_extmarks(_G.diagnostic_ns)
+
+ table.sort(extmarks, function(a, b)
+ return a[4].priority > b[4].priority
+ end)
+
+ return extmarks[1][4].hl_group
+ end
+
function _G.get_virt_text_and_signs(severity_sort)
vim.diagnostic.config({
severity_sort = severity_sort,
@@ -1864,6 +1892,12 @@ describe('vim.diagnostic', function()
result = exec_lua [[return _G.get_virt_text_and_signs({ reverse = true })]]
eq({ 'Error', 'Warn', 'Info' }, result[1])
eq({ 'Info', 'Warn', 'Error' }, result[2])
+
+ local underline_hl = exec_lua [[return _G.get_highest_underline_hl(true)]]
+ eq('DiagnosticUnderlineError', underline_hl)
+
+ underline_hl = exec_lua [[return _G.get_highest_underline_hl({ reverse = true })]]
+ eq('DiagnosticUnderlineInfo', underline_hl)
end)
it('can show diagnostic sources in virtual text', function()
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index 78c684083b..b7e292cad0 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -219,13 +219,13 @@ describe('vim.lsp.diagnostic', function()
eq(1, #result)
eq(
exec_lua(function()
- return vim.str_byteindex(line, 7, true)
+ return vim.str_byteindex(line, 'utf-16', 7)
end),
result[1].col
)
eq(
exec_lua(function()
- return vim.str_byteindex(line, 8, true)
+ return vim.str_byteindex(line, 'utf-16', 8)
end),
result[1].end_col
)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index f128bdc961..295d677aa0 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -4506,9 +4506,10 @@ describe('builtin popupmenu', function()
:let g:menustr = 'foo' |
]])
end
+ local no_menu_screen ---@type string|test.function.ui.screen.Expect
if multigrid then
api.nvim_input_mouse('left', 'press', '', 4, 1, 2)
- screen:expect({
+ no_menu_screen = {
grid = [[
## grid 1
[2:--------------------------------]|*2
@@ -4527,19 +4528,189 @@ describe('builtin popupmenu', function()
{2:WINBAR }|
^popup menu test |
]],
- })
+ }
else
feed('<LeftMouse><31,2>')
- screen:expect([[
+ no_menu_screen = {
+ grid = [[
popup menu test |
{1:~ }|
{3:[No Name] [+] }|
popup menu test│{2:WINBAR }|
{1:~ }│^popup menu test |
:let g:menustr = 'bar' |
- ]])
+ ]],
+ }
end
+ screen:expect(no_menu_screen)
eq('bar', api.nvim_get_var('menustr'))
+
+ local no_sel_screen ---@type string|test.function.ui.screen.Expect
+ if multigrid then
+ no_sel_screen = {
+ grid = [[
+ ## grid 1
+ [2:--------------------------------]|*2
+ {3:[No Name] [+] }|
+ [5:---------------]│[6:----------------]|*2
+ [3:--------------------------------]|
+ ## grid 2
+ popup menu test |
+ {1:~ }|
+ ## grid 3
+ :let g:menustr = 'bar' |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {n: baz }|
+ ## grid 5
+ popup menu test|
+ {1:~ }|
+ ## grid 6
+ {2:WINBAR }|
+ ^popup menu test |
+ ]],
+ float_pos = { [4] = { -1, 'NW', 1, 1, 19, false, 250 } },
+ }
+ else
+ no_sel_screen = {
+ grid = [[
+ popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {3:[No Name] [+] }{n: bar }{3: }|
+ popup menu test│{2:WIN}{n: baz }{2: }|
+ {1:~ }│^popup menu test |
+ :let g:menustr = 'bar' |
+ ]],
+ }
+ end
+ local sel_screens = {} ---@type (string|test.function.ui.screen.Expect)[]
+ for i, s in ipairs({ 'foo', 'bar', 'baz' }) do
+ local sel_screen = vim.deepcopy(no_sel_screen)
+ local grid = assert(sel_screen.grid)
+ grid = grid:gsub(vim.pesc(('{n: %s }'):format(s)), ('{s: %s }'):format(s))
+ sel_screen.grid = grid
+ sel_screens[i] = sel_screen
+ end
+
+ command([[let g:menustr = '']])
+ local g = multigrid and 1 or 0
+
+ api.nvim_input_mouse('right', 'press', '', g, 0, 20)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 1, 19)
+ screen:expect(sel_screens[1])
+ api.nvim_input_mouse('move', '', '', g, 1, 18)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 2, 23)
+ screen:expect(sel_screens[2])
+ api.nvim_input_mouse('move', '', '', g, 2, 24)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 3, 19)
+ screen:expect(sel_screens[3])
+ api.nvim_input_mouse('left', 'press', '', g, 3, 18)
+ screen:expect(no_menu_screen)
+ eq('', api.nvim_get_var('menustr'))
+
+ command('wincmd t | set rightleft')
+ if multigrid then
+ no_menu_screen = {
+ grid = [[
+ ## grid 1
+ [2:--------------------------------]|*2
+ {4:[No Name] [+] }|
+ [5:---------------]│[6:----------------]|*2
+ [3:--------------------------------]|
+ ## grid 2
+ tset unem pupo^p|
+ {1: ~}|
+ ## grid 3
+ :let g:menustr = 'bar' |
+ ## grid 5
+ popup menu test|
+ {1:~ }|
+ ## grid 6
+ {2:WINBAR }|
+ popup menu test |
+ ]],
+ }
+ else
+ no_menu_screen = {
+ grid = [[
+ tset unem pupo^p|
+ {1: ~}|
+ {4:[No Name] [+] }|
+ popup menu test│{2:WINBAR }|
+ {1:~ }│popup menu test |
+ :let g:menustr = 'bar' |
+ ]],
+ }
+ end
+ screen:expect(no_menu_screen)
+
+ if multigrid then
+ no_sel_screen = {
+ grid = [[
+ ## grid 1
+ [2:--------------------------------]|*2
+ {4:[No Name] [+] }|
+ [5:---------------]│[6:----------------]|*2
+ [3:--------------------------------]|
+ ## grid 2
+ tset unem pupo^p|
+ {1: ~}|
+ ## grid 3
+ :let g:menustr = 'bar' |
+ ## grid 4
+ {n: oof }|
+ {n: rab }|
+ {n: zab }|
+ ## grid 5
+ popup menu test|
+ {1:~ }|
+ ## grid 6
+ {2:WINBAR }|
+ popup menu test |
+ ]],
+ float_pos = { [4] = { -1, 'NW', 1, 1, 17, false, 250 } },
+ }
+ else
+ no_sel_screen = {
+ grid = [[
+ tset unem pupo^p|
+ {1: }{n: oof }{1: ~}|
+ {4:[No Name] [+] }{n: rab }{4: }|
+ popup menu test│{2:W}{n: zab }{2: }|
+ {1:~ }│popup menu test |
+ :let g:menustr = 'bar' |
+ ]],
+ }
+ end
+ for i, s in ipairs({ 'oof', 'rab', 'zab' }) do
+ local sel_screen = vim.deepcopy(no_sel_screen)
+ local grid = assert(sel_screen.grid)
+ grid = grid:gsub(vim.pesc(('{n: %s }'):format(s)), ('{s: %s }'):format(s))
+ sel_screen.grid = grid
+ sel_screens[i] = sel_screen
+ end
+
+ api.nvim_input_mouse('right', 'press', '', g, 0, 20)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 1, 21)
+ screen:expect(sel_screens[1])
+ api.nvim_input_mouse('move', '', '', g, 1, 22)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 2, 17)
+ screen:expect(sel_screens[2])
+ api.nvim_input_mouse('move', '', '', g, 2, 16)
+ screen:expect(no_sel_screen)
+ api.nvim_input_mouse('move', '', '', g, 3, 21)
+ screen:expect(sel_screens[3])
+ api.nvim_input_mouse('left', 'press', '', g, 3, 22)
+ screen:expect(no_menu_screen)
+ eq('', api.nvim_get_var('menustr'))
+
+ command('set norightleft')
end)
if not multigrid then
diff --git a/test/old/testdir/runtest.vim b/test/old/testdir/runtest.vim
index e05a78e9ca..058635c332 100644
--- a/test/old/testdir/runtest.vim
+++ b/test/old/testdir/runtest.vim
@@ -174,10 +174,6 @@ func GetAllocId(name)
return lnum - top - 1
endfunc
-if has('reltime')
- let g:func_start = reltime()
-endif
-
" Get the list of swap files in the current directory.
func s:GetSwapFileList()
let save_dir = &directory
@@ -567,6 +563,16 @@ for g:testfunc in sort(s:tests)
" A test can set g:test_is_flaky to retry running the test.
let g:test_is_flaky = 0
+ " A test can set g:max_run_nr to change the max retry count.
+ let g:max_run_nr = 5
+ if has('mac')
+ let g:max_run_nr = 10
+ endif
+
+ " By default, give up if the same error occurs. A test can set
+ " g:giveup_same_error to 0 to not give up on the same error and keep trying.
+ let g:giveup_same_error = 1
+
let starttime = strftime("%H:%M:%S")
call RunTheTest(g:testfunc)
@@ -582,10 +588,15 @@ for g:testfunc in sort(s:tests)
call extend(s:messages, v:errors)
let endtime = strftime("%H:%M:%S")
- call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}:')
+ if has('reltime')
+ let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
+ else
+ let suffix = ''
+ endif
+ call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
call extend(total_errors, v:errors)
- if g:run_nr >= 5 || prev_error == v:errors[0]
+ if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
call add(total_errors, 'Flaky test failed too often, giving up')
let v:errors = total_errors
break
@@ -596,7 +607,8 @@ for g:testfunc in sort(s:tests)
" Flakiness is often caused by the system being very busy. Sleep a
" couple of seconds to have a higher chance of succeeding the second
" time.
- sleep 2
+ let delay = g:run_nr * 2
+ exe 'sleep' delay
let prev_error = v:errors[0]
let v:errors = []
diff --git a/test/old/testdir/test_findfile.vim b/test/old/testdir/test_findfile.vim
index 06d781ed69..d3fdcad045 100644
--- a/test/old/testdir/test_findfile.vim
+++ b/test/old/testdir/test_findfile.vim
@@ -1,5 +1,7 @@
" Test findfile() and finddir()
+source check.vim
+
let s:files = [ 'Xfinddir1/foo',
\ 'Xfinddir1/bar',
\ 'Xfinddir1/Xdir2/foo',
@@ -286,4 +288,223 @@ func Test_find_non_existing_path()
let &path = save_path
endfunc
+" Test for 'findexpr'
+func Test_findexpr()
+ CheckUnix
+ call assert_equal('', &findexpr)
+ call writefile(['aFile'], 'Xfindexpr1.c', 'D')
+ call writefile(['bFile'], 'Xfindexpr2.c', 'D')
+ call writefile(['cFile'], 'Xfindexpr3.c', 'D')
+
+ " basic tests
+ func FindExpr1()
+ let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
+ return fnames->copy()->filter('v:val =~? v:fname')
+ endfunc
+
+ set findexpr=FindExpr1()
+ find Xfindexpr3
+ call assert_match('Xfindexpr3.c', @%)
+ bw!
+ 2find Xfind
+ call assert_match('Xfindexpr2.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', @%)
+ 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', @%)
+ call assert_equal(2, tabpagenr())
+ %bw!
+ call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path')
+
+ " Buffer-local option
+ set findexpr=['abc']
+ new
+ setlocal findexpr=['def']
+ find xxxx
+ call assert_equal('def', @%)
+ wincmd w
+ find xxxx
+ call assert_equal('abc', @%)
+ aboveleft new
+ call assert_equal("['abc']", &findexpr)
+ wincmd k
+ aboveleft new
+ call assert_equal("['abc']", &findexpr)
+ %bw!
+
+ " Empty list
+ set findexpr=[]
+ call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path')
+
+ " Error cases
+
+ " Syntax error in the expression
+ set findexpr=FindExpr1{}
+ call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression')
+
+ " Find expression throws an error
+ func FindExpr2()
+ throw 'find error'
+ endfunc
+ set findexpr=FindExpr2()
+ call assert_fails('find Xfindexpr1.c', 'find error')
+
+ " 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 to create a new window from the find expression
+ func FindExpr3()
+ new
+ return ["foo"]
+ endfunc
+ set findexpr=FindExpr3()
+ call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
+
+ " Try to modify the current buffer from the find expression
+ func FindExpr4()
+ 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
+endfunc
+
+" Test for using a script-local function for 'findexpr'
+func Test_findexpr_scriptlocal_func()
+ func! s:FindExprScript()
+ let g:FindExprArg = v:fname
+ return ['xxx']
+ endfunc
+
+ set findexpr=s:FindExprScript()
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ new | only
+ let g:FindExprArg = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ bw!
+
+ set findexpr=<SID>FindExprScript()
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ new | only
+ let g:FindExprArg = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ bw!
+
+ let &findexpr = 's:FindExprScript()'
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ new | only
+ let g:FindExprArg = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ bw!
+
+ let &findexpr = '<SID>FindExprScript()'
+ call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+ new | only
+ let g:FindExprArg = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ 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 = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ 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 = ''
+ find abc
+ call assert_equal('abc', g:FindExprArg)
+ bw!
+
+ set findexpr=
+ delfunc s:FindExprScript
+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']
+
+ " 'findexpr' that accepts a regular expression
+ func FindExprRegexp()
+ return s:fnames->copy()->filter('v:val =~? v:fname')
+ endfunc
+
+ " 'findexpr' that accepts a glob
+ func FindExprGlob()
+ let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname)
+ return s:fnames->copy()->filter('v:val =~? pat')
+ endfunc
+
+ for regexp in [v:true, v:false]
+ let &findexpr = regexp ? 'FindExprRegexp()' : 'FindExprGlob()'
+
+ call feedkeys(":find \<Tab>\<C-B>\"\<CR>", "xt")
+ call assert_equal('"find Xfindexpr1.c', @:)
+
+ call feedkeys(":find Xfind\<Tab>\<Tab>\<C-B>\"\<CR>", "xt")
+ call assert_equal('"find Xfindexpr2.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 feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt")
+ call assert_equal('"find Xfindexpr3.c', @:)
+ call assert_equal(['Xfindexpr3.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 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
+ unlet s:fnames
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_map_functions.vim b/test/old/testdir/test_map_functions.vim
index 8f7c8bae76..5118063b36 100644
--- a/test/old/testdir/test_map_functions.vim
+++ b/test/old/testdir/test_map_functions.vim
@@ -527,6 +527,25 @@ func Test_map_restore_negative_sid()
call delete('Xresult')
endfunc
+" Check that restoring a mapping doesn't remove a mapping whose {rhs} matches
+" the restored mapping's {lhs}.
+func Test_map_restore_with_rhs_match_lhs()
+ nnoremap <F2> <F3>
+ nnoremap <F3> <F4>
+ call assert_equal('<F3>', maparg('<F2>', 'n'))
+ call assert_equal('<F4>', maparg('<F3>', 'n'))
+ let d = maparg('<F3>', 'n', v:false, v:true)
+ nunmap <F3>
+ call assert_equal('<F3>', maparg('<F2>', 'n'))
+ call assert_equal('', maparg('<F3>', 'n'))
+ call mapset(d)
+ call assert_equal('<F3>', maparg('<F2>', 'n'))
+ call assert_equal('<F4>', maparg('<F3>', 'n'))
+
+ nunmap <F2>
+ nunmap <F3>
+endfunc
+
func Test_maplist()
new
func s:ClearMappingsAbbreviations()
diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim
index 487a89e038..7b7163d372 100644
--- a/test/old/testdir/test_modeline.vim
+++ b/test/old/testdir/test_modeline.vim
@@ -217,6 +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('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_options.vim b/test/old/testdir/test_options.vim
index ccc7abe2d8..6902560518 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -1386,7 +1386,8 @@ func Test_local_scrolloff()
call assert_equal(5, &so)
wincmd w
call assert_equal(3, &so)
- setlocal so<
+ "setlocal so<
+ set so<
call assert_equal(5, &so)
setglob so=8
call assert_equal(8, &so)
@@ -1403,7 +1404,8 @@ func Test_local_scrolloff()
call assert_equal(7, &siso)
wincmd w
call assert_equal(3, &siso)
- setlocal siso<
+ "setlocal siso<
+ set siso<
call assert_equal(7, &siso)
setglob siso=4
call assert_equal(4, &siso)
@@ -1557,7 +1559,7 @@ endfunc
" Test for changing options in a sandbox
func Test_opt_sandbox()
- for opt in ['backupdir', 'cdpath', 'exrc']
+ for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr']
call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
endfor
@@ -1595,17 +1597,17 @@ func Test_set_number_global_local_option()
call assert_equal(12, &l:scrolloff)
call assert_equal(12, &scrolloff)
- " :set {option}< set the effective value of {option} to its global value.
- set scrolloff<
- " Nvim: local value is removed
- " call assert_equal(10, &l:scrolloff)
- call assert_equal(-1, &l:scrolloff)
+ " :setlocal {option}< set the effective value of {option} to its global value.
+ "set scrolloff<
+ setlocal scrolloff<
+ call assert_equal(10, &l:scrolloff)
call assert_equal(10, &scrolloff)
- " :setlocal {option}< removes the local value, so that the global value will be used.
+ " :set {option}< removes the local value, so that the global value will be used.
setglobal scrolloff=15
setlocal scrolloff=18
- setlocal scrolloff<
+ "setlocal scrolloff<
+ set scrolloff<
call assert_equal(-1, &l:scrolloff)
call assert_equal(15, &scrolloff)
@@ -1620,17 +1622,17 @@ func Test_set_boolean_global_local_option()
call assert_equal(0, &l:autoread)
call assert_equal(0, &autoread)
- " :set {option}< set the effective value of {option} to its global value.
- set autoread<
- " Nvim: local value is removed
- " call assert_equal(1, &l:autoread)
- call assert_equal(-1, &l:autoread)
+ " :setlocal {option}< set the effective value of {option} to its global value.
+ "set autoread<
+ setlocal autoread<
+ call assert_equal(1, &l:autoread)
call assert_equal(1, &autoread)
- " :setlocal {option}< removes the local value, so that the global value will be used.
+ " :set {option}< removes the local value, so that the global value will be used.
setglobal noautoread
setlocal autoread
- setlocal autoread<
+ "setlocal autoread<
+ set autoread<
call assert_equal(-1, &l:autoread)
call assert_equal(0, &autoread)
diff --git a/test/old/testdir/test_undo.vim b/test/old/testdir/test_undo.vim
index a207f4f4e0..d876277850 100644
--- a/test/old/testdir/test_undo.vim
+++ b/test/old/testdir/test_undo.vim
@@ -187,7 +187,8 @@ func Test_global_local_undolevels()
" Resetting the local 'undolevels' value to use the global value
setlocal undolevels=5
- setlocal undolevels<
+ "setlocal undolevels<
+ set undolevels<
call assert_equal(-123456, &l:undolevels)
" Drop created windows