aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/api.txt6
-rw-r--r--runtime/doc/builtin.txt18
-rw-r--r--runtime/doc/cmdline.txt4
-rw-r--r--runtime/doc/lsp.txt4
-rw-r--r--runtime/doc/lua.txt7
-rw-r--r--runtime/doc/message.txt3
-rw-r--r--runtime/doc/news.txt11
-rw-r--r--runtime/doc/options.txt28
-rw-r--r--runtime/doc/terminal.txt1
-rw-r--r--runtime/doc/treesitter.txt27
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/filetype.lua4
-rw-r--r--runtime/lua/vim/_defaults.lua1
-rw-r--r--runtime/lua/vim/_inspector.lua7
-rw-r--r--runtime/lua/vim/_meta/api.lua6
-rw-r--r--runtime/lua/vim/_meta/json.lua5
-rw-r--r--runtime/lua/vim/_meta/options.lua34
-rw-r--r--runtime/lua/vim/lsp.lua84
-rw-r--r--runtime/lua/vim/lsp/_folding_range.lua4
-rw-r--r--runtime/lua/vim/lsp/client.lua21
-rw-r--r--runtime/lua/vim/lsp/util.lua5
-rw-r--r--runtime/lua/vim/treesitter/query.lua53
-rw-r--r--runtime/optwin.vim4
-rwxr-xr-xscripts/gen_eval_files.lua25
-rw-r--r--src/cjson/lua_cjson.c121
-rw-r--r--src/nvim/api/autocmd.c6
-rw-r--r--src/nvim/autocmd.c6
-rw-r--r--src/nvim/autocmd_defs.h1
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/ex_docmd.c14
-rw-r--r--src/nvim/generators/gen_options.lua6
-rw-r--r--src/nvim/insexpand.c34
-rw-r--r--src/nvim/message.c221
-rw-r--r--src/nvim/normal.c4
-rw-r--r--src/nvim/option.c14
-rw-r--r--src/nvim/option_vars.h2
-rw-r--r--src/nvim/options.lua49
-rw-r--r--src/nvim/optionstr.c18
-rw-r--r--test/functional/api/autocmd_spec.lua50
-rw-r--r--test/functional/editor/completion_spec.lua15
-rw-r--r--test/functional/legacy/messages_spec.lua39
-rw-r--r--test/functional/lua/filetype_spec.lua2
-rw-r--r--test/functional/lua/json_spec.lua39
-rw-r--r--test/functional/plugin/lsp/handler_spec.lua42
-rw-r--r--test/functional/treesitter/parser_spec.lua132
-rw-r--r--test/functional/treesitter/testutil.lua25
-rw-r--r--test/old/testdir/gen_opt_test.vim6
-rw-r--r--test/old/testdir/test_cmdline.vim26
-rw-r--r--test/old/testdir/test_messages.vim47
-rw-r--r--test/old/testdir/test_options.vim7
50 files changed, 876 insertions, 415 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 9cb8f72348..cb3b2a3f77 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -3494,9 +3494,9 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()*
• event: (string) name of the triggered event
|autocmd-events|
• group: (number|nil) autocommand group id, if any
- • match: (string) expanded value of <amatch>
- • buf: (number) expanded value of <abuf>
- • file: (string) expanded value of <afile>
+ • file: (string) <afile> (not expanded to a full path)
+ • match: (string) <amatch> (expanded to a full path)
+ • buf: (number) <abuf>
• data: (any) arbitrary data passed from
|nvim_exec_autocmds()| *event-data*
• command (string) optional: Vim command to execute on event.
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 304e63fd95..f2942b959b 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -2997,7 +2997,7 @@ getbufline({buf}, {lnum} [, {end}]) *getbufline()*
Parameters: ~
• {buf} (`integer|string`)
• {lnum} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -3635,7 +3635,7 @@ getline({lnum} [, {end}]) *getline()*
Parameters: ~
• {lnum} (`integer|string`)
- • {end_} (`nil|false?`)
+ • {end} (`nil|false?`)
Return: ~
(`string`)
@@ -6292,7 +6292,7 @@ matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()*
• {buf} (`string|integer`)
• {pat} (`string`)
• {lnum} (`string|integer`)
- • {end_} (`string|integer`)
+ • {end} (`string|integer`)
• {dict} (`table?`)
Return: ~
@@ -7786,7 +7786,7 @@ reltime({start}, {end})
Parameters: ~
• {start} (`any?`)
- • {end_} (`any?`)
+ • {end} (`any?`)
Return: ~
(`any`)
@@ -7847,7 +7847,7 @@ remove({list}, {idx}, {end})
Parameters: ~
• {list} (`any[]`)
• {idx} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -7869,7 +7869,7 @@ remove({blob}, {idx}, {end})
Parameters: ~
• {blob} (`any`)
• {idx} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -8500,7 +8500,7 @@ searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeo
Parameters: ~
• {start} (`string`)
• {middle} (`string`)
- • {end_} (`string`)
+ • {end} (`string`)
• {flags} (`string?`)
• {skip} (`string|function?`)
• {stopline} (`integer?`)
@@ -8524,7 +8524,7 @@ searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {ti
Parameters: ~
• {start} (`string`)
• {middle} (`string`)
- • {end_} (`string`)
+ • {end} (`string`)
• {flags} (`string?`)
• {skip} (`string|function?`)
• {stopline} (`integer?`)
@@ -9840,7 +9840,7 @@ slice({expr}, {start} [, {end}]) *slice()*
Parameters: ~
• {expr} (`any`)
• {start} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 7967e2ce1a..f6a3e95b35 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -41,15 +41,19 @@ thus you cannot edit beyond that.
The command-lines that you enter are remembered in a history table. You can
recall them with the up and down cursor keys. There are actually five
history tables:
+
- one for ':' commands
- one for search strings
- one for expressions
- one for input lines, typed for the |input()| function.
- one for debug mode commands
+
These are completely separate. Each history can only be accessed when
entering the same type of line.
Use the 'history' option to set the number of lines that are remembered.
+
Notes:
+
- When you enter a command-line that is exactly the same as an older one, the
old one is removed (to avoid repeated commands moving older commands out of
the history).
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 2654d7f14f..1607f3492c 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -872,7 +872,9 @@ start({config}, {opts}) *vim.lsp.start()*
(`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
Predicate used to decide if a client should be re-used.
Used on all running clients. The default implementation
- re-uses a client if name and root_dir matches.
+ re-uses a client if it has the same name and if the given
+ workspace folders (or root_dir) are all included in the
+ client's workspace folders.
• {bufnr}? (`integer`) Buffer handle to attach to if
starting or re-using a client (0 for current).
• {attach}? (`boolean`) Whether to attach the client to a
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 48fa595a53..80808abd51 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -811,11 +811,14 @@ vim.json.decode({str}, {opts}) *vim.json.decode()*
Return: ~
(`any`)
-vim.json.encode({obj}) *vim.json.encode()*
+vim.json.encode({obj}, {opts}) *vim.json.encode()*
Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
Parameters: ~
- • {obj} (`any`)
+ • {obj} (`any`)
+ • {opts} (`table<string,any>?`) Options table with keys:
+ • escape_slash: (boolean) (default false) When true, escapes
+ `/` character in JSON strings
Return: ~
(`string`)
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index ac151811c2..d6f5aeb354 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -27,7 +27,7 @@ depends on the 'shortmess' option.
Clear messages, keeping only the {count} most
recent ones.
-The number of remembered messages is fixed at 200.
+The number of remembered messages is determined by the 'messagesopt' option.
*g<*
The "g<" command can be used to see the last page of previous command output.
@@ -789,6 +789,7 @@ If you accidentally hit <Enter> or <Space> and you want to see the displayed
text then use |g<|. This only works when 'more' is set.
To reduce the number of hit-enter prompts:
+- Set 'messagesopt'.
- Set 'cmdheight' to 2 or higher.
- Add flags to 'shortmess'.
- Reset 'showcmd' and/or 'ruler'.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 58fe2e02e8..3ce73135ba 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -25,6 +25,7 @@ EXPERIMENTS
OPTIONS
• 'jumpoptions' flag "unload" has been renamed to "clean".
+• The `msghistory` option has been removed in favor of 'messagesopt'.
==============================================================================
BREAKING CHANGES *news-breaking*
@@ -58,8 +59,9 @@ DEFAULTS
• |]d-default| and |[d-default| accept a count.
• |[D-default| and |]D-default| jump to the first and last diagnostic in the
current buffer, respectively.
-• 'number', 'relativenumber', and 'signcolumn' are disabled in |terminal|
- buffers. See |terminal-config| for an example of changing these defaults.
+• 'number', 'relativenumber', 'signcolumn', and 'foldcolumn' are disabled in
+ |terminal| buffers. See |terminal-config| for an example of changing these defaults.
+• |vim.json.encode()| no longer escapes the forward slash symbol by default
DIAGNOSTICS
@@ -169,6 +171,7 @@ The following new features were added.
API
• |nvim__ns_set()| can set properties for a namespace
+• |vim.json.encode()| has an option to enable forward slash escaping
DEFAULTS
@@ -246,7 +249,7 @@ LUA
OPTIONS
• 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|.
-• 'msghistory' controls maximum number of messages to remember.
+• 'messagesopt' configures |:messages| and |hit-enter| prompt.
• 'tabclose' controls which tab page to focus when closing a tab page.
PERFORMANCE
@@ -280,6 +283,8 @@ TREESITTER
• |LanguageTree:node_for_range()| gets anonymous and named nodes for a range
• |vim.treesitter.get_node()| now takes an option `include_anonymous`, default
false, which allows it to return anonymous nodes as well as named nodes.
+• |treesitter-directive-trim!| can trim all whitespace (not just empty lines)
+ from both sides of a node.
TUI
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 9782846be5..0db7b0f03c 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -3197,7 +3197,7 @@ A jump table for the options with a short description can be found at |Q_op|.
global
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing| and 'msghistory' for
+ each of these histories (see |cmdline-editing| and 'messagesopt' for
the number of messages to remember).
The maximum value is 10000.
@@ -4045,6 +4045,25 @@ A jump table for the options with a short description can be found at |Q_op|.
generated from a list of items, e.g., the Buffers menu. Changing this
option has no direct effect, the menu must be refreshed first.
+ *'messagesopt'* *'mopt'*
+'messagesopt' 'mopt' string (default "hit-enter,history:500")
+ global
+ Option settings when outputting messages. It can consist of the
+ following items. Items must be separated by a comma.
+
+ hit-enter Use |hit-enter| prompt when the message is longer than
+ 'cmdheight' size.
+
+ wait:{n} Ignored when "hit-enter" is present. Instead of using
+ |hit-enter| prompt, will simply wait for {n}
+ milliseconds so the user has a chance to read the
+ message, use 0 to disable sleep (but then the user may
+ miss an important message).
+
+ history:{n} Determines how many entries are remembered in the
+ |:messages| history. The maximum value is 10000.
+ Setting it to zero clears the message history.
+
*'mkspellmem'* *'msm'*
'mkspellmem' 'msm' string (default "460000,2000,500")
global
@@ -4290,13 +4309,6 @@ A jump table for the options with a short description can be found at |Q_op|.
Defines the maximum time in msec between two mouse clicks for the
second click to be recognized as a multi click.
- *'msghistory'* *'mhi'*
-'msghistory' 'mhi' number (default 500)
- global
- Determines how many entries are remembered in the |:messages| history.
- The maximum value is 10000.
- Setting it to zero clears the message history.
-
*'nrformats'* *'nf'*
'nrformats' 'nf' string (default "bin,hex")
local to buffer
diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt
index 27586c38a7..5a1421f430 100644
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -111,6 +111,7 @@ global configuration.
- 'number' is disabled
- 'relativenumber' is disabled
- 'signcolumn' is set to "no"
+- 'foldcolumn' is set to "0"
You can change the defaults with a TermOpen autocommand: >vim
au TermOpen * setlocal list
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 5fc6429f7a..877d90a3b7 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -245,15 +245,32 @@ The following directives are built in:
(#gsub! @_node ".*%.(.*)" "%1")
<
`trim!` *treesitter-directive-trim!*
- Trim blank lines from the end of the node. This will set a new
- `metadata[capture_id].range`.
+ Trims whitespace from the node. Sets a new
+ `metadata[capture_id].range`. Takes a capture ID and, optionally, four
+ integers to customize trimming behavior (`1` meaning trim, `0` meaning
+ don't trim). When only given a capture ID, trims blank lines (lines
+ that contain only whitespace, or are empty) from the end of the node
+ (for backwards compatibility). Can trim all whitespace from both sides
+ of the node if parameters are given.
+ Examples: >query
+ ; only trim blank lines from the end of the node
+ ; (equivalent to (#trim! @fold 0 0 1 0))
+ (#trim! @fold)
+
+ ; trim blank lines from both sides of the node
+ (#trim! @fold 1 0 1 0)
+
+ ; trim all whitespace around the node
+ (#trim! @fold 1 1 1 1)
+<
Parameters: ~
{capture_id}
+ {trim_start_linewise}
+ {trim_start_charwise}
+ {trim_end_linewise} (default `1` if only given {capture_id})
+ {trim_end_charwise}
- Example: >query
- (#trim! @fold)
-<
Further directives can be added via |vim.treesitter.query.add_directive()|.
Use |vim.treesitter.query.list_directives()| to list all available directives.
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 8d2d672ce1..7021e1d3e2 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -189,6 +189,7 @@ nvim_terminal:
- 'nonumber'
- 'norelativenumber'
- 'signcolumn' set to "no"
+ - 'foldcolumn' set to "0"
- 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in
place of |hl-StatusLine| and |hl-StatusLineNC|
diff --git a/runtime/filetype.lua b/runtime/filetype.lua
index 797033da06..730991a00c 100644
--- a/runtime/filetype.lua
+++ b/runtime/filetype.lua
@@ -13,8 +13,8 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, {
end
local ft, on_detect = vim.filetype.match({
-- The unexpanded file name is needed here. #27914
- -- Neither args.file nor args.match are guaranteed to be unexpanded.
- filename = vim.fn.bufname(args.buf),
+ -- However, bufname() can't be used, as it doesn't work with :doautocmd. #31306
+ filename = args.file,
buf = args.buf,
})
if not ft then
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index f891c3baa4..0b8a54e957 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -495,6 +495,7 @@ do
vim.wo[0][0].number = false
vim.wo[0][0].relativenumber = false
vim.wo[0][0].signcolumn = 'no'
+ vim.wo[0][0].foldcolumn = '0'
-- This is gross. Proper list options support when?
local winhl = vim.o.winhighlight
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index f4e41a31e5..520c437906 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -191,7 +191,12 @@ function vim.show_pos(bufnr, row, col, filter)
append('Treesitter', 'Title')
nl()
for _, capture in ipairs(items.treesitter) do
- item(capture, capture.lang)
+ item(
+ capture,
+ capture.metadata.priority
+ and string.format('%s priority: %d', capture.lang, capture.metadata.priority)
+ or capture.lang
+ )
end
nl()
end
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index d74ee11b46..b2385197bd 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -963,9 +963,9 @@ function vim.api.nvim_create_augroup(name, opts) end
--- - id: (number) autocommand id
--- - event: (string) name of the triggered event `autocmd-events`
--- - group: (number|nil) autocommand group id, if any
---- - match: (string) expanded value of [<amatch>]
---- - buf: (number) expanded value of [<abuf>]
---- - file: (string) expanded value of [<afile>]
+--- - file: (string) [<afile>] (not expanded to a full path)
+--- - match: (string) [<amatch>] (expanded to a full path)
+--- - buf: (number) [<abuf>]
--- - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
--- - command (string) optional: Vim command to execute on event. Cannot be used with
--- {callback}
diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index 07d89aafc8..1a7e87db9c 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -35,5 +35,8 @@ function vim.json.decode(str, opts) end
--- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
---@param obj any
+---@param opts? table<string,any> Options table with keys:
+--- - escape_slash: (boolean) (default false) When true, escapes `/`
+--- character in JSON strings
---@return string
-function vim.json.encode(obj) end
+function vim.json.encode(obj, opts) end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 01d5235d94..59d270e640 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -3016,7 +3016,7 @@ vim.go.hid = vim.go.hidden
--- A history of ":" commands, and a history of previous search patterns
--- is remembered. This option decides how many entries may be stored in
---- each of these histories (see `cmdline-editing` and 'msghistory' for
+--- each of these histories (see `cmdline-editing` and 'messagesopt' for
--- the number of messages to remember).
--- The maximum value is 10000.
---
@@ -4084,6 +4084,28 @@ vim.o.mis = vim.o.menuitems
vim.go.menuitems = vim.o.menuitems
vim.go.mis = vim.go.menuitems
+--- Option settings when outputting messages. It can consist of the
+--- following items. Items must be separated by a comma.
+---
+--- hit-enter Use `hit-enter` prompt when the message is longer than
+--- 'cmdheight' size.
+---
+--- wait:{n} Ignored when "hit-enter" is present. Instead of using
+--- `hit-enter` prompt, will simply wait for {n}
+--- milliseconds so the user has a chance to read the
+--- message, use 0 to disable sleep (but then the user may
+--- miss an important message).
+---
+--- history:{n} Determines how many entries are remembered in the
+--- `:messages` history. The maximum value is 10000.
+--- Setting it to zero clears the message history.
+---
+--- @type string
+vim.o.messagesopt = "hit-enter,history:500"
+vim.o.mopt = vim.o.messagesopt
+vim.go.messagesopt = vim.o.messagesopt
+vim.go.mopt = vim.go.messagesopt
+
--- Parameters for `:mkspell`. This tunes when to start compressing the
--- word tree. Compression can be slow when there are many words, but
--- it's needed to avoid running out of memory. The amount of memory used
@@ -4379,16 +4401,6 @@ vim.o.mouset = vim.o.mousetime
vim.go.mousetime = vim.o.mousetime
vim.go.mouset = vim.go.mousetime
---- Determines how many entries are remembered in the `:messages` history.
---- The maximum value is 10000.
---- Setting it to zero clears the message history.
----
---- @type integer
-vim.o.msghistory = 500
-vim.o.mhi = vim.o.msghistory
-vim.go.msghistory = vim.o.msghistory
-vim.go.mhi = vim.go.msghistory
-
--- This defines what bases Vim will consider for numbers when using the
--- CTRL-A and CTRL-X commands for adding to and subtracting from a number
--- respectively; see `CTRL-A` for more info on these commands.
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 4717d7995a..e1946816da 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -114,6 +114,22 @@ function lsp._unsupported_method(method)
return msg
end
+---@private
+---@param workspace_folders string|lsp.WorkspaceFolder[]?
+---@return lsp.WorkspaceFolder[]?
+function lsp._get_workspace_folders(workspace_folders)
+ if type(workspace_folders) == 'table' then
+ return workspace_folders
+ elseif type(workspace_folders) == 'string' then
+ return {
+ {
+ uri = vim.uri_from_fname(workspace_folders),
+ name = workspace_folders,
+ },
+ }
+ end
+end
+
local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
local format_line_ending = {
@@ -196,19 +212,24 @@ local function reuse_client_default(client, config)
return false
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 root == dir.uri then
- return true
+ local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir)
+ or {}
+ local config_folders_included = 0
+
+ if not next(config_folders) then
+ return false
+ end
+
+ for _, config_folder in ipairs(config_folders) do
+ for _, client_folder in ipairs(client.workspace_folders) do
+ if config_folder.uri == client_folder.uri then
+ config_folders_included = config_folders_included + 1
+ break
end
end
end
- -- TODO(lewis6991): also check config.workspace_folders
-
- return false
+ return config_folders_included == #config_folders
end
--- Reset defaults set by `set_defaults`.
@@ -311,9 +332,10 @@ end
--- @inlinedoc
---
--- Predicate used to decide if a client should be re-used. Used on all
---- running clients. The default implementation re-uses a client if name and
---- root_dir matches.
---- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean)
+--- running clients. The default implementation re-uses a client if it has the
+--- same name and if the given workspace folders (or root_dir) are all included
+--- in the client's workspace folders.
+--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
---
--- Buffer handle to attach to if starting or re-using a client (0 for current).
--- @field bufnr? integer
@@ -1257,44 +1279,6 @@ function lsp.with(handler, override_config)
end
end
---- Helper function to use when implementing a handler.
---- This will check that all of the keys in the user configuration
---- are valid keys and make sense to include for this handler.
----
---- Will error on invalid keys (i.e. keys that do not exist in the options)
---- @param name string
---- @param options table<string,any>
---- @param user_config table<string,any>
-function lsp._with_extend(name, options, user_config)
- user_config = user_config or {}
-
- local resulting_config = {} --- @type table<string,any>
- for k, v in pairs(user_config) do
- if options[k] == nil then
- error(
- debug.traceback(
- string.format(
- 'Invalid option for `%s`: %s. Valid options are:\n%s',
- name,
- k,
- vim.inspect(vim.tbl_keys(options))
- )
- )
- )
- end
-
- resulting_config[k] = v
- end
-
- for k, v in pairs(options) do
- if resulting_config[k] == nil then
- resulting_config[k] = v
- end
- end
-
- return resulting_config
-end
-
--- Registry for client side commands.
--- This is an extension point for plugins to handle custom commands which are
--- not part of the core language server protocol specification.
diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
index 6a445017a3..2f1767aaf5 100644
--- a/runtime/lua/vim/lsp/_folding_range.lua
+++ b/runtime/lua/vim/lsp/_folding_range.lua
@@ -271,7 +271,9 @@ local function setup(bufnr)
buffer = bufnr,
callback = function(args)
local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
- request(bufnr, client)
+ if client:supports_method(vim.lsp.protocol.Methods.textDocument_foldingRange, bufnr) then
+ request(bufnr, client)
+ end
end,
})
api.nvim_create_autocmd('LspNotify', {
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index a83d75bf75..7eb023da39 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -365,21 +365,6 @@ local function get_name(id, config)
return tostring(id)
end
---- @param workspace_folders string|lsp.WorkspaceFolder[]?
---- @return lsp.WorkspaceFolder[]?
-local function get_workspace_folders(workspace_folders)
- if type(workspace_folders) == 'table' then
- return workspace_folders
- elseif type(workspace_folders) == 'string' then
- return {
- {
- uri = vim.uri_from_fname(workspace_folders),
- name = workspace_folders,
- },
- }
- end
-end
-
--- @generic T
--- @param x elem_or_list<T>?
--- @return T[]
@@ -417,7 +402,7 @@ function Client.create(config)
flags = config.flags or {},
get_language_id = config.get_language_id or default_get_language_id,
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
- workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir),
+ workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
root_dir = config.root_dir,
_before_init_cb = config.before_init,
_on_init_cbs = ensure_list(config.on_init),
@@ -1174,7 +1159,7 @@ function Client:_add_workspace_folder(dir)
end
end
- local wf = assert(get_workspace_folders(dir))
+ local wf = assert(lsp._get_workspace_folders(dir))
self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = wf, removed = {} },
@@ -1189,7 +1174,7 @@ end
--- Remove a directory to the workspace folders.
--- @param dir string?
function Client:_remove_workspace_folder(dir)
- local wf = assert(get_workspace_folders(dir))
+ local wf = assert(lsp._get_workspace_folders(dir))
self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = {}, removed = wf },
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ced8aa5745..5bf436f2ca 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -2162,6 +2162,11 @@ function M._refresh(method, opts)
local first = vim.fn.line('w0', window)
local last = vim.fn.line('w$', window)
for _, client in ipairs(clients) do
+ for rid, req in pairs(client.requests) do
+ if req.method == method and req.type == 'pending' and req.bufnr == bufnr then
+ client:cancel_request(rid)
+ end
+ end
client:request(method, {
textDocument = textDocument,
range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 1677e8d364..dbe3d54c2f 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -572,13 +572,17 @@ local directive_handlers = {
metadata[id].text = text:gsub(pattern, replacement)
end,
- -- Trim blank lines from end of the node
- -- Example: (#trim! @fold)
- -- TODO(clason): generalize to arbitrary whitespace removal
+ -- Trim whitespace from both sides of the node
+ -- Example: (#trim! @fold 1 1 1 1)
['trim!'] = function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
assert(type(capture_id) == 'number')
+ local trim_start_lines = pred[3] == '1'
+ local trim_start_cols = pred[4] == '1'
+ local trim_end_lines = pred[5] == '1' or not pred[3] -- default true for backwards compatibility
+ local trim_end_cols = pred[6] == '1'
+
local nodes = match[capture_id]
if not nodes or #nodes == 0 then
return
@@ -588,20 +592,45 @@ local directive_handlers = {
local start_row, start_col, end_row, end_col = node:range()
- -- Don't trim if region ends in middle of a line
- if end_col ~= 0 then
- return
+ local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n')
+ if end_col == 0 then
+ -- get_node_text() will ignore the last line if the node ends at column 0
+ node_text[#node_text + 1] = ''
end
- while end_row >= start_row do
- -- As we only care when end_col == 0, always inspect one line above end_row.
- local end_line = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
+ local end_idx = #node_text
+ local start_idx = 1
- if end_line ~= '' then
- break
+ if trim_end_lines then
+ while end_idx > 0 and node_text[end_idx]:find('^%s*$') do
+ end_idx = end_idx - 1
+ end_row = end_row - 1
+ -- set the end position to the last column of the next line, or 0 if we just trimmed the
+ -- last line
+ end_col = end_idx > 0 and #node_text[end_idx] or 0
end
+ end
+ if trim_end_cols then
+ if end_idx == 0 then
+ end_row = start_row
+ end_col = start_col
+ else
+ local whitespace_start = node_text[end_idx]:find('(%s*)$')
+ end_col = (whitespace_start - 1) + (end_idx == 1 and start_col or 0)
+ end
+ end
- end_row = end_row - 1
+ if trim_start_lines then
+ while start_idx <= end_idx and node_text[start_idx]:find('^%s*$') do
+ start_idx = start_idx + 1
+ start_row = start_row + 1
+ start_col = 0
+ end
+ end
+ if trim_start_cols and node_text[start_idx] then
+ local _, whitespace_end = node_text[start_idx]:find('^(%s*)')
+ whitespace_end = whitespace_end or 0
+ start_col = (start_idx == 1 and start_col or 0) + whitespace_end
end
-- If this produces an invalid range, we just skip it.
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 48a4bd2816..923f55f8fa 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -626,8 +626,8 @@ call <SID>AddOption("terse", gettext("add 's' flag in 'shortmess' (don't show se
call <SID>BinOptionG("terse", &terse)
call <SID>AddOption("shortmess", gettext("list of flags to make messages shorter"))
call <SID>OptionG("shm", &shm)
-call <SID>AddOption("msghistory", gettext("how many messages are remembered"))
-call append("$", " \tset mhi=" . &mhi)
+call <SID>AddOption("messagesopt", gettext("Option settings when outputting messages"))
+call <SID>OptionG("mopt", &mopt)
call <SID>AddOption("showcmd", gettext("show (partial) command keys in location given by 'showcmdloc'"))
let &sc = s:old_sc
call <SID>BinOptionG("sc", &sc)
diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua
index 498bf6b26f..de9df5054f 100755
--- a/scripts/gen_eval_files.lua
+++ b/scripts/gen_eval_files.lua
@@ -134,6 +134,15 @@ local API_TYPES = {
void = '',
}
+--- @param s string
+--- @return string
+local function luaescape(s)
+ if LUA_KEYWORDS[s] then
+ return s .. '_'
+ end
+ return s
+end
+
--- @param x string
--- @param sep? string
--- @return string[]
@@ -208,7 +217,7 @@ local function render_fun_sig(f, params)
--- @param v [string,string]
--- @return string
function(v)
- return v[1]
+ return luaescape(v[1])
end,
params
),
@@ -224,7 +233,6 @@ local function render_fun_sig(f, params)
end
--- Uniquify names
---- Fix any names that are lua keywords
--- @param params [string,string,string][]
--- @return [string,string,string][]
local function process_params(params)
@@ -232,9 +240,6 @@ local function process_params(params)
local sfx = 1
for _, p in ipairs(params) do
- if LUA_KEYWORDS[p[1]] then
- p[1] = p[1] .. '_'
- end
if seen[p[1]] then
p[1] = p[1] .. sfx
sfx = sfx + 1
@@ -389,10 +394,10 @@ local function render_api_meta(_f, fun, write)
local param_names = {} --- @type string[]
local params = process_params(fun.params)
for _, p in ipairs(params) do
- param_names[#param_names + 1] = p[1]
- local pdesc = p[3]
+ local pname, ptype, pdesc = luaescape(p[1]), p[2], p[3]
+ param_names[#param_names + 1] = pname
if pdesc then
- local s = '--- @param ' .. p[1] .. ' ' .. p[2] .. ' '
+ local s = '--- @param ' .. pname .. ' ' .. ptype .. ' '
local pdesc_a = split(vim.trim(norm_text(pdesc)))
write(s .. pdesc_a[1])
for i = 2, #pdesc_a do
@@ -402,7 +407,7 @@ local function render_api_meta(_f, fun, write)
write('--- ' .. pdesc_a[i])
end
else
- write('--- @param ' .. p[1] .. ' ' .. p[2])
+ write('--- @param ' .. pname .. ' ' .. ptype)
end
end
@@ -494,7 +499,7 @@ local function render_eval_meta(f, fun, write)
local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0
for i, param in ipairs(params) do
- local pname, ptype = param[1], param[2]
+ local pname, ptype = luaescape(param[1]), param[2]
local optional = (pname ~= '...' and i > req_args) and '?' or ''
write(fmt('--- @param %s%s %s', pname, optional, ptype))
end
diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c
index 254355e5a2..e4bd0bcf6b 100644
--- a/src/cjson/lua_cjson.c
+++ b/src/cjson/lua_cjson.c
@@ -173,6 +173,16 @@ typedef struct {
} json_config_t;
typedef struct {
+ const char **char2escape[256];
+} json_encode_options_t;
+
+typedef struct {
+ json_config_t *cfg;
+ json_encode_options_t *options;
+ strbuf_t *json;
+} json_encode_t;
+
+typedef struct {
/* convert null in json objects to lua nil instead of vim.NIL */
bool luanil_object;
/* convert null in json arrays to lua nil instead of vim.NIL */
@@ -209,7 +219,7 @@ static const char *char2escape[256] = {
"\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f",
NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -555,11 +565,11 @@ static void json_create_config(lua_State *l)
/* ===== ENCODING ===== */
-static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,
+static void json_encode_exception(lua_State *l, json_encode_t *ctx, int lindex,
const char *reason)
{
- if (!cfg->encode_keep_buffer)
- strbuf_free(json);
+ if (!ctx->cfg->encode_keep_buffer)
+ strbuf_free(ctx->json);
luaL_error(l, "Cannot serialise %s: %s",
lua_typename(l, lua_type(l, lindex)), reason);
}
@@ -570,12 +580,13 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js
* - String (Lua stack index)
*
* Returns nothing. Doesn't remove string from Lua stack */
-static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
+static void json_append_string(lua_State *l, json_encode_t *ctx, int lindex)
{
const char *escstr;
unsigned i;
const char *str;
size_t len;
+ strbuf_t *json = ctx->json;
str = lua_tolstring(l, lindex, &len);
@@ -587,7 +598,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
strbuf_append_char_unsafe(json, '\"');
for (i = 0; i < len; i++) {
- escstr = char2escape[(unsigned char)str[i]];
+ escstr = (*ctx->options->char2escape)[(unsigned char)str[i]];
if (escstr)
strbuf_append_string(json, escstr);
else
@@ -600,11 +611,12 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
* -1 object (not a pure array)
* >=0 elements in array
*/
-static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
+static int lua_array_length(lua_State *l, json_encode_t *ctx)
{
double k;
int max;
int items;
+ json_config_t *cfg = ctx->cfg;
max = 0;
items = 0;
@@ -635,7 +647,7 @@ static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
max > items * cfg->encode_sparse_ratio &&
max > cfg->encode_sparse_safe) {
if (!cfg->encode_sparse_convert)
- json_encode_exception(l, cfg, json, -1, "excessively sparse array");
+ json_encode_exception(l, ctx, -1, "excessively sparse array");
return -1;
}
@@ -666,17 +678,18 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
current_depth);
}
-static void json_append_data(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json);
+static void json_append_data(lua_State *l, json_encode_t *cfg,
+ int current_depth);
/* json_append_array args:
* - lua_State
* - JSON strbuf
* - Size of passwd Lua array (top of stack) */
-static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth,
- strbuf_t *json, int array_length)
+static void json_append_array(lua_State *l, json_encode_t *ctx, int current_depth,
+ int array_length)
{
int comma, i;
+ strbuf_t *json = ctx->json;
strbuf_append_char(json, '[');
@@ -688,23 +701,25 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
comma = 1;
lua_rawgeti(l, -1, i);
- json_append_data(l, cfg, current_depth, json);
+ json_append_data(l, ctx, current_depth);
lua_pop(l, 1);
}
strbuf_append_char(json, ']');
}
-static void json_append_number(lua_State *l, json_config_t *cfg,
- strbuf_t *json, int lindex)
+static void json_append_number(lua_State *l, json_encode_t *ctx,
+ int lindex)
{
double num = lua_tonumber(l, lindex);
int len;
+ json_config_t *cfg = ctx->cfg;
+ strbuf_t *json = ctx->json;
if (cfg->encode_invalid_numbers == 0) {
/* Prevent encoding invalid numbers */
if (isinf(num) || isnan(num))
- json_encode_exception(l, cfg, json, lindex,
+ json_encode_exception(l, ctx, lindex,
"must not be NaN or Infinity");
} else if (cfg->encode_invalid_numbers == 1) {
/* Encode NaN/Infinity separately to ensure Javascript compatible
@@ -733,10 +748,11 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
strbuf_extend_length(json, len);
}
-static void json_append_object(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json)
+static void json_append_object(lua_State *l, json_encode_t *ctx,
+ int current_depth)
{
int comma, keytype;
+ strbuf_t *json = ctx->json;
/* Object */
strbuf_append_char(json, '{');
@@ -754,19 +770,19 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
keytype = lua_type(l, -2);
if (keytype == LUA_TNUMBER) {
strbuf_append_char(json, '"');
- json_append_number(l, cfg, json, -2);
+ json_append_number(l, ctx, -2);
strbuf_append_mem(json, "\":", 2);
} else if (keytype == LUA_TSTRING) {
- json_append_string(l, json, -2);
+ json_append_string(l, ctx, -2);
strbuf_append_char(json, ':');
} else {
- json_encode_exception(l, cfg, json, -2,
+ json_encode_exception(l, ctx, -2,
"table key must be a number or string");
/* never returns */
}
/* table, key, value */
- json_append_data(l, cfg, current_depth, json);
+ json_append_data(l, ctx, current_depth);
lua_pop(l, 1);
/* table, key */
}
@@ -775,20 +791,22 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
}
/* Serialise Lua data into JSON string. */
-static void json_append_data(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json)
+static void json_append_data(lua_State *l, json_encode_t *ctx,
+ int current_depth)
{
int len;
int as_array = 0;
int as_empty_dict = 0;
int has_metatable;
+ json_config_t *cfg = ctx->cfg;
+ strbuf_t *json = ctx->json;
switch (lua_type(l, -1)) {
case LUA_TSTRING:
- json_append_string(l, json, -1);
+ json_append_string(l, ctx, -1);
break;
case LUA_TNUMBER:
- json_append_number(l, cfg, json, -1);
+ json_append_number(l, ctx, -1);
break;
case LUA_TBOOLEAN:
if (lua_toboolean(l, -1))
@@ -818,12 +836,12 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
if (as_array) {
len = lua_objlen(l, -1);
- json_append_array(l, cfg, current_depth, json, len);
+ json_append_array(l, ctx, current_depth, len);
} else {
- len = lua_array_length(l, cfg, json);
+ len = lua_array_length(l, ctx);
if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object && !as_empty_dict)) {
- json_append_array(l, cfg, current_depth, json, len);
+ json_append_array(l, ctx, current_depth, len);
} else {
if (has_metatable) {
lua_getmetatable(l, -1);
@@ -833,11 +851,11 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
as_array = lua_rawequal(l, -1, -2);
lua_pop(l, 2); /* pop pointer + metatable */
if (as_array) {
- json_append_array(l, cfg, current_depth, json, 0);
+ json_append_array(l, ctx, current_depth, 0);
break;
}
}
- json_append_object(l, cfg, current_depth, json);
+ json_append_object(l, ctx, current_depth);
}
}
break;
@@ -846,7 +864,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
break;
case LUA_TLIGHTUSERDATA:
if (lua_touserdata(l, -1) == &json_array) {
- json_append_array(l, cfg, current_depth, json, 0);
+ json_append_array(l, ctx, current_depth, 0);
}
break;
case LUA_TUSERDATA:
@@ -862,7 +880,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
default:
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
* and LUA_TLIGHTUSERDATA) cannot be serialised */
- json_encode_exception(l, cfg, json, -1, "type not supported");
+ json_encode_exception(l, ctx, -1, "type not supported");
/* never returns */
}
}
@@ -870,12 +888,44 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
static int json_encode(lua_State *l)
{
json_config_t *cfg = json_fetch_config(l);
+ json_encode_options_t options = { .char2escape = { char2escape } };
+ json_encode_t ctx = { .options = &options, .cfg = cfg };
strbuf_t local_encode_buf;
strbuf_t *encode_buf;
char *json;
int len;
+ const char *customChar2escape[256];
- luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
+ switch (lua_gettop(l)) {
+ case 1:
+ break;
+ case 2:
+ luaL_checktype(l, 2, LUA_TTABLE);
+ lua_getfield(l, 2, "escape_slash");
+
+ /* We only handle the escape_slash option for now */
+ if (lua_isnil(l, -1)) {
+ lua_pop(l, 2);
+ break;
+ }
+
+ luaL_checktype(l, -1, LUA_TBOOLEAN);
+
+ int escape_slash = lua_toboolean(l, -1);
+
+ if (escape_slash) {
+ /* This can be optimised by adding a new hard-coded escape table for this case,
+ * but this path will rarely if ever be used, so let's just memcpy.*/
+ memcpy(customChar2escape, char2escape, sizeof(char2escape));
+ customChar2escape['/'] = "\\/";
+ *ctx.options->char2escape = customChar2escape;
+ }
+
+ lua_pop(l, 2);
+ break;
+ default:
+ return luaL_error (l, "expected 1 or 2 arguments");
+ }
if (!cfg->encode_keep_buffer) {
/* Use private buffer */
@@ -887,7 +937,8 @@ static int json_encode(lua_State *l)
strbuf_reset(encode_buf);
}
- json_append_data(l, cfg, 0, encode_buf);
+ ctx.json = encode_buf;
+ json_append_data(l, &ctx, 0);
json = strbuf_string(encode_buf, &len);
lua_pushlstring(l, json, len);
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index db87500d08..45e2de69e0 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -386,9 +386,9 @@ cleanup:
/// - id: (number) autocommand id
/// - event: (string) name of the triggered event |autocmd-events|
/// - group: (number|nil) autocommand group id, if any
-/// - match: (string) expanded value of [<amatch>]
-/// - buf: (number) expanded value of [<abuf>]
-/// - file: (string) expanded value of [<afile>]
+/// - file: (string) [<afile>] (not expanded to a full path)
+/// - match: (string) [<amatch>] (expanded to a full path)
+/// - buf: (number) [<abuf>]
/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
/// - command (string) optional: Vim command to execute on event. Cannot be used with
/// {callback}
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index c08ef7a4c1..118a50e15d 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1666,7 +1666,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
} else {
autocmd_fname = fname_io;
}
+ char *afile_orig = NULL; ///< Unexpanded <afile>
if (autocmd_fname != NULL) {
+ afile_orig = xstrdup(autocmd_fname);
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
}
@@ -1798,6 +1800,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// save vector size, to avoid an endless loop when more patterns
// are added when executing autocommands
.ausize = kv_size(autocmds[(int)event]),
+ .afile_orig = afile_orig,
.fname = fname,
.sfname = sfname,
.tail = tail,
@@ -1865,6 +1868,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
autocmd_nested = save_autocmd_nested;
xfree(SOURCING_NAME);
estack_pop();
+ xfree(afile_orig);
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
autocmd_fname_full = save_autocmd_fname_full;
@@ -2029,8 +2033,8 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
MAXSIZE_TEMP_DICT(data, 7);
PUT_C(data, "id", INTEGER_OBJ(ac->id));
PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event)));
+ PUT_C(data, "file", CSTR_AS_OBJ(apc->afile_orig));
PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match));
- PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname));
PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h
index 490782b209..cba947e85f 100644
--- a/src/nvim/autocmd_defs.h
+++ b/src/nvim/autocmd_defs.h
@@ -52,6 +52,7 @@ struct AutoPatCmd_S {
AutoPat *lastpat; ///< Last matched AutoPat
size_t auidx; ///< Current autocmd index to execute
size_t ausize; ///< Saved AutoCmd vector size
+ char *afile_orig; ///< Unexpanded <afile>
char *fname; ///< Fname to match with
char *sfname; ///< Sfname to match with
char *tail; ///< Tail of fname
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 248f419807..f5e11a188f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -604,7 +604,7 @@ static int insert_execute(VimState *state, int key)
|| (ins_compl_enter_selects()
&& (s->c == CAR || s->c == K_KENTER || s->c == NL)))
&& stop_arrow() == OK) {
- ins_compl_delete();
+ ins_compl_delete(false);
ins_compl_insert(false);
}
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 9968f32de1..b7c83ea1ac 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -6101,12 +6101,18 @@ static void ex_sleep(exarg_T *eap)
default:
semsg(_(e_invarg2), eap->arg); return;
}
- do_sleep(len);
+ do_sleep(len, false);
}
/// Sleep for "msec" milliseconds, but return early on CTRL-C.
-void do_sleep(int64_t msec)
+///
+/// @param hide_cursor hide the cursor if true
+void do_sleep(int64_t msec, bool hide_cursor)
{
+ if (hide_cursor) {
+ ui_busy_start();
+ }
+
ui_flush(); // flush before waiting
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, msec, got_int);
@@ -6115,6 +6121,10 @@ void do_sleep(int64_t msec)
if (got_int) {
vpeekc();
}
+
+ if (hide_cursor) {
+ ui_busy_stop();
+ }
}
/// ":winsize" command (obsolete).
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 779b31e7a0..c79683dc00 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -47,7 +47,9 @@ end
--- @param s string
--- @return string
local lowercase_to_titlecase = function(s)
- return s:sub(1, 1):upper() .. s:sub(2)
+ return table.concat(vim.tbl_map(function(word) --- @param word string
+ return word:sub(1, 1):upper() .. word:sub(2)
+ end, vim.split(s, '[-_]')))
end
-- Generate options enum file
@@ -177,7 +179,7 @@ for _, option in ipairs(options_meta) do
vars_w(
(' kOpt%sFlag%s = 0x%02x,'):format(
opt_name,
- lowercase_to_titlecase(flag_name),
+ lowercase_to_titlecase(flag_name:gsub(':$', '')),
enum_values[flag_name]
)
)
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 93d081153c..b18c4ead41 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -951,14 +951,14 @@ static void ins_compl_longest_match(compl_T *match)
compl_leader = xstrdup(match->cp_str);
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
+ ins_compl_delete(false);
ins_bytes(compl_leader + get_compl_len());
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
compl_used_match = false;
@@ -985,14 +985,14 @@ static void ins_compl_longest_match(compl_T *match)
// Leader was shortened, need to change the inserted text.
*p = NUL;
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
+ ins_compl_delete(false);
ins_bytes(compl_leader + get_compl_len());
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
}
@@ -1811,9 +1811,8 @@ static bool ins_compl_need_restart(void)
static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
- ins_compl_delete();
+ ins_compl_delete(true);
ins_bytes(compl_leader + get_compl_len());
- restore_orig_extmarks();
compl_used_match = false;
if (compl_started) {
@@ -2137,7 +2136,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// CTRL-E means completion is Ended, go back to the typed text.
// but only do this, if the Popup is still visible
if (c == Ctrl_E) {
- ins_compl_delete();
+ ins_compl_delete(false);
char *p = NULL;
if (compl_leader != NULL) {
p = compl_leader;
@@ -3610,11 +3609,24 @@ static void ins_compl_update_shown_match(void)
}
/// Delete the old text being completed.
-void ins_compl_delete(void)
+void ins_compl_delete(bool new_leader)
{
+ // Avoid deleting text that will be reinserted when changing leader. This
+ // allows marks present on the original text to shrink/grow appropriately.
+ int orig_col = 0;
+ if (new_leader) {
+ char *orig = compl_orig_text;
+ char *leader = ins_compl_leader();
+ while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) {
+ leader += utf_ptr2len(leader);
+ orig += utf_ptr2len(orig);
+ }
+ orig_col = (int)(orig - compl_orig_text);
+ }
+
// In insert mode: Delete the typed part.
// In replace mode: Put the old characters back, if any.
- int col = compl_col + (compl_status_adding() ? compl_length : 0);
+ int col = compl_col + (compl_status_adding() ? compl_length : orig_col);
if ((int)curwin->w_cursor.col > col) {
if (stop_arrow() == FAIL) {
return;
@@ -3858,7 +3870,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
if (allow_get_expansion && insert_match
&& (!compl_get_longest || compl_used_match)) {
// Delete old text to be replaced
- ins_compl_delete();
+ ins_compl_delete(false);
}
// When finding the longest common text we stick at the original text,
@@ -3911,7 +3923,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
// Delete old text to be replaced, since we're still searching and
// don't want to match ourselves!
- ins_compl_delete();
+ ins_compl_delete(false);
}
// Enter will select a match when the match wasn't inserted and the popup
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 0b1156a6bd..a32a06edca 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -26,6 +26,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
@@ -94,6 +95,16 @@ static char *confirm_msg_tail; // tail of confirm_msg
MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
+static int msg_hist_max = 500; // The default max value is 500
+
+// args in 'messagesopt' option
+#define MESSAGES_OPT_HIT_ENTER "hit-enter"
+#define MESSAGES_OPT_WAIT "wait:"
+#define MESSAGES_OPT_HISTORY "history:"
+
+// The default is "hit-enter,history:500"
+static int msg_flags = kOptMoptFlagHitEnter | kOptMoptFlagHistory;
+static int msg_wait = 0;
static FILE *verbose_fd = NULL;
static bool verbose_did_open = false;
@@ -1038,14 +1049,69 @@ int delete_first_msg(void)
return OK;
}
-void check_msg_hist(void)
+static void check_msg_hist(void)
{
// Don't let the message history get too big
- while (msg_hist_len > 0 && msg_hist_len > p_mhi) {
+ while (msg_hist_len > 0 && msg_hist_len > msg_hist_max) {
(void)delete_first_msg();
}
}
+int messagesopt_changed(void)
+{
+ int messages_flags_new = 0;
+ int messages_wait_new = 0;
+ int messages_history_new = 0;
+
+ char *p = p_meo;
+ while (*p != NUL) {
+ if (strnequal(p, S_LEN(MESSAGES_OPT_HIT_ENTER))) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER);
+ messages_flags_new |= kOptMoptFlagHitEnter;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_WAIT))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_WAIT);
+ messages_wait_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagWait;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_HISTORY))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY);
+ messages_history_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagHistory;
+ }
+
+ if (*p != ',' && *p != NUL) {
+ return FAIL;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ // Either "wait" or "hit-enter" is required
+ if (!(messages_flags_new & (kOptMoptFlagHitEnter | kOptMoptFlagWait))) {
+ return FAIL;
+ }
+
+ // "history" must be set
+ if (!(messages_flags_new & kOptMoptFlagHistory)) {
+ return FAIL;
+ }
+
+ // "history" must be <= 10000
+ if (messages_history_new > 10000) {
+ return FAIL;
+ }
+
+ msg_flags = messages_flags_new;
+ msg_wait = messages_wait_new;
+
+ msg_hist_max = messages_history_new;
+ check_msg_hist();
+
+ return OK;
+}
+
/// :messages command implementation
void ex_messages(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
@@ -1209,83 +1275,88 @@ void wait_return(int redraw)
cmdline_row = Rows - 1;
}
- hit_return_msg(true);
-
- do {
- // Remember "got_int", if it is set vgetc() probably returns a
- // CTRL-C, but we need to loop then.
- had_got_int = got_int;
-
- // Don't do mappings here, we put the character back in the
- // typeahead buffer.
- no_mapping++;
- allow_keys++;
-
- // Temporarily disable Recording. If Recording is active, the
- // character will be recorded later, since it will be added to the
- // typebuf after the loop
- const int save_reg_recording = reg_recording;
- save_scriptout = scriptout;
- reg_recording = 0;
- scriptout = NULL;
- c = safe_vgetc();
- if (had_got_int && !global_busy) {
- got_int = false;
- }
- no_mapping--;
- allow_keys--;
- reg_recording = save_reg_recording;
- scriptout = save_scriptout;
-
- // Allow scrolling back in the messages.
- // Also accept scroll-down commands when messages fill the screen,
- // to avoid that typing one 'j' too many makes the messages
- // disappear.
- if (p_more) {
- if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
- || c == K_UP || c == K_PAGEUP) {
- if (msg_scrolled > Rows) {
- // scroll back to show older messages
- do_more_prompt(c);
- } else {
- msg_didout = false;
- c = K_IGNORE;
- msg_col = 0;
- }
- if (quit_more) {
- c = CAR; // just pretend CR was hit
- quit_more = false;
- got_int = false;
- } else if (c != K_IGNORE) {
+ if (msg_flags & kOptMoptFlagHitEnter) {
+ hit_return_msg(true);
+
+ do {
+ // Remember "got_int", if it is set vgetc() probably returns a
+ // CTRL-C, but we need to loop then.
+ had_got_int = got_int;
+
+ // Don't do mappings here, we put the character back in the
+ // typeahead buffer.
+ no_mapping++;
+ allow_keys++;
+
+ // Temporarily disable Recording. If Recording is active, the
+ // character will be recorded later, since it will be added to the
+ // typebuf after the loop
+ const int save_reg_recording = reg_recording;
+ save_scriptout = scriptout;
+ reg_recording = 0;
+ scriptout = NULL;
+ c = safe_vgetc();
+ if (had_got_int && !global_busy) {
+ got_int = false;
+ }
+ no_mapping--;
+ allow_keys--;
+ reg_recording = save_reg_recording;
+ scriptout = save_scriptout;
+
+ // Allow scrolling back in the messages.
+ // Also accept scroll-down commands when messages fill the screen,
+ // to avoid that typing one 'j' too many makes the messages
+ // disappear.
+ if (p_more) {
+ if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
+ || c == K_UP || c == K_PAGEUP) {
+ if (msg_scrolled > Rows) {
+ // scroll back to show older messages
+ do_more_prompt(c);
+ } else {
+ msg_didout = false;
+ c = K_IGNORE;
+ msg_col = 0;
+ }
+ if (quit_more) {
+ c = CAR; // just pretend CR was hit
+ quit_more = false;
+ got_int = false;
+ } else if (c != K_IGNORE) {
+ c = K_IGNORE;
+ hit_return_msg(false);
+ }
+ } else if (msg_scrolled > Rows - 2
+ && (c == 'j' || c == 'd' || c == 'f'
+ || c == K_DOWN || c == K_PAGEDOWN)) {
c = K_IGNORE;
- hit_return_msg(false);
}
- } else if (msg_scrolled > Rows - 2
- && (c == 'j' || c == 'd' || c == 'f'
- || c == K_DOWN || c == K_PAGEDOWN)) {
- c = K_IGNORE;
}
+ } while ((had_got_int && c == Ctrl_C)
+ || c == K_IGNORE
+ || c == K_LEFTDRAG || c == K_LEFTRELEASE
+ || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
+ || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
+ || c == K_MOUSELEFT || c == K_MOUSERIGHT
+ || c == K_MOUSEDOWN || c == K_MOUSEUP
+ || c == K_MOUSEMOVE);
+ os_breakcheck();
+
+ // Avoid that the mouse-up event causes visual mode to start.
+ if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
+ || c == K_X1MOUSE || c == K_X2MOUSE) {
+ jump_to_mouse(MOUSE_SETPOS, NULL, 0);
+ } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
+ // Put the character back in the typeahead buffer. Don't use the
+ // stuff buffer, because lmaps wouldn't work.
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
+ do_redraw = true; // need a redraw even though there is typeahead
}
- } while ((had_got_int && c == Ctrl_C)
- || c == K_IGNORE
- || c == K_LEFTDRAG || c == K_LEFTRELEASE
- || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
- || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
- || c == K_MOUSELEFT || c == K_MOUSERIGHT
- || c == K_MOUSEDOWN || c == K_MOUSEUP
- || c == K_MOUSEMOVE);
- os_breakcheck();
-
- // Avoid that the mouse-up event causes visual mode to start.
- if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
- || c == K_X1MOUSE || c == K_X2MOUSE) {
- jump_to_mouse(MOUSE_SETPOS, NULL, 0);
- } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
- // Put the character back in the typeahead buffer. Don't use the
- // stuff buffer, because lmaps wouldn't work.
- ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
- do_redraw = true; // need a redraw even though there is
- // typeahead
+ } else {
+ c = CAR;
+ // Wait to allow the user to verify the output.
+ do_sleep(msg_wait, true);
}
}
redir_off = false;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index ba84380529..92a6068c5a 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -812,7 +812,7 @@ static void normal_get_additional_char(NormalState *s)
// There is a busy wait here when typing "f<C-\>" and then
// something different from CTRL-N. Can't be avoided.
while ((s->c = vpeekc()) <= 0 && towait > 0) {
- do_sleep(towait > 50 ? 50 : towait);
+ do_sleep(towait > 50 ? 50 : towait, false);
towait -= 50;
}
if (s->c > 0) {
@@ -5561,7 +5561,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gs": Goto sleep.
case 's':
- do_sleep(cap->count1 * 1000);
+ do_sleep(cap->count1 * 1000, false);
break;
// "ga": Display the ascii value of the character under the
diff --git a/src/nvim/option.c b/src/nvim/option.c
index fd0ac375a6..e261f06b42 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2199,13 +2199,6 @@ static const char *did_set_modified(optset_T *args)
return NULL;
}
-/// Process the updated 'msghistory' option value.
-static const char *did_set_msghistory(optset_T *args FUNC_ATTR_UNUSED)
-{
- check_msg_hist();
- return NULL;
-}
-
/// Process the updated 'number' or 'relativenumber' option value.
static const char *did_set_number_relativenumber(optset_T *args)
{
@@ -2886,13 +2879,6 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
return e_invarg;
}
break;
- case kOptMsghistory:
- if (value < 0) {
- return e_positive;
- } else if (value > 10000) {
- return e_invarg;
- }
- break;
case kOptPyxversion:
if (value == 0) {
*newval = 3;
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 3bb2035e7c..aca876bddb 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -448,6 +448,7 @@ EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
EXTERN OptInt p_mis; ///< 'menuitems'
+EXTERN char *p_meo; ///< 'messagesopt'
EXTERN char *p_msm; ///< 'mkspellmem'
EXTERN int p_ml; ///< 'modeline'
EXTERN int p_mle; ///< 'modelineexpr'
@@ -464,7 +465,6 @@ EXTERN OptInt p_mousescroll_vert INIT( = MOUSESCROLL_VERT_DFLT);
EXTERN OptInt p_mousescroll_hor INIT( = MOUSESCROLL_HOR_DFLT);
EXTERN OptInt p_mouset; ///< 'mousetime'
EXTERN int p_more; ///< 'more'
-EXTERN OptInt p_mhi; ///< 'msghistory'
EXTERN char *p_nf; ///< 'nrformats'
EXTERN char *p_opfunc; ///< 'operatorfunc'
EXTERN char *p_para; ///< 'paragraphs'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 97fe09f376..a5d5a45b59 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -4094,7 +4094,7 @@ return {
desc = [=[
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing| and 'msghistory' for
+ each of these histories (see |cmdline-editing| and 'messagesopt' for
the number of messages to remember).
The maximum value is 10000.
]=],
@@ -5449,6 +5449,38 @@ return {
varname = 'p_mis',
},
{
+ abbreviation = 'mopt',
+ cb = 'did_set_messagesopt',
+ defaults = { if_true = 'hit-enter,history:500' },
+ values = { 'hit-enter', 'wait:', 'history:' },
+ flags = true,
+ deny_duplicates = true,
+ desc = [=[
+ Option settings when outputting messages. It can consist of the
+ following items. Items must be separated by a comma.
+
+ hit-enter Use |hit-enter| prompt when the message is longer than
+ 'cmdheight' size.
+
+ wait:{n} Ignored when "hit-enter" is present. Instead of using
+ |hit-enter| prompt, will simply wait for {n}
+ milliseconds so the user has a chance to read the
+ message, use 0 to disable sleep (but then the user may
+ miss an important message).
+
+ history:{n} Determines how many entries are remembered in the
+ |:messages| history. The maximum value is 10000.
+ Setting it to zero clears the message history.
+ ]=],
+ expand_cb = 'expand_set_messagesopt',
+ full_name = 'messagesopt',
+ list = 'onecommacolon',
+ scope = { 'global' },
+ short_desc = N_('options for outputting messages'),
+ type = 'string',
+ varname = 'p_meo',
+ },
+ {
abbreviation = 'msm',
cb = 'did_set_mkspellmem',
defaults = { if_true = '460000,2000,500' },
@@ -5893,21 +5925,6 @@ return {
varname = 'p_mouset',
},
{
- abbreviation = 'mhi',
- cb = 'did_set_msghistory',
- defaults = { if_true = 500 },
- desc = [=[
- Determines how many entries are remembered in the |:messages| history.
- The maximum value is 10000.
- Setting it to zero clears the message history.
- ]=],
- full_name = 'msghistory',
- scope = { 'global' },
- short_desc = N_('how many messages are remembered'),
- type = 'number',
- varname = 'p_mhi',
- },
- {
abbreviation = 'nf',
cb = 'did_set_nrformats',
defaults = { if_true = 'bin,hex' },
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 918443db9f..75b6585553 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -1682,6 +1682,24 @@ const char *did_set_matchpairs(optset_T *args)
return NULL;
}
+/// Process the updated 'messagesopt' option value.
+const char *did_set_messagesopt(optset_T *args FUNC_ATTR_UNUSED)
+{
+ if (messagesopt_changed() == FAIL) {
+ return e_invarg;
+ }
+ return NULL;
+}
+
+int expand_set_messagesopt(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ opt_mopt_values,
+ ARRAY_SIZE(opt_mopt_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'mkspellmem' option is changed.
const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED)
{
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
index 3f9883f43f..bb6456f45b 100644
--- a/test/functional/api/autocmd_spec.lua
+++ b/test/functional/api/autocmd_spec.lua
@@ -273,54 +273,72 @@ describe('autocmd api', function()
eq({}, api.nvim_get_autocmds({ event = 'User', pattern = 'Test' }))
end)
- it('receives an args table', function()
+ local function test_autocmd_args(event)
+ local function get_amatch(pat)
+ return event == 'User' and pat or vim.fs.normalize(n.fn.fnamemodify(pat, ':p'))
+ end
+
local group_id = api.nvim_create_augroup('TestGroup', {})
-- Having an existing autocmd calling expand("<afile>") shouldn't change args #18964
- api.nvim_create_autocmd('User', {
+ api.nvim_create_autocmd(event, {
group = 'TestGroup',
pattern = 'Te*',
command = 'call expand("<afile>")',
})
- local autocmd_id = exec_lua [[
- return vim.api.nvim_create_autocmd("User", {
+ local autocmd_id = exec_lua(([[
+ return vim.api.nvim_create_autocmd(%q, {
group = "TestGroup",
pattern = "Te*",
callback = function(args)
vim.g.autocmd_args = args
end,
})
- ]]
+ ]]):format(event))
- api.nvim_exec_autocmds('User', { pattern = 'Test pattern' })
+ local exec_pat = 'Test pattern'
+ local amatch = get_amatch(exec_pat)
+ api.nvim_exec_autocmds(event, { pattern = exec_pat })
eq({
id = autocmd_id,
group = group_id,
- event = 'User',
- match = 'Test pattern',
- file = 'Test pattern',
+ event = event,
+ match = amatch,
+ file = exec_pat,
buf = 1,
}, api.nvim_get_var('autocmd_args'))
-- Test without a group
- autocmd_id = exec_lua [[
- return vim.api.nvim_create_autocmd("User", {
+ autocmd_id = exec_lua(([[
+ return vim.api.nvim_create_autocmd(%q, {
pattern = "*",
callback = function(args)
vim.g.autocmd_args = args
end,
})
- ]]
+ ]]):format(event))
- api.nvim_exec_autocmds('User', { pattern = 'some_pat' })
+ exec_pat = 'some_pat'
+ amatch = get_amatch(exec_pat)
+ api.nvim_exec_autocmds(event, { pattern = exec_pat })
eq({
id = autocmd_id,
group = nil,
- event = 'User',
- match = 'some_pat',
- file = 'some_pat',
+ event = event,
+ match = amatch,
+ file = exec_pat,
buf = 1,
}, api.nvim_get_var('autocmd_args'))
+ end
+
+ describe('receives correct args table', function()
+ it('for event that takes non-file pattern', function()
+ test_autocmd_args('User')
+ end)
+
+ it('for event that takes file pattern', function()
+ test_autocmd_args('BufEnter')
+ end)
end)
it('can receive arbitrary data', function()
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index eb23de2007..c20d925713 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -1265,7 +1265,7 @@ describe('completion', function()
command([[
call setline(1, ['aaaa'])
let ns_id = nvim_create_namespace('extmark')
- let mark_id = nvim_buf_set_extmark(0, ns_id, 0, 0, { 'end_col':2, 'hl_group':'Error'})
+ let mark_id = nvim_buf_set_extmark(0, ns_id, 0, 0, { 'end_col':2, 'hl_group':'Error' })
let mark = nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })
inoremap <C-x> <C-r>=Complete()<CR>
function Complete() abort
@@ -1312,5 +1312,18 @@ describe('completion', function()
{5:-- Keyword completion (^N^P) }{19:Back at original} |
]],
})
+ -- But still grows with end_right_gravity #31437
+ command(
+ "call nvim_buf_set_extmark(0, ns_id, 1, 0, { 'end_col':2, 'hl_group':'Error', 'end_right_gravity': 1 })"
+ )
+ feed('<Esc>ji<C-N>a')
+ screen:expect({
+ grid = [[
+ {9:aa}aaa |
+ {9:aaa}^aa |
+ aaaaa |
+ {5:-- INSERT --} |
+ ]],
+ })
end)
end)
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index bc58551a34..063bd05618 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -689,4 +689,43 @@ describe('messages', function()
]])
os.remove('b.txt')
end)
+
+ -- oldtest: Test_messagesopt_wait()
+ it('&messagesopt "wait"', function()
+ screen = Screen.new(45, 6)
+ command('set cmdheight=1')
+
+ -- Check hit-enter prompt
+ command('set messagesopt=hit-enter,history:500')
+ feed(":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ screen:expect([[
+ |
+ {3: }|
+ foo |
+ bar |
+ baz |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+
+ -- Check no hit-enter prompt when "wait:" is set
+ command('set messagesopt=wait:100,history:500')
+ feed(":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ screen:expect({
+ grid = [[
+ |
+ {1:~ }|
+ {3: }|
+ foo |
+ bar |
+ baz |
+ ]],
+ timeout = 100,
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index 6a4f8d70f3..b75ff75b05 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -208,7 +208,7 @@ describe('filetype.lua', function()
eq('gitconfig', api.nvim_get_option_value('filetype', {}))
end)
- pending('works with :doautocmd BufRead #31306', function()
+ it('works with :doautocmd BufRead #31306', function()
clear({ args = { '--clean' } })
eq('', api.nvim_get_option_value('filetype', {}))
command('doautocmd BufRead README.md')
diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua
index a6e814d739..8df42fcaa1 100644
--- a/test/functional/lua/json_spec.lua
+++ b/test/functional/lua/json_spec.lua
@@ -152,6 +152,45 @@ describe('vim.json.encode()', function()
clear()
end)
+ it('dumps strings with & without escaped slash', function()
+ -- With slash
+ eq('"Test\\/"', exec_lua([[return vim.json.encode('Test/', { escape_slash = true })]]))
+ eq(
+ 'Test/',
+ exec_lua([[return vim.json.decode(vim.json.encode('Test/', { escape_slash = true }))]])
+ )
+
+ -- Without slash
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/')]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', {})]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', { _invalid = true })]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', { escape_slash = false })]]))
+ eq(
+ '"Test/"',
+ exec_lua([[return vim.json.encode('Test/', { _invalid = true, escape_slash = false })]])
+ )
+ eq(
+ 'Test/',
+ exec_lua([[return vim.json.decode(vim.json.encode('Test/', { escape_slash = false }))]])
+ )
+
+ -- Checks for for global side-effects
+ eq(
+ '"Test/"',
+ exec_lua([[
+ vim.json.encode('Test/', { escape_slash = true })
+ return vim.json.encode('Test/')
+ ]])
+ )
+ eq(
+ '"Test\\/"',
+ exec_lua([[
+ vim.json.encode('Test/', { escape_slash = false })
+ return vim.json.encode('Test/', { escape_slash = true })
+ ]])
+ )
+ end)
+
it('dumps strings', function()
eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
eq('""', exec_lua([[return vim.json.encode('')]]))
diff --git a/test/functional/plugin/lsp/handler_spec.lua b/test/functional/plugin/lsp/handler_spec.lua
deleted file mode 100644
index 4b05b676a8..0000000000
--- a/test/functional/plugin/lsp/handler_spec.lua
+++ /dev/null
@@ -1,42 +0,0 @@
-local t = require('test.testutil')
-local n = require('test.functional.testnvim')()
-
-local eq = t.eq
-local exec_lua = n.exec_lua
-local pcall_err = t.pcall_err
-local matches = t.matches
-
-describe('lsp-handlers', function()
- describe('vim.lsp._with_extend', function()
- it('should return a table with the default keys', function()
- eq(
- { hello = 'world' },
- exec_lua(function()
- return vim.lsp._with_extend('test', { hello = 'world' })
- end)
- )
- end)
-
- it('should override with config keys', function()
- eq(
- { hello = 'universe', other = true },
- exec_lua(function()
- return vim.lsp._with_extend(
- 'test',
- { other = true, hello = 'world' },
- { hello = 'universe' }
- )
- end)
- )
- end)
-
- it('should not allow invalid keys', function()
- matches(
- '.*Invalid option for `test`.*',
- pcall_err(exec_lua, function()
- return vim.lsp._with_extend('test', { hello = 'world' }, { invalid = true })
- end)
- )
- end)
- end)
-end)
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 2f8d204d36..2f80cee226 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -1,5 +1,6 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
+local ts_t = require('test.functional.treesitter.testutil')
local clear = n.clear
local dedent = t.dedent
@@ -8,6 +9,7 @@ local insert = n.insert
local exec_lua = n.exec_lua
local pcall_err = t.pcall_err
local feed = n.feed
+local run_query = ts_t.run_query
describe('treesitter parser API', function()
before_each(function()
@@ -644,6 +646,109 @@ print()
end)
end)
+ describe('trim! directive', function()
+ it('can trim all whitespace', function()
+ -- luacheck: push ignore 611 613
+ insert([=[
+ print([[
+
+ f
+ helllo
+ there
+ asdf
+ asdfassd
+
+
+
+ ]])
+ print([[
+
+
+
+ ]])
+
+ print([[]])
+
+ print([[
+ ]])
+
+ print([[ hello 😃 ]])
+ ]=])
+ -- luacheck: pop
+
+ local query_text = [[
+ ; query
+ ((string_content) @str
+ (#trim! @str 1 1 1 1))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'lua')
+ end)
+
+ eq({
+ { 'str', { 2, 12, 6, 10 } },
+ { 'str', { 11, 10, 11, 10 } },
+ { 'str', { 17, 10, 17, 10 } },
+ { 'str', { 19, 10, 19, 10 } },
+ { 'str', { 22, 15, 22, 25 } },
+ }, run_query('lua', query_text))
+ end)
+
+ it('trims only empty lines by default (backwards compatible)', function()
+ insert(dedent [[
+ ## Heading
+
+ With some text
+
+ ## And another
+
+ With some more here]])
+
+ local query_text = [[
+ ; query
+ ((section) @fold
+ (#trim! @fold))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'markdown')
+ end)
+
+ eq({
+ { 'fold', { 0, 0, 2, 14 } },
+ { 'fold', { 4, 0, 6, 19 } },
+ }, run_query('markdown', query_text))
+ end)
+
+ it('can trim lines', function()
+ insert(dedent [[
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ ]])
+
+ local query_text = [[
+ ; query
+ ((list_item
+ (list)) @fold
+ (#trim! @fold 1 1 1 1))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'markdown')
+ end)
+
+ eq({
+ { 'fold', { 0, 0, 4, 13 } },
+ { 'fold', { 1, 2, 3, 15 } },
+ }, run_query('markdown', query_text))
+ end)
+ end)
+
it('tracks the root range properly (#22911)', function()
insert([[
int main() {
@@ -659,32 +764,19 @@ print()
vim.treesitter.start(0, 'c')
end)
- local function run_query()
- return exec_lua(function()
- local query = vim.treesitter.query.parse('c', query0)
- local parser = vim.treesitter.get_parser()
- local tree = parser:parse()[1]
- local res = {}
- for id, node in query:iter_captures(tree:root()) do
- table.insert(res, { query.captures[id], node:range() })
- end
- return res
- end)
- end
-
eq({
- { 'function', 0, 0, 2, 1 },
- { 'declaration', 1, 2, 1, 12 },
- }, run_query())
+ { 'function', { 0, 0, 2, 1 } },
+ { 'declaration', { 1, 2, 1, 12 } },
+ }, run_query('c', query0))
n.command 'normal ggO'
insert('int a;')
eq({
- { 'declaration', 0, 0, 0, 6 },
- { 'function', 1, 0, 3, 1 },
- { 'declaration', 2, 2, 2, 12 },
- }, run_query())
+ { 'declaration', { 0, 0, 0, 6 } },
+ { 'function', { 1, 0, 3, 1 } },
+ { 'declaration', { 2, 2, 2, 12 } },
+ }, run_query('c', query0))
end)
it('handles ranges when source is a multiline string (#20419)', function()
diff --git a/test/functional/treesitter/testutil.lua b/test/functional/treesitter/testutil.lua
new file mode 100644
index 0000000000..f8934f06c3
--- /dev/null
+++ b/test/functional/treesitter/testutil.lua
@@ -0,0 +1,25 @@
+local n = require('test.functional.testnvim')()
+
+local exec_lua = n.exec_lua
+
+local M = {}
+
+---@param language string
+---@param query_string string
+function M.run_query(language, query_string)
+ return exec_lua(function(lang, query_str)
+ local query = vim.treesitter.query.parse(lang, query_str)
+ local parser = vim.treesitter.get_parser()
+ local tree = parser:parse()[1]
+ local res = {}
+ for id, node, metadata in query:iter_captures(tree:root(), 0) do
+ table.insert(
+ res,
+ { query.captures[id], metadata[id] and metadata[id].range or { node:range() } }
+ )
+ end
+ return res
+ end, language, query_string)
+end
+
+return M
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
index be5a7e6ee4..74aea93ee9 100644
--- a/test/old/testdir/gen_opt_test.vim
+++ b/test/old/testdir/gen_opt_test.vim
@@ -117,7 +117,6 @@ let test_values = {
"\ 'imstyle': [[0, 1], [-1, 2, 999]],
\ 'lines': [[2, 24, 1000], [-1, 0, 1]],
\ 'linespace': [[-1, 0, 2, 4, 999], ['']],
- \ 'msghistory': [[0, 1, 100, 10000], [-1, 10001]],
\ 'numberwidth': [[1, 4, 8, 10, 11, 20], [-1, 0, 21]],
\ 'regexpengine': [[0, 1, 2], [-1, 3, 999]],
\ 'report': [[0, 1, 2, 9999], [-1]],
@@ -264,6 +263,11 @@ let test_values = {
\ 'eol:\\u21b5', 'eol:\\U000021b5', 'eol:x,space:y'],
\ ['xxx', 'eol:']],
\ 'matchpairs': [['', '(:)', '(:),<:>'], ['xxx']],
+ \ 'messagesopt': [['hit-enter,history:1', 'hit-enter,history:10000',
+ \ 'history:100,wait:100', 'history:0,wait:0',
+ \ 'hit-enter,history:1,wait:1'],
+ \ ['xxx', 'history:500', 'hit-enter,history:-1',
+ \ 'hit-enter,history:10001', 'hit-enter']],
\ 'mkspellmem': [['10000,100,12'], ['', 'xxx', '10000,100']],
\ 'mouse': [['', 'n', 'v', 'i', 'c', 'h', 'a', 'r', 'nvi'],
\ ['xxx', 'n,v,i']],
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index 2f34ecb414..cbce0e908d 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -4181,30 +4181,4 @@ func Test_cd_bslash_completion_windows()
let &shellslash = save_shellslash
endfunc
-func Test_msghistory()
- " After setting 'msghistory' to 2 and outputting a message 4 times with
- " :echomsg, is the number of output lines of :messages 2?
- set msghistory=2
- echomsg 'foo'
- echomsg 'bar'
- echomsg 'baz'
- echomsg 'foobar'
- call assert_equal(['baz', 'foobar'], GetMessages())
-
- " When the number of messages is 10 and 'msghistory' is changed to 5, is the
- " number of output lines of :messages 5?
- set msghistory=10
- for num in range(1, 10)
- echomsg num
- endfor
- set msghistory=5
- call assert_equal(5, len(GetMessages()))
-
- " Check empty list
- set msghistory=0
- call assert_true(empty(GetMessages()))
-
- set msghistory&
-endfunc
-
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_messages.vim b/test/old/testdir/test_messages.vim
index ac5184645f..d79bc0fae7 100644
--- a/test/old/testdir/test_messages.vim
+++ b/test/old/testdir/test_messages.vim
@@ -216,6 +216,7 @@ endfunc
" Test more-prompt (see :help more-prompt).
func Test_message_more()
CheckRunVimInTerminal
+
let buf = RunVimInTerminal('', {'rows': 6})
call term_sendkeys(buf, ":call setline(1, range(1, 100))\n")
@@ -611,4 +612,50 @@ func Test_cmdheight_zero()
tabonly
endfunc
+func Test_messagesopt_history()
+ " After setting 'messagesopt' "history" to 2 and outputting a message 4 times
+ " with :echomsg, is the number of output lines of :messages 2?
+ set messagesopt=hit-enter,history:2
+ echomsg 'foo'
+ echomsg 'bar'
+ echomsg 'baz'
+ echomsg 'foobar'
+ call assert_equal(['baz', 'foobar'], GetMessages())
+
+ " When the number of messages is 10 and 'messagesopt' "history" is changed to
+ " 5, is the number of output lines of :messages 5?
+ set messagesopt=hit-enter,history:10
+ for num in range(1, 10)
+ echomsg num
+ endfor
+ set messagesopt=hit-enter,history:5
+ call assert_equal(5, len(GetMessages()))
+
+ " Check empty list
+ set messagesopt=hit-enter,history:0
+ call assert_true(empty(GetMessages()))
+
+ set messagesopt&
+endfunc
+
+func Test_messagesopt_wait()
+ CheckRunVimInTerminal
+
+ let buf = RunVimInTerminal('', {'rows': 6, 'cols': 45})
+ call term_sendkeys(buf, ":set cmdheight=1\n")
+
+ " Check hit-enter prompt
+ call term_sendkeys(buf, ":set messagesopt=hit-enter,history:500\n")
+ call term_sendkeys(buf, ":echo 'foo' | echo 'bar' echo 'baz'\n")
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ " Check no hit-enter prompt when "wait:" is set
+ call term_sendkeys(buf, ":set messagesopt=wait:100,history:500\n")
+ call term_sendkeys(buf, ":echo 'foo' | echo 'bar' echo 'baz'\n")
+ call WaitForAssert({-> assert_equal(' 0,0-1 All', term_getline(buf, 6))})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim
index 3773775564..d090186b62 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -644,6 +644,10 @@ func Test_set_completion_string_values()
" call feedkeys(":set hl=8b i\<Left>\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
" call assert_equal("\"set hl=8bi i", @:)
+ " messagesopt
+ call assert_equal(['history:', 'hit-enter', 'wait:'],
+ \ getcompletion('set messagesopt+=', 'cmdline')->sort())
+
"
" Test flag lists
"
@@ -743,7 +747,6 @@ func Test_set_option_errors()
call assert_fails('set backupcopy=', 'E474:')
call assert_fails('set regexpengine=3', 'E474:')
call assert_fails('set history=10001', 'E474:')
- call assert_fails('set msghistory=10001', 'E474:')
call assert_fails('set numberwidth=21', 'E474:')
call assert_fails('set colorcolumn=-a', 'E474:')
call assert_fails('set colorcolumn=a', 'E474:')
@@ -757,7 +760,6 @@ func Test_set_option_errors()
endif
call assert_fails('set helpheight=-1', 'E487:')
call assert_fails('set history=-1', 'E487:')
- call assert_fails('set msghistory=-1', 'E487:')
call assert_fails('set report=-1', 'E487:')
call assert_fails('set shiftwidth=-1', 'E487:')
call assert_fails('set sidescroll=-1', 'E487:')
@@ -2517,6 +2519,7 @@ func Test_string_option_revert_on_failure()
\ ['lispoptions', 'expr:1', 'a123'],
\ ['listchars', 'tab:->', 'tab:'],
\ ['matchpairs', '<:>', '<:'],
+ \ ['messagesopt', 'hit-enter,history:100', 'a123'],
\ ['mkspellmem', '100000,1000,100', '100000'],
\ ['mouse', 'nvi', 'z'],
\ ['mousemodel', 'extend', 'a123'],