aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.styluaignore1
-rw-r--r--CMakeLists.txt17
-rw-r--r--MAINTAIN.md6
-rw-r--r--cmake.deps/deps.txt4
-rw-r--r--runtime/autoload/netrw.vim2
-rw-r--r--runtime/doc/api.txt75
-rw-r--r--runtime/doc/deprecated.txt2
-rw-r--r--runtime/doc/develop.txt7
-rw-r--r--runtime/doc/diagnostic.txt18
-rw-r--r--runtime/doc/fold.txt6
-rw-r--r--runtime/doc/intro.txt3
-rw-r--r--runtime/doc/lsp.txt147
-rw-r--r--runtime/doc/lua.txt751
-rw-r--r--runtime/doc/motion.txt2
-rw-r--r--runtime/doc/news.txt7
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--runtime/doc/treesitter.txt128
-rw-r--r--runtime/ftplugin/forth.vim5
-rw-r--r--runtime/ftplugin/kotlin.vim33
-rw-r--r--runtime/indent/kotlin.vim60
-rw-r--r--runtime/lua/vim/F.lua5
-rw-r--r--runtime/lua/vim/_editor.lua96
-rw-r--r--runtime/lua/vim/_meta/api.lua96
-rw-r--r--runtime/lua/vim/_meta/builtin.lua48
-rw-r--r--runtime/lua/vim/_meta/diff.lua35
-rw-r--r--runtime/lua/vim/_meta/json.lua37
-rw-r--r--runtime/lua/vim/_meta/misc.lua8
-rw-r--r--runtime/lua/vim/_meta/options.lua52
-rw-r--r--runtime/lua/vim/_meta/spell.lua15
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua6
-rw-r--r--runtime/lua/vim/_options.lua236
-rw-r--r--runtime/lua/vim/diagnostic.lua32
-rw-r--r--runtime/lua/vim/filetype.lua102
-rw-r--r--runtime/lua/vim/fs.lua27
-rw-r--r--runtime/lua/vim/highlight.lua31
-rw-r--r--runtime/lua/vim/keymap.lua39
-rw-r--r--runtime/lua/vim/lsp.lua19
-rw-r--r--runtime/lua/vim/lsp/buf.lua23
-rw-r--r--runtime/lua/vim/lsp/codelens.lua6
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua10
-rw-r--r--runtime/lua/vim/lsp/handlers.lua43
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua7
-rw-r--r--runtime/lua/vim/shared.lua120
-rw-r--r--runtime/lua/vim/treesitter.lua20
-rw-r--r--runtime/lua/vim/treesitter/_meta.lua3
-rw-r--r--runtime/lua/vim/treesitter/_query_linter.lua177
-rw-r--r--runtime/lua/vim/treesitter/dev.lua57
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua63
-rw-r--r--runtime/lua/vim/treesitter/language.lua4
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua108
-rw-r--r--runtime/lua/vim/treesitter/query.lua51
-rw-r--r--runtime/lua/vim/ui.lua54
-rw-r--r--runtime/lua/vim/version.lua131
-rw-r--r--runtime/plugin/nvim.lua6
-rw-r--r--runtime/syntax/kotlin.vim157
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor20
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor.json1
-rw-r--r--runtime/tutor/tutor.tutor10
-rw-r--r--runtime/tutor/tutor.tutor.json66
-rwxr-xr-xscripts/gen_eval_files.lua15
-rwxr-xr-xscripts/gen_vimdoc.py37
-rw-r--r--src/nvim/api/autocmd.c73
-rw-r--r--src/nvim/api/buffer.c14
-rw-r--r--src/nvim/api/command.c11
-rw-r--r--src/nvim/api/extmark.c42
-rw-r--r--src/nvim/api/vim.c32
-rw-r--r--src/nvim/api/win_config.c22
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/decoration.c39
-rw-r--r--src/nvim/decoration.h2
-rw-r--r--src/nvim/drawline.c76
-rw-r--r--src/nvim/edit.c5
-rw-r--r--src/nvim/eval.c101
-rw-r--r--src/nvim/eval.lua6
-rw-r--r--src/nvim/fold.c39
-rw-r--r--src/nvim/highlight_group.c6
-rw-r--r--src/nvim/mapping.c158
-rw-r--r--src/nvim/marktree.c9
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/os/pty_process_win.c2
-rw-r--r--src/nvim/regexp.c51
-rw-r--r--src/nvim/spell.c2
-rw-r--r--test/README.md22
-rw-r--r--test/functional/api/extmark_spec.lua17
-rw-r--r--test/functional/api/highlight_spec.lua17
-rw-r--r--test/functional/lua/buffer_updates_spec.lua56
-rw-r--r--test/functional/lua/fs_spec.lua12
-rw-r--r--test/functional/lua/vim_spec.lua17
-rw-r--r--test/functional/plugin/man_spec.lua3
-rw-r--r--test/functional/terminal/edit_spec.lua1
-rw-r--r--test/functional/terminal/tui_spec.lua15
-rw-r--r--test/functional/treesitter/highlight_spec.lua117
-rw-r--r--test/functional/ui/decorations_spec.lua240
-rw-r--r--test/functional/ui/float_spec.lua40
-rw-r--r--test/functional/ui/fold_spec.lua234
-rw-r--r--test/old/testdir/test_filetype.vim1
-rw-r--r--test/unit/marktree_spec.lua106
98 files changed, 2992 insertions, 1956 deletions
diff --git a/.styluaignore b/.styluaignore
index 11aa24df3a..786a9ce4d3 100644
--- a/.styluaignore
+++ b/.styluaignore
@@ -1,6 +1,7 @@
/scripts
/src
/test
+/build
/runtime/lua/vim/re.lua
/runtime/lua/vim/_meta/options.lua
/runtime/lua/coxpcall.lua
diff --git a/CMakeLists.txt b/CMakeLists.txt
index aec39109e4..f0303be3eb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -230,18 +230,11 @@ add_glob_target(
TOUCH_STRATEGY SINGLE)
add_dependencies(lintlua-luacheck lua-dev-deps)
-add_glob_target(
- TARGET lintlua-stylua
- COMMAND ${STYLUA_PRG}
- FLAGS --color=always --check
- GLOB_DIRS runtime/
- GLOB_PAT *.lua
- EXCLUDE
- /runtime/lua/vim/re.lua
- /runtime/lua/vim/_meta/.*
- /runtime/lua/coxpcall.lua
- TOUCH_STRATEGY SINGLE)
-
+# Don't use add_glob_target as .styluaignore won't be respected.
+# https://github.com/JohnnyMorganz/StyLua/issues/751
+add_custom_target(lintlua-stylua
+ COMMAND ${STYLUA_PRG} --color=always --check .
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
add_custom_target(lintlua)
add_dependencies(lintlua lintlua-luacheck lintlua-stylua)
diff --git a/MAINTAIN.md b/MAINTAIN.md
index f3fe873a84..3e31fde118 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -69,7 +69,7 @@ remove existing features, or refactor parts of the code that would change
user's workflow. In these cases, a deprecation policy is needed to properly
inform users of the change.
-In general, when a feature is slated to be removed it should:
+When a (non-experimental) feature is slated to be removed it should:
1. Be _soft_ deprecated in the _next_ release
- Use of the deprecated feature will still work.
@@ -106,6 +106,10 @@ Feature removals which may benefit from community input or further discussion
should also have a tracking issue (which should be linked to in the release
notes).
+Exceptions to this policy may be made (for experimental subsystems or when
+there is broad consensus among maintainers). The rationale for the exception
+should be stated explicitly and publicly.
+
Third-party dependencies
------------------------
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index a890ec894d..945f8d5f45 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -4,8 +4,8 @@ LIBUV_SHA256 7aa66be3413ae10605e1f5c9ae934504ffe317ef68ea16fdaa83e23905c681bd
MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz
MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba
-LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/8af63f992058ebbac2d72ef92811cf22a90fa347.tar.gz
-LUAJIT_SHA256 dd97df7f2ef20721549cfd2ddb2c23b4b3f450358c525ea57a46f67ceb5ba813
+LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/e897c5743f97a6b05c59852709092e7da4119914.tar.gz
+LUAJIT_SHA256 f28030c61602bffd28a445f3fef23198264a845aab94d909587bac6c12c8b874
LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz
LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim
index be170d8aec..2dd5cf45bf 100644
--- a/runtime/autoload/netrw.vim
+++ b/runtime/autoload/netrw.vim
@@ -2951,7 +2951,7 @@ fun! s:NetrwGetFile(readcmd, tfile, method)
" to process this detection correctly.
" call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>"))
" call Decho("..did_filetype()=".did_filetype())
- setl ft=
+" setl ft=
" call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">")
let iskkeep= &isk
setl isk-=/
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index d710217305..f04fe9bb87 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1791,9 +1791,9 @@ nvim_create_user_command({name}, {command}, {*opts})
For Lua usage see |lua-guide-commands-create|.
Example: >vim
- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
- :SayHello
- Hello world!
+ :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
+ :SayHello
+ Hello world!
<
Parameters: ~
@@ -2041,10 +2041,14 @@ whether a buffer is loaded.
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as Lua callbacks.
- Example (Lua): capture buffer updates in a global `events` variable (use "vim.print(events)" to see its contents): >lua
- events = {}
- vim.api.nvim_buf_attach(0, false, {
- on_lines=function(...) table.insert(events, {...}) end})
+ Example (Lua): capture buffer updates in a global `events` variable (use
+ "vim.print(events)" to see its contents): >lua
+ events = {}
+ vim.api.nvim_buf_attach(0, false, {
+ on_lines = function(...)
+ table.insert(events, {...})
+ end,
+ })
<
Parameters: ~
@@ -2553,8 +2557,8 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
Region can be given as (row,col) tuples, or valid extmark ids (whose
positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
respectively, thus the following are equivalent: >lua
- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+ vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+ vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
<
If `end` is less than `start`, traversal works backwards. (Useful with
@@ -2565,18 +2569,18 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
an extmark will be considered.
Example: >lua
- local api = vim.api
- local pos = api.nvim_win_get_cursor(0)
- local ns = api.nvim_create_namespace('my-plugin')
- -- Create new extmark at line 1, column 1.
- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
- -- Create new extmark at line 3, column 1.
- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
- -- Get extmarks only from line 3.
- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
- -- Get all marks in this buffer + namespace.
- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
- vim.print(ms)
+ local api = vim.api
+ local pos = api.nvim_win_get_cursor(0)
+ local ns = api.nvim_create_namespace('my-plugin')
+ -- Create new extmark at line 1, column 1.
+ local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+ -- Create new extmark at line 3, column 1.
+ local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+ -- Get extmarks only from line 3.
+ local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+ -- Get all marks in this buffer + namespace.
+ local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+ vim.print(ms)
<
Parameters: ~
@@ -3062,6 +3066,7 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
Example (Lua): buffer-relative float (travels as buffer is scrolled) >lua
vim.api.nvim_open_win(0, false,
{relative='win', width=12, height=3, bufpos={100,10}})
+ })
<
Attributes: ~
@@ -3342,9 +3347,9 @@ nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()*
})
<
- Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like
- "$HOME" and "~" must be expanded explicitly: >lua
- pattern = vim.fn.expand("~") .. "/some/path/*.py"
+ Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|),
+ thus names like "$HOME" and "~" must be expanded explicitly: >lua
+ pattern = vim.fn.expand("~") .. "/some/path/*.py"
<
Parameters: ~
@@ -3449,17 +3454,17 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()*
Get all autocommands that match the corresponding {opts}.
These examples will get autocommands matching ALL the given criteria: >lua
- -- Matches all criteria
- autocommands = vim.api.nvim_get_autocmds({
- group = "MyGroup",
- event = {"BufEnter", "BufWinEnter"},
- pattern = {"*.c", "*.h"}
- })
-
- -- All commands from one group
- autocommands = vim.api.nvim_get_autocmds({
- group = "MyGroup",
- })
+ -- Matches all criteria
+ autocommands = vim.api.nvim_get_autocmds({
+ group = "MyGroup",
+ event = {"BufEnter", "BufWinEnter"},
+ pattern = {"*.c", "*.h"}
+ })
+
+ -- All commands from one group
+ autocommands = vim.api.nvim_get_autocmds({
+ group = "MyGroup",
+ })
<
NOTE: When multiple patterns or events are provided, it will find all the
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 81dd1e64bf..2e9312cf74 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -151,6 +151,8 @@ TREESITTER FUNCTIONS
and |TSNode:type()| instead.
- *vim.treesitter.query.get_query()* Use |vim.treesitter.query.get()|
instead.
+- *LanguageTree:for_each_child()* Use |LanguageTree:children()|
+ (non-recursive) instead.
LUA
- vim.register_keystroke_callback() Use |vim.on_key()| instead.
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index 0ed537c248..71c16659eb 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -238,7 +238,7 @@ Docstring format:
`---@see`, `---@param`, `---@returns`
- Limited markdown is supported.
- List-items start with `-` (useful to nest or "indent")
-- Use `<pre>` for code samples.
+- Use ``` for code samples.
Code samples can be annotated as `vim` or `lua`
- Use `@nodoc` to prevent documentation generation.
- Files which has `@meta` are only used for typing and documentation.
@@ -250,12 +250,13 @@ vim.paste in runtime/lua/vim/_editor.lua like this: >
--- (such as the |TUI|) pastes text into the editor.
---
--- Example: To remove ANSI color codes when pasting:
- --- <pre>lua
+ ---
+ --- ```lua
--- vim.paste = (function()
--- local overridden = vim.paste
--- ...
--- end)()
- --- </pre>
+ --- ```
---
---@see |paste|
---
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index 866c32722a..e9ca9ee347 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -359,13 +359,11 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
followed by namespace configuration, and finally global configuration.
For example, if a user enables virtual text globally with >lua
-
- vim.diagnostic.config({ virtual_text = true })
+ vim.diagnostic.config({ virtual_text = true })
<
and a diagnostic producer sets diagnostics with >lua
-
- vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
+ vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
<
then virtual text will not be enabled for those diagnostics.
@@ -608,16 +606,14 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
Parse a diagnostic from a string.
For example, consider a line of output from a linter: >
-
- WARNING filename:27:3: Variable 'foo' does not exist
+ WARNING filename:27:3: Variable 'foo' does not exist
<
This can be parsed into a diagnostic |diagnostic-structure| with: >lua
-
- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
- local groups = { "severity", "lnum", "col", "message" }
- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+ local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+ local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+ local groups = { "severity", "lnum", "col", "message" }
+ vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
<
Parameters: ~
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index 24d7605e4a..8f7393f5e3 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -520,8 +520,10 @@ expression. It can use these special Vim variables:
foldlevel.
v:foldlevel the foldlevel of the fold
-In the result a TAB is replaced with a space and unprintable characters are
-made into printable characters.
+If the result is a |List|, it is parsed and drawn like "overlay" virtual text
+(see |nvim_buf_set_extmark()|), otherwise the result is converted to a string
+where a TAB is replaced with a space and unprintable characters are made into
+printable characters.
The resulting line is truncated to fit in the window, it never wraps.
When there is room after the text, it is filled with the character specified
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index e1a92bc87b..975aff6bf6 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -22,8 +22,7 @@ is not located in the default place. You can jump to subjects like with tags:
Use CTRL-] to jump to a subject under the cursor, use CTRL-T to jump back.
*pronounce*
-Vim is pronounced as one word, like Jim. So Nvim is N-Jim, which sounds like
-"Ninja". Starting Nvim is like performing a roundhouse kick.
+Vim is pronounced as one word, like Jim. So Nvim is "En-Vim", two syllables.
This manual is a reference for all Nvim editor and API features. It is not an
introduction; instead for beginners, there is a hands-on |tutor| and a user
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 29c08fb32d..5103cc223f 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -901,12 +901,11 @@ start({config}, {opts}) *vim.lsp.start()*
the current buffer to the client.
Example: >lua
-
- vim.lsp.start({
- name = 'my-server-name',
- cmd = {'name-of-language-server-executable'},
- root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
- })
+ vim.lsp.start({
+ name = 'my-server-name',
+ cmd = {'name-of-language-server-executable'},
+ root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
+ })
<
See |vim.lsp.start_client()| for all available options. The most important
@@ -1078,9 +1077,9 @@ status() *vim.lsp.status()*
stop_client({client_id}, {force}) *vim.lsp.stop_client()*
Stops a client(s).
- You can also use the `stop()` function on a |vim.lsp.client| object. To stop all clients: >lua
-
- vim.lsp.stop_client(vim.lsp.get_clients())
+ You can also use the `stop()` function on a |vim.lsp.client| object. To
+ stop all clients: >lua
+ vim.lsp.stop_client(vim.lsp.get_clients())
<
By default asks the server to shutdown, unless stop was requested already
@@ -1196,10 +1195,10 @@ definition({options}) *vim.lsp.buf.definition()*
document_highlight() *vim.lsp.buf.document_highlight()*
Send request to the server to resolve document highlights for the current
text document position. This request can be triggered by a key mapping or
- by events such as `CursorHold` , e.g.: >vim
- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
+ by events such as `CursorHold`, e.g.: >vim
+ autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
+ autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
+ autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
<
Note: Usage of |vim.lsp.buf.document_highlight()| requires the following
@@ -1242,12 +1241,12 @@ format({options}) *vim.lsp.buf.format()*
buffer (0).
• filter (function|nil): Predicate used to filter clients.
Receives a client as argument and must return a boolean.
- Clients matching the predicate are included. Example: • >lua
+ Clients matching the predicate are included. Example: >lua
- -- Never request typescript-language-server for formatting
- vim.lsp.buf.format {
- filter = function(client) return client.name ~= "tsserver" end
- }
+ -- Never request typescript-language-server for formatting
+ vim.lsp.buf.format {
+ filter = function(client) return client.name ~= "tsserver" end
+ }
<
• async boolean|nil If true the method won't block.
Defaults to false. Editing the buffer while formatting
@@ -1366,24 +1365,23 @@ on_diagnostic({_}, {result}, {ctx}, {config})
See |vim.diagnostic.config()| for configuration options. Handler-specific
configuration can be set using |vim.lsp.with()|: >lua
-
- vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with(
- vim.lsp.diagnostic.on_diagnostic, {
- -- Enable underline, use default values
- underline = true,
- -- Enable virtual text, override spacing to 4
- virtual_text = {
- spacing = 4,
- },
- -- Use a function to dynamically turn signs off
- -- and on, using buffer local variables
- signs = function(namespace, bufnr)
- return vim.b[bufnr].show_signs == true
- end,
- -- Disable a feature
- update_in_insert = false,
- }
- )
+ vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with(
+ vim.lsp.diagnostic.on_diagnostic, {
+ -- Enable underline, use default values
+ underline = true,
+ -- Enable virtual text, override spacing to 4
+ virtual_text = {
+ spacing = 4,
+ },
+ -- Use a function to dynamically turn signs off
+ -- and on, using buffer local variables
+ signs = function(namespace, bufnr)
+ return vim.b[bufnr].show_signs == true
+ end,
+ -- Disable a feature
+ update_in_insert = false,
+ }
+ )
<
Parameters: ~
@@ -1395,24 +1393,23 @@ on_publish_diagnostics({_}, {result}, {ctx}, {config})
See |vim.diagnostic.config()| for configuration options. Handler-specific
configuration can be set using |vim.lsp.with()|: >lua
-
- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
- vim.lsp.diagnostic.on_publish_diagnostics, {
- -- Enable underline, use default values
- underline = true,
- -- Enable virtual text, override spacing to 4
- virtual_text = {
- spacing = 4,
- },
- -- Use a function to dynamically turn signs off
- -- and on, using buffer local variables
- signs = function(namespace, bufnr)
- return vim.b[bufnr].show_signs == true
- end,
- -- Disable a feature
- update_in_insert = false,
- }
- )
+ vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
+ vim.lsp.diagnostic.on_publish_diagnostics, {
+ -- Enable underline, use default values
+ underline = true,
+ -- Enable virtual text, override spacing to 4
+ virtual_text = {
+ spacing = 4,
+ },
+ -- Use a function to dynamically turn signs off
+ -- and on, using buffer local variables
+ signs = function(namespace, bufnr)
+ return vim.b[bufnr].show_signs == true
+ end,
+ -- Disable a feature
+ update_in_insert = false,
+ }
+ )
<
Parameters: ~
@@ -1457,7 +1454,7 @@ refresh() *vim.lsp.codelens.refresh()*
It is recommended to trigger this using an autocmd or via keymap.
Example: >vim
- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
+ autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
<
run() *vim.lsp.codelens.run()*
@@ -1534,8 +1531,7 @@ start({bufnr}, {client_id}, {opts}) *vim.lsp.semantic_tokens.start()*
server that supports it, you can delete the semanticTokensProvider table
from the {server_capabilities} of your client in your |LspAttach| callback
or your configuration's `on_attach` callback: >lua
-
- client.server_capabilities.semanticTokensProvider = nil
+ client.server_capabilities.semanticTokensProvider = nil
<
Parameters: ~
@@ -1565,15 +1561,14 @@ Lua module: vim.lsp.handlers *lsp-handlers*
hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
|lsp-handler| for the method "textDocument/hover" >lua
-
- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
- vim.lsp.handlers.hover, {
- -- Use a sharp border with `FloatBorder` highlights
- border = "single",
- -- add the title in hover float window
- title = "hover"
- }
- )
+ vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+ vim.lsp.handlers.hover, {
+ -- Use a sharp border with `FloatBorder` highlights
+ border = "single",
+ -- add the title in hover float window
+ title = "hover"
+ }
+ )
<
Parameters: ~
@@ -1585,18 +1580,20 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
*vim.lsp.handlers.signature_help()*
signature_help({_}, {result}, {ctx}, {config})
- |lsp-handler| for the method "textDocument/signatureHelp". The active
- parameter is highlighted with |hl-LspSignatureActiveParameter|. >lua
-
- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
- vim.lsp.handlers.signature_help, {
- -- Use a sharp border with `FloatBorder` highlights
- border = "single"
- }
- )
+ |lsp-handler| for the method "textDocument/signatureHelp".
+
+ The active parameter is highlighted with |hl-LspSignatureActiveParameter|. >lua
+ vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+ vim.lsp.handlers.signature_help, {
+ -- Use a sharp border with `FloatBorder` highlights
+ border = "single"
+ }
+ )
<
Parameters: ~
+ • {result} (table) Response from the language server
+ • {ctx} (table) Client context
• {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index c7f5a292e7..6cfec45523 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -582,17 +582,20 @@ VIM.HIGHLIGHT *vim.highlight*
Nvim includes a function for highlighting a selection on yank.
-To enable it, add the following to your `init.vim` : >vim
+To enable it, add the following to your `init.vim`: >vim
au TextYankPost * silent! lua vim.highlight.on_yank()
+
<
You can customize the highlight group and the duration of the highlight
via: >vim
au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
+
<
If you want to exclude visual selections from highlighting on yank, use: >vim
au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
+
<
vim.highlight.on_yank({opts}) *vim.highlight.on_yank()*
@@ -688,19 +691,18 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
either directly or via callback arguments, are 1-based.
Examples: >lua
+ vim.diff('a\n', 'b\nc\n')
+ -- =>
+ -- @@ -1 +1,2 @@
+ -- -a
+ -- +b
+ -- +c
- vim.diff('a\n', 'b\nc\n')
- -- =>
- -- @ -1 +1,2 @
- -- -a
- -- +b
- -- +c
-
- vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
- -- =>
- -- {
- -- {1, 1, 1, 2}
- -- }
+ vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
+ -- =>
+ -- {
+ -- {1, 1, 1, 2}
+ -- }
<
Parameters: ~
@@ -781,16 +783,18 @@ vim.json.decode({str}, {opts}) *vim.json.decode()*
• Decodes empty array as `{}` (empty Lua table).
Example: >lua
-
- :lua vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}'))
- --> { bar = {}, foo = vim.empty_dict(), zub = vim.NIL }
-
- < Parameters: ~ • {str} Stringified JSON data. • {opts} Options map keys: •
- luanil: { object: bool, array: bool } • `luanil.object=true` converts `null` in JSON objects to Lua `nil` instead of `vim.NIL` . • `luanil.array=true` converts `null` in JSON arrays to Lua `nil` instead of `vim.NIL` .
+ vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}'))
+ -- { bar = {}, foo = vim.empty_dict(), zub = vim.NIL }
+<
Parameters: ~
- • {str} (string)
- • {opts} table<string,|nil any>
+ • {str} (string) Stringified JSON data.
+ • {opts} table<string,any>|nil Options table with keys:
+ • luanil: (table) Table with keys:
+ • object: (boolean) When true, converts `null` in JSON
+ objects to Lua `nil` instead of |vim.NIL|.
+ • array: (boolean) When true, converts `null` in JSON arrays
+ to Lua `nil` instead of |vim.NIL|.
Return: ~
any
@@ -817,12 +821,11 @@ vim.spell.check({str}) *vim.spell.check()*
the buffer. Consider calling this with |nvim_buf_call()|.
Example: >lua
-
- vim.spell.check("the quik brown fox")
- -- =>
- -- {
- -- {'quik', 'bad', 5}
- -- }
+ vim.spell.check("the quik brown fox")
+ -- =>
+ -- {
+ -- {'quik', 'bad', 5}
+ -- }
<
Parameters: ~
@@ -978,14 +981,13 @@ vim.str_utf_end({str}, {index}) *vim.str_utf_end()*
(character) that {index} points to.
Examples: >lua
+ -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
- -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
-
- -- Returns 0 because the index is pointing at the last byte of a character
- vim.str_utf_end('æ', 2)
+ -- Returns 0 because the index is pointing at the last byte of a character
+ vim.str_utf_end('æ', 2)
- -- Returns 1 because the index is pointing at the penultimate byte of a character
- vim.str_utf_end('æ', 1)
+ -- Returns 1 because the index is pointing at the penultimate byte of a character
+ vim.str_utf_end('æ', 1)
<
Parameters: ~
@@ -1015,14 +1017,13 @@ vim.str_utf_start({str}, {index}) *vim.str_utf_start()*
character.
Examples: >lua
+ -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
- -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
+ -- Returns 0 because the index is pointing at the first byte of a character
+ vim.str_utf_start('æ', 1)
- -- Returns 0 because the index is pointing at the first byte of a character
- vim.str_utf_start('æ', 1)
-
- -- Returns -1 because the index is pointing at the second byte of a character
- vim.str_utf_start('æ', 2)
+ -- Returns -1 because the index is pointing at the second byte of a character
+ vim.str_utf_start('æ', 2)
<
Parameters: ~
@@ -1080,20 +1081,19 @@ vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()*
likewise experimental).
Example (stub for a |ui-popupmenu| implementation): >lua
-
- ns = vim.api.nvim_create_namespace('my_fancy_pum')
-
- vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
- if event == "popupmenu_show" then
- local items, selected, row, col, grid = ...
- print("display pum ", #items)
- elseif event == "popupmenu_select" then
- local selected = ...
- print("selected", selected)
- elseif event == "popupmenu_hide" then
- print("FIN")
- end
- end)
+ ns = vim.api.nvim_create_namespace('my_fancy_pum')
+
+ vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
+ if event == "popupmenu_show" then
+ local items, selected, row, col, grid = ...
+ print("display pum ", #items)
+ elseif event == "popupmenu_select" then
+ local selected = ...
+ print("selected", selected)
+ elseif event == "popupmenu_hide" then
+ print("FIN")
+ end
+ end)
<
Parameters: ~
@@ -1116,27 +1116,26 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
time.
Examples: >lua
+ ---
+ -- Wait for 100 ms, allowing other events to process
+ vim.wait(100, function() end)
- ---
- -- Wait for 100 ms, allowing other events to process
- vim.wait(100, function() end)
+ ---
+ -- Wait for 100 ms or until global variable set.
+ vim.wait(100, function() return vim.g.waiting_for_var end)
- ---
- -- Wait for 100 ms or until global variable set.
- vim.wait(100, function() return vim.g.waiting_for_var end)
+ ---
+ -- Wait for 1 second or until global variable set, checking every ~500 ms
+ vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
- ---
- -- Wait for 1 second or until global variable set, checking every ~500 ms
- vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
+ ---
+ -- Schedule a function to set a value in 100ms
+ vim.defer_fn(function() vim.g.timer_result = true end, 100)
- ---
- -- Schedule a function to set a value in 100ms
- vim.defer_fn(function() vim.g.timer_result = true end, 100)
-
- -- Would wait ten seconds if results blocked. Actually only waits 100 ms
- if vim.wait(10000, function() return vim.g.timer_result end) then
- print('Only waiting a little bit of time!')
- end
+ -- Would wait ten seconds if results blocked. Actually only waits 100 ms
+ if vim.wait(10000, function() return vim.g.timer_result end) then
+ print('Only waiting a little bit of time!')
+ end
<
Parameters: ~
@@ -1171,6 +1170,7 @@ Lua list: >lua
local list = { 1, 2, 3 }
vim.fn.remove(list, 0)
vim.print(list) --> "{ 1, 2, 3 }"
+
<
vim.call({func}, {...}) *vim.call()*
@@ -1430,13 +1430,12 @@ vim.bo *vim.bo*
print(vim.bo.baz) -- error: invalid key
<
- Parameters: ~
- • {bufnr} (integer|nil)
-
vim.env *vim.env*
Environment variables defined in the editor session. See |expand-env| and
|:let-environment| for the Vimscript behavior. Invalid or unset key
- returns `nil` . Example: >lua
+ returns `nil`.
+
+ Example: >lua
vim.env.FOO = 'bar'
print(vim.env.TERM)
<
@@ -1497,31 +1496,30 @@ vim.cmd *vim.cmd()*
callable function to the command.
Example: >lua
+ vim.cmd('echo 42')
+ vim.cmd([[
+ augroup My_group
+ autocmd!
+ autocmd FileType c setlocal cindent
+ augroup END
+ ]])
- vim.cmd('echo 42')
- vim.cmd([[
- augroup My_group
- autocmd!
- autocmd FileType c setlocal cindent
- augroup END
- ]])
+ -- Ex command :echo "foo"
+ -- Note string literals need to be double quoted.
+ vim.cmd('echo "foo"')
+ vim.cmd { cmd = 'echo', args = { '"foo"' } }
+ vim.cmd.echo({ args = { '"foo"' } })
+ vim.cmd.echo('"foo"')
- -- Ex command :echo "foo"
- -- Note string literals need to be double quoted.
- vim.cmd('echo "foo"')
- vim.cmd { cmd = 'echo', args = { '"foo"' } }
- vim.cmd.echo({ args = { '"foo"' } })
- vim.cmd.echo('"foo"')
+ -- Ex command :write! myfile.txt
+ vim.cmd('write! myfile.txt')
+ vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true }
+ vim.cmd.write { args = { "myfile.txt" }, bang = true }
+ vim.cmd.write { "myfile.txt", bang = true }
- -- Ex command :write! myfile.txt
- vim.cmd('write! myfile.txt')
- vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true }
- vim.cmd.write { args = { "myfile.txt" }, bang = true }
- vim.cmd.write { "myfile.txt", bang = true }
-
- -- Ex command :colorscheme blue
- vim.cmd('colorscheme blue')
- vim.cmd.colorscheme('blue')
+ -- Ex command :colorscheme blue
+ vim.cmd('colorscheme blue')
+ vim.cmd.colorscheme('blue')
<
Parameters: ~
@@ -1579,9 +1577,8 @@ vim.keycode({str}) *vim.keycode()*
Translate keycodes.
Example: >lua
-
- local k = vim.keycode
- vim.g.mapleader = k'<bs>'
+ local k = vim.keycode
+ vim.g.mapleader = k'<bs>'
<
Parameters: ~
@@ -1653,16 +1650,15 @@ vim.paste({lines}, {phase}) *vim.paste()*
|TUI|) pastes text into the editor.
Example: To remove ANSI color codes when pasting: >lua
-
- vim.paste = (function(overridden)
- return function(lines, phase)
- for i,line in ipairs(lines) do
- -- Scrub ANSI color codes from paste input.
- lines[i] = line:gsub('\27%[[0-9;mK]+', '')
- end
- overridden(lines, phase)
- end
- end)(vim.paste)
+ vim.paste = (function(overridden)
+ return function(lines, phase)
+ for i,line in ipairs(lines) do
+ -- Scrub ANSI color codes from paste input.
+ lines[i] = line:gsub('\27%[[0-9;mK]+', '')
+ end
+ overridden(lines, phase)
+ end
+ end)(vim.paste)
<
Parameters: ~
@@ -1684,8 +1680,7 @@ vim.print({...}) *vim.print()*
"Pretty prints" the given arguments and returns them unmodified.
Example: >lua
-
- local hl_normal = vim.print(vim.api.nvim_get_hl_by_name('Normal', true))
+ local hl_normal = vim.print(vim.api.nvim_get_hl_by_name('Normal', true))
<
Return: ~
@@ -1737,20 +1732,19 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
Run a system command
Examples: >lua
+ local on_exit = function(obj)
+ print(obj.code)
+ print(obj.signal)
+ print(obj.stdout)
+ print(obj.stderr)
+ end
- local on_exit = function(obj)
- print(obj.code)
- print(obj.signal)
- print(obj.stdout)
- print(obj.stderr)
- end
-
- -- Run asynchronously
- vim.system({'echo', 'hello'}, { text = true }, on_exit)
+ -- Run asynchronously
+ vim.system({'echo', 'hello'}, { text = true }, on_exit)
- -- Run synchronously
- local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
+ -- Run synchronously
+ local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
+ -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
<
See |uv.spawn()| for more details.
@@ -1894,12 +1888,9 @@ vim.defaulttable({create}) *vim.defaulttable()*
If {create} is `nil`, this will create a defaulttable whose constructor
function is this function, effectively allowing to create nested tables on
- the fly:
-
- >lua
-
- local a = vim.defaulttable()
- a.b.c = 1
+ the fly: >lua
+ local a = vim.defaulttable()
+ a.b.c = 1
<
Parameters: ~
@@ -1924,18 +1915,16 @@ vim.gsplit({s}, {sep}, {opts}) *vim.gsplit()*
in "lazy" fashion (as opposed to |vim.split()| which is "eager").
Example: >lua
-
- for s in vim.gsplit(':aa::b:', ':', {plain=true}) do
- print(s)
- end
+ for s in vim.gsplit(':aa::b:', ':', {plain=true}) do
+ print(s)
+ end
<
If you want to also inspect the separator itself (instead of discarding
it), use |string.gmatch()|. Example: >lua
-
- for word, num in ('foo111bar222'):gmatch('([^0-9]*)(d*)') do
- print(('word: s num: s'):format(word, num))
- end
+ for word, num in ('foo111bar222'):gmatch('([^0-9]*)(%d*)') do
+ print(('word: %s num: %s'):format(word, num))
+ end
<
Parameters: ~
@@ -2021,22 +2010,20 @@ vim.pesc({s}) *vim.pesc()*
vim.ringbuf({size}) *vim.ringbuf()*
Create a ring buffer limited to a maximal number of items. Once the buffer
- is full, adding a new entry overrides the oldest entry.
->
-
- local ringbuf = vim.ringbuf(4)
- ringbuf:push("a")
- ringbuf:push("b")
- ringbuf:push("c")
- ringbuf:push("d")
- ringbuf:push("e") -- overrides "a"
- print(ringbuf:pop()) -- returns "b"
- print(ringbuf:pop()) -- returns "c"
-
- -- Can be used as iterator. Pops remaining items:
- for val in ringbuf do
- print(val)
- end
+ is full, adding a new entry overrides the oldest entry. >lua
+ local ringbuf = vim.ringbuf(4)
+ ringbuf:push("a")
+ ringbuf:push("b")
+ ringbuf:push("c")
+ ringbuf:push("d")
+ ringbuf:push("e") -- overrides "a"
+ print(ringbuf:pop()) -- returns "b"
+ print(ringbuf:pop()) -- returns "c"
+
+ -- Can be used as iterator. Pops remaining items:
+ for val in ringbuf do
+ print(val)
+ end
<
Returns a Ringbuf instance with the following methods:
@@ -2090,11 +2077,10 @@ vim.split({s}, {sep}, {opts}) *vim.split()*
a table (unlike |vim.gsplit()|).
Examples: >lua
-
- split(":aa::b:", ":") --> {'','aa','','b',''}
- split("axaby", "ab?") --> {'','x','y'}
- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
+ split(":aa::b:", ":") --> {'','aa','','b',''}
+ split("axaby", "ab?") --> {'','x','y'}
+ split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
+ split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
<
Parameters: ~
@@ -2137,11 +2123,10 @@ vim.tbl_contains({t}, {value}, {opts}) *vim.tbl_contains()*
a predicate that is checked for each value.
Example: >lua
-
- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
- return vim.deep_equal(v, { 'b', 'c' })
- end, { predicate = true })
- -- true
+ vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+ return vim.deep_equal(v, { 'b', 'c' })
+ end, { predicate = true })
+ -- true
<
Parameters: ~
@@ -2158,12 +2143,9 @@ vim.tbl_contains({t}, {value}, {opts}) *vim.tbl_contains()*
• |vim.list_contains()| for checking values in list-like tables
vim.tbl_count({t}) *vim.tbl_count()*
- Counts the number of non-nil values in table `t`.
-
- >lua
-
- vim.tbl_count({ a=1, b=2 }) --> 2
- vim.tbl_count({ 1, 2 }) --> 2
+ Counts the number of non-nil values in table `t`. >lua
+ vim.tbl_count({ a=1, b=2 }) --> 2
+ vim.tbl_count({ 1, 2 }) --> 2
<
Parameters: ~
@@ -2237,9 +2219,8 @@ vim.tbl_get({o}, {...}) *vim.tbl_get()*
arguments. Return `nil` if the key does not exist.
Examples: >lua
-
- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
+ vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
+ vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
<
Parameters: ~
@@ -2340,36 +2321,33 @@ vim.validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
Usage example: >lua
-
- function user.new(name, age, hobbies)
- vim.validate{
- name={name, 'string'},
- age={age, 'number'},
- hobbies={hobbies, 'table'},
- }
- ...
- end
+ function user.new(name, age, hobbies)
+ vim.validate{
+ name={name, 'string'},
+ age={age, 'number'},
+ hobbies={hobbies, 'table'},
+ }
+ ...
+ end
<
Examples with explicit argument values (can be run directly): >lua
+ vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
+ --> NOP (success)
- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
- --> NOP (success)
-
- vim.validate{arg1={1, 'table'}}
- --> error('arg1: expected table, got number')
+ vim.validate{arg1={1, 'table'}}
+ --> error('arg1: expected table, got number')
- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
- --> error('arg1: expected even number, got 3')
+ vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
+ --> error('arg1: expected even number, got 3')
<
If multiple types are valid they can be given as a list. >lua
+ vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
+ -- NOP (success)
- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
- --> NOP (success)
-
- vim.validate{arg1={1, {'string', 'table'}}}
- --> error('arg1: expected string|table, got number')
+ vim.validate{arg1={1, {'string', 'table'}}}
+ -- error('arg1: expected string|table, got number')
<
Parameters: ~
@@ -2506,10 +2484,9 @@ vim.ui.input({opts}, {on_confirm}) *vim.ui.input()*
work until `on_confirm`.
Example: >lua
-
- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
- vim.o.shiftwidth = tonumber(input)
- end)
+ vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
+ vim.o.shiftwidth = tonumber(input)
+ end)
<
Parameters: ~
@@ -2535,10 +2512,9 @@ vim.ui.open({path}) *vim.ui.open()*
Expands "~/" and environment variables in filesystem paths.
Examples: >lua
-
- vim.ui.open("https://neovim.io/")
- vim.ui.open("~/path/to/file")
- vim.ui.open("$VIMRUNTIME")
+ vim.ui.open("https://neovim.io/")
+ vim.ui.open("~/path/to/file")
+ vim.ui.open("$VIMRUNTIME")
<
Parameters: ~
@@ -2556,19 +2532,18 @@ vim.ui.select({items}, {opts}, {on_choice}) *vim.ui.select()*
(potentially asynchronous) work until `on_choice`.
Example: >lua
-
- vim.ui.select({ 'tabs', 'spaces' }, {
- prompt = 'Select tabs or spaces:',
- format_item = function(item)
- return "I'd like to choose " .. item
- end,
- }, function(choice)
- if choice == 'spaces' then
- vim.o.expandtab = true
- else
- vim.o.expandtab = false
- end
- end)
+ vim.ui.select({ 'tabs', 'spaces' }, {
+ prompt = 'Select tabs or spaces:',
+ format_item = function(item)
+ return "I'd like to choose " .. item
+ end,
+ }, function(choice)
+ if choice == 'spaces' then
+ vim.o.expandtab = true
+ else
+ vim.o.expandtab = false
+ end
+ end)
<
Parameters: ~
@@ -2620,58 +2595,56 @@ vim.filetype.add({filetypes}) *vim.filetype.add()*
See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
Example: >lua
-
- vim.filetype.add({
- extension = {
- foo = 'fooscript',
- bar = function(path, bufnr)
- if some_condition() then
- return 'barscript', function(bufnr)
- -- Set a buffer variable
- vim.b[bufnr].barscript_version = 2
+ vim.filetype.add({
+ extension = {
+ foo = 'fooscript',
+ bar = function(path, bufnr)
+ if some_condition() then
+ return 'barscript', function(bufnr)
+ -- Set a buffer variable
+ vim.b[bufnr].barscript_version = 2
+ end
end
- end
- return 'bar'
- end,
- },
- filename = {
- ['.foorc'] = 'toml',
- ['/etc/foo/config'] = 'toml',
- },
- pattern = {
- ['.*/etc/foo/.*'] = 'fooscript',
- -- Using an optional priority
- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
- -- A pattern containing an environment variable
- ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
- ['README.(a+)$'] = function(path, bufnr, ext)
- if ext == 'md' then
- return 'markdown'
- elseif ext == 'rst' then
- return 'rst'
- end
- end,
- },
- })
+ return 'bar'
+ end,
+ },
+ filename = {
+ ['.foorc'] = 'toml',
+ ['/etc/foo/config'] = 'toml',
+ },
+ pattern = {
+ ['.*&zwj;/etc/foo/.*'] = 'fooscript',
+ -- Using an optional priority
+ ['.*&zwj;/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
+ -- A pattern containing an environment variable
+ ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
+ ['README.(%a+)$'] = function(path, bufnr, ext)
+ if ext == 'md' then
+ return 'markdown'
+ elseif ext == 'rst' then
+ return 'rst'
+ end
+ end,
+ },
+ })
<
To add a fallback match on contents, use >lua
-
- vim.filetype.add {
- pattern = {
- ['.*'] = {
- priority = -math.huge,
- function(path, bufnr)
- local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or ''
- if vim.regex([[^#!.*\<mine\>]]):match_str(content) ~= nil then
- return 'mine'
- elseif vim.regex([[\<drawing\>]]):match_str(content) ~= nil then
- return 'drawing'
- end
- end,
- },
- },
- }
+ vim.filetype.add {
+ pattern = {
+ ['.*'] = {
+ priority = -math.huge,
+ function(path, bufnr)
+ local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or ''
+ if vim.regex([[^#!.*\\<mine\\>]]):match_str(content) ~= nil then
+ return 'mine'
+ elseif vim.regex([[\\<drawing\\>]]):match_str(content) ~= nil then
+ return 'drawing'
+ end
+ end,
+ },
+ },
+ }
<
Parameters: ~
@@ -2687,8 +2660,7 @@ vim.filetype.get_option({filetype}, {option})
files.
Example: >lua
-
- vim.filetype.get_option('vim', 'commentstring')
+ vim.filetype.get_option('vim', 'commentstring')
<
Note: this uses |nvim_get_option_value()| but caches the result. This
@@ -2717,21 +2689,18 @@ vim.filetype.match({args}) *vim.filetype.match()*
the filetype.
Each of the three options is specified using a key to the single argument
- of this function. Example:
+ of this function. Example: >lua
+ -- Using a buffer number
+ vim.filetype.match({ buf = 42 })
- >lua
+ -- Override the filename of the given buffer
+ vim.filetype.match({ buf = 42, filename = 'foo.c' })
- -- Using a buffer number
- vim.filetype.match({ buf = 42 })
+ -- Using a filename without a buffer
+ vim.filetype.match({ filename = 'main.lua' })
- -- Override the filename of the given buffer
- vim.filetype.match({ buf = 42, filename = 'foo.c' })
-
- -- Using a filename without a buffer
- vim.filetype.match({ filename = 'main.lua' })
-
- -- Using file contents
- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
+ -- Using file contents
+ vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
<
Parameters: ~
@@ -2762,10 +2731,9 @@ Lua module: vim.keymap *vim.keymap*
vim.keymap.del({modes}, {lhs}, {opts}) *vim.keymap.del()*
Remove an existing mapping. Examples: >lua
+ vim.keymap.del('n', 'lhs')
- vim.keymap.del('n', 'lhs')
-
- vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 })
+ vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 })
<
Parameters: ~
@@ -2778,19 +2746,18 @@ vim.keymap.del({modes}, {lhs}, {opts}) *vim.keymap.del()*
vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
Adds a new |mapping|. Examples: >lua
-
- -- Map to a Lua function:
- vim.keymap.set('n', 'lhs', function() print("real lua function") end)
- -- Map to multiple modes:
- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true })
- -- Buffer-local mapping:
- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 })
- -- Expr mapping:
- vim.keymap.set('i', '<Tab>', function()
- return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
- end, { expr = true })
- -- <Plug> mapping:
- vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)')
+ -- Map to a Lua function:
+ vim.keymap.set('n', 'lhs', function() print("real lua function") end)
+ -- Map to multiple modes:
+ vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true })
+ -- Buffer-local mapping:
+ vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 })
+ -- Expr mapping:
+ vim.keymap.set('i', '<Tab>', function()
+ return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
+ end, { expr = true })
+ -- <Plug> mapping:
+ vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
<
Parameters: ~
@@ -2871,24 +2838,23 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
narrow the search to find only that type.
Examples: >lua
+ -- location of Cargo.toml from the current buffer's path
+ local cargo = vim.fs.find('Cargo.toml', {
+ upward = true,
+ stop = vim.uv.os_homedir(),
+ path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)),
+ })
- -- location of Cargo.toml from the current buffer's path
- local cargo = vim.fs.find('Cargo.toml', {
- upward = true,
- stop = vim.uv.os_homedir(),
- path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)),
- })
+ -- list all test directories under the runtime directory
+ local test_dirs = vim.fs.find(
+ {'test', 'tst', 'testdir'},
+ {limit = math.huge, type = 'directory', path = './runtime/'}
+ )
- -- list all test directories under the runtime directory
- local test_dirs = vim.fs.find(
- {'test', 'tst', 'testdir'},
- {limit = math.huge, type = 'directory', path = './runtime/'}
- )
-
- -- get all files ending with .cpp or .hpp inside lib/
- local cpp_hpp = vim.fs.find(function(name, path)
- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
- end, {limit = math.huge, type = 'file'})
+ -- get all files ending with .cpp or .hpp inside lib/
+ local cpp_hpp = vim.fs.find(function(name, path)
+ return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$')
+ end, {limit = math.huge, type = 'file'})
<
Parameters: ~
@@ -2934,15 +2900,14 @@ vim.fs.normalize({path}, {opts}) *vim.fs.normalize()*
variables are also expanded.
Examples: >lua
+ vim.fs.normalize('C:\\\\Users\\\\jdoe')
+ -- 'C:/Users/jdoe'
- vim.fs.normalize('C:\\Users\\jdoe')
- --> 'C:/Users/jdoe'
-
- vim.fs.normalize('~/src/neovim')
- --> '/home/jdoe/src/neovim'
+ vim.fs.normalize('~/src/neovim')
+ -- '/home/jdoe/src/neovim'
- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
- --> '/Users/jdoe/.config/nvim/init.vim'
+ vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+ -- '/Users/jdoe/.config/nvim/init.vim'
<
Parameters: ~
@@ -2958,18 +2923,17 @@ vim.fs.parents({start}) *vim.fs.parents()*
Iterate over all the parents of the given path.
Example: >lua
+ local root_dir
+ for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
+ if vim.fn.isdirectory(dir .. "/.git") == 1 then
+ root_dir = dir
+ break
+ end
+ end
- local root_dir
- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
- if vim.fn.isdirectory(dir .. "/.git") == 1 then
- root_dir = dir
- break
- end
- end
-
- if root_dir then
- print("Found git repository at", root_dir)
- end
+ if root_dir then
+ print("Found git repository at", root_dir)
+ end
<
Parameters: ~
@@ -3033,11 +2997,10 @@ spec. Plugins, and plugin managers, can use this to check available tools
and dependencies on the current system.
Example: >lua
-
- local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
- if vim.version.gt(v, {3, 2, 0}) then
- -- ...
- end
+ local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
+ if vim.version.gt(v, {3, 2, 0}) then
+ -- ...
+ end
<
@@ -3050,34 +3013,33 @@ tested against a version, using |vim.version.range()|.
Supported range specs are shown in the following table. Note: suffixed
versions (1.2.3-rc1) are not matched. >
-
- 1.2.3 is 1.2.3
- =1.2.3 is 1.2.3
- >1.2.3 greater than 1.2.3
- <1.2.3 before 1.2.3
- >=1.2.3 at least 1.2.3
- ~1.2.3 is >=1.2.3 <1.3.0 "reasonably close to 1.2.3"
- ^1.2.3 is >=1.2.3 <2.0.0 "compatible with 1.2.3"
- ^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
- ^0.0.1 is =0.0.1 (0.0.x is special)
- ^1.2 is >=1.2.0 <2.0.0 (like ^1.2.0)
- ~1.2 is >=1.2.0 <1.3.0 (like ~1.2.0)
- ^1 is >=1.0.0 <2.0.0 "compatible with 1"
- ~1 same "reasonably close to 1"
- 1.x same
- 1.* same
- 1 same
- * any version
- x same
-
- 1.2.3 - 2.3.4 is >=1.2.3 <=2.3.4
-
- Partial right: missing pieces treated as x (2.3 => 2.3.x).
- 1.2.3 - 2.3 is >=1.2.3 <2.4.0
- 1.2.3 - 2 is >=1.2.3 <3.0.0
-
- Partial left: missing pieces treated as 0 (1.2 => 1.2.0).
- 1.2 - 2.3.0 is 1.2.0 - 2.3.0
+ 1.2.3 is 1.2.3
+ =1.2.3 is 1.2.3
+ >1.2.3 greater than 1.2.3
+ <1.2.3 before 1.2.3
+ >=1.2.3 at least 1.2.3
+ ~1.2.3 is >=1.2.3 <1.3.0 "reasonably close to 1.2.3"
+ ^1.2.3 is >=1.2.3 <2.0.0 "compatible with 1.2.3"
+ ^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
+ ^0.0.1 is =0.0.1 (0.0.x is special)
+ ^1.2 is >=1.2.0 <2.0.0 (like ^1.2.0)
+ ~1.2 is >=1.2.0 <1.3.0 (like ~1.2.0)
+ ^1 is >=1.0.0 <2.0.0 "compatible with 1"
+ ~1 same "reasonably close to 1"
+ 1.x same
+ 1.* same
+ 1 same
+ * any version
+ x same
+
+ 1.2.3 - 2.3.4 is >=1.2.3 <=2.3.4
+
+ Partial right: missing pieces treated as x (2.3 => 2.3.x).
+ 1.2.3 - 2.3 is >=1.2.3 <2.4.0
+ 1.2.3 - 2 is >=1.2.3 <3.0.0
+
+ Partial left: missing pieces treated as 0 (1.2 => 1.2.0).
+ 1.2 - 2.3.0 is 1.2.0 - 2.3.0
<
@@ -3087,15 +3049,14 @@ vim.version.cmp({v1}, {v2}) *vim.version.cmp()*
tuple, e.g. `{1, 0, 3}`).
Example: >lua
-
- if vim.version.cmp({1,0,3}, {0,2,1}) == 0 then
- -- ...
- end
- local v1 = vim.version.parse('1.0.3-pre')
- local v2 = vim.version.parse('0.2.1')
- if vim.version.cmp(v1, v2) == 0 then
- -- ...
- end
+ if vim.version.cmp({1,0,3}, {0,2,1}) == 0 then
+ -- ...
+ end
+ local v1 = vim.version.parse('1.0.3-pre')
+ local v2 = vim.version.parse('0.2.1')
+ if vim.version.cmp(v1, v2) == 0 then
+ -- ...
+ end
<
Note: ~
@@ -3150,9 +3111,9 @@ vim.version.lt({v1}, {v2}) *vim.version.lt()*
vim.version.parse({version}, {opts}) *vim.version.parse()*
Parses a semantic version string and returns a version object which can be
- used with other `vim.version` functions. For example "1.0.1-rc1+build.2" returns: >
-
- { major = 1, minor = 0, patch = 1, prerelease = "rc1", build = "build.2" }
+ used with other `vim.version` functions. For example "1.0.1-rc1+build.2"
+ returns: >
+ { major = 1, minor = 0, patch = 1, prerelease = "rc1", build = "build.2" }
<
Parameters: ~
@@ -3171,29 +3132,26 @@ vim.version.parse({version}, {opts}) *vim.version.parse()*
vim.version.range({spec}) *vim.version.range()*
Parses a semver |version-range| "spec" and returns a range object: >
-
- {
- from: Version
- to: Version
- has(v: string|Version)
- }
+ {
+ from: Version
+ to: Version
+ has(v: string|Version)
+ }
<
`:has()` checks if a version is in the range (inclusive `from`, exclusive
`to`).
Example: >lua
-
- local r = vim.version.range('1.0.0 - 2.0.0')
- print(r:has('1.9.9')) -- true
- print(r:has('2.0.0')) -- false
- print(r:has(vim.version())) -- check against current Nvim version
+ local r = vim.version.range('1.0.0 - 2.0.0')
+ print(r:has('1.9.9')) -- true
+ print(r:has('2.0.0')) -- false
+ print(r:has(vim.version())) -- check against current Nvim version
<
Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly: >lua
-
- local r = vim.version.range('1.0.0 - 2.0.0')
- print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to))
+ local r = vim.version.range('1.0.0 - 2.0.0')
+ print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to))
<
Parameters: ~
@@ -3228,7 +3186,6 @@ iterator runs out of values (for function iterators, this means that the
first value returned by the function is nil).
Examples: >lua
-
local it = vim.iter({ 1, 2, 3, 4, 5 })
it:map(function(v)
return v * 3
@@ -3275,7 +3232,6 @@ filter({f}, {src}, {...}) *vim.iter.filter()*
Filter a table or iterator.
This is a convenience function that performs: >lua
-
vim.iter(src):filter(f):totable()
<
@@ -3325,19 +3281,16 @@ Iter:enumerate() *Iter:enumerate()*
the iterator value.
For list tables, prefer >lua
-
vim.iter(ipairs(t))
<
over >lua
-
vim.iter(t):enumerate()
<
as the former is faster.
Example: >lua
-
local it = vim.iter(vim.gsplit('abc', '')):enumerate()
it:next()
-- 1 'a'
@@ -3354,7 +3307,6 @@ Iter:filter({f}) *Iter:filter()*
Add a filter step to the iterator pipeline.
Example: >lua
-
local bufs = vim.iter(vim.api.nvim_list_bufs()):filter(vim.api.nvim_buf_is_loaded)
<
@@ -3373,7 +3325,6 @@ Iter:find({f}) *Iter:find()*
found.
Examples: >lua
-
local it = vim.iter({ 3, 6, 9, 12 })
it:find(12)
-- 12
@@ -3394,7 +3345,6 @@ Iter:fold({init}, {f}) *Iter:fold()*
Fold ("reduce") an iterator or table into a single value.
Examples: >lua
-
-- Create a new table with only even values
local t = { a = 1, b = 2, c = 3, d = 4 }
local it = vim.iter(t)
@@ -3419,7 +3369,6 @@ Iter:last() *Iter:last()*
Drains the iterator.
Example: >lua
-
local it = vim.iter(vim.gsplit('abcdefg', ''))
it:last()
-- 'g'
@@ -3438,7 +3387,6 @@ Iter:map({f}) *Iter:map()*
If the map function returns nil, the value is filtered from the iterator.
Example: >lua
-
local it = vim.iter({ 1, 2, 3, 4 }):map(function(v)
if v % 2 == 0 then
return v * 3
@@ -3461,7 +3409,6 @@ Iter:next() *Iter:next()*
Return the next value from the iterator.
Example: >lua
-
local it = vim.iter(string.gmatch('1 2 3', '%d+')):map(tonumber)
it:next()
-- 1
@@ -3480,7 +3427,6 @@ Iter:nextback() *Iter:nextback()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({1, 2, 3, 4})
it:nextback()
-- 4
@@ -3497,7 +3443,6 @@ Iter:nth({n}) *Iter:nth()*
This function advances the iterator.
Example: >lua
-
local it = vim.iter({ 3, 6, 9, 12 })
it:nth(2)
-- 6
@@ -3519,7 +3464,6 @@ Iter:nthback({n}) *Iter:nthback()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({ 3, 6, 9, 12 })
it:nthback(2)
-- 9
@@ -3539,7 +3483,6 @@ Iter:peek() *Iter:peek()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({ 3, 6, 9, 12 })
it:peek()
-- 3
@@ -3558,7 +3501,6 @@ Iter:peekback() *Iter:peekback()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({1, 2, 3, 4})
it:peekback()
-- 4
@@ -3577,7 +3519,6 @@ Iter:rev() *Iter:rev()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({ 3, 6, 9, 12 }):rev()
it:totable()
-- { 12, 9, 6, 3 }
@@ -3596,7 +3537,6 @@ Iter:rfind({f}) *Iter:rfind()*
Only supported for iterators on list-like tables.
Examples: >lua
-
local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate()
it:rfind(1)
-- 5 1
@@ -3614,7 +3554,6 @@ Iter:skip({n}) *Iter:skip()*
Skip values in the iterator.
Example: >lua
-
local it = vim.iter({ 3, 6, 9, 12 }):skip(2)
it:next()
-- 9
@@ -3632,7 +3571,6 @@ Iter:skipback({n}) *Iter:skipback()*
Only supported for iterators on list-like tables.
Example: >lua
-
local it = vim.iter({ 1, 2, 3, 4, 5 }):skipback(2)
it:next()
-- 1
@@ -3669,7 +3607,6 @@ Iter:totable() *Iter:totable()*
the iterator pipeline, each value will be included in a table.
Examples: >lua
-
vim.iter(string.gmatch('100 20 50', '%d+')):map(tonumber):totable()
-- { 100, 20, 50 }
@@ -3691,7 +3628,6 @@ map({f}, {src}, {...}) *vim.iter.map()*
Map and filter a table or iterator.
This is a convenience function that performs: >lua
-
vim.iter(src):map(f):totable()
<
@@ -3711,7 +3647,6 @@ totable({f}, {...}) *vim.iter.totable()*
Collect an iterator into a table.
This is a convenience function that performs: >lua
-
vim.iter(f):totable()
<
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 05244cde91..dc92601bfc 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -660,6 +660,7 @@ i` *v_i`* *i`*
Special case: With a count of 2 the quotes are
included, but no extra white space as with a"/a'/a`.
+ *o_object-select*
When used after an operator:
For non-block objects:
For the "a" commands: The operator applies to the object and the white
@@ -675,6 +676,7 @@ For a block object:
the surrounding braces are excluded. For the "a" commands, the braces
are included.
+ *v_object-select*
When used in Visual mode:
When start and end of the Visual area are the same (just after typing "v"):
One object is selected, the same as for using an operator.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 93012e78a9..7c971097fb 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -89,6 +89,8 @@ The following new APIs and features were added.
• Added inline virtual text support to |nvim_buf_set_extmark()|.
+• 'foldtext' now supports virtual text format. |fold-foldtext|
+
• The terminal buffer now supports reflow (wrapped lines adapt when the buffer
is resized horizontally). Note: Lines that are not visible and kept in
|'scrollback'| are not reflown.
@@ -135,7 +137,7 @@ The following new APIs and features were added.
`vim.treesitter.language.register`.
• The `#set!` directive now supports `injection.self` and `injection.parent` for injecting either the current node's language
or the parent LanguageTree's language, respectively.
- • Added `vim.treesitter.preview_query()`, for live editing of treesitter
+ • Added `vim.treesitter.query.edit()`, for live editing of treesitter
queries.
• Improved error messages for query parsing.
@@ -268,4 +270,7 @@ release.
• `vim.loop` has been renamed to `vim.uv`.
+• vim.treesitter.languagetree functions:
+ - |LanguageTree:for_each_child()| Use |LanguageTree:children()| (non-recursive) instead.
+
vim:tw=78:ts=8:sw=2:et:ft=help:norl:
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index f758782f09..e1518c58bb 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -945,7 +945,7 @@ A jump table for the options with a short description can be found at |Q_op|.
backups if you don't care about losing the file.
Note that environment variables are not expanded. If you want to use
- $HOME you must expand it explicitly, e.g.: >
+ $HOME you must expand it explicitly, e.g.: >vim
:let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
< Note that the default also makes sure that "crontab -e" works (when a
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 287985f75b..10f689bad7 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -551,8 +551,7 @@ Lua module: vim.treesitter *lua-treesitter-core*
foldexpr({lnum}) *vim.treesitter.foldexpr()*
Returns the fold level for {lnum} in the current buffer. Can be set
directly to 'foldexpr': >lua
-
- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
+ vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
<
Parameters: ~
@@ -677,8 +676,8 @@ inspect_tree({opts}) *vim.treesitter.inspect_tree()*
While in the window, press "a" to toggle display of anonymous nodes, "I"
to toggle the display of the source language of each node, "o" to toggle
- the query previewer, and press <Enter> to jump to the node under the
- cursor in the source buffer.
+ the query editor, and press <Enter> to jump to the node under the cursor
+ in the source buffer.
Can also be shown with `:InspectTree`. *:InspectTree*
@@ -731,11 +730,6 @@ node_contains({node}, {range}) *vim.treesitter.node_contains()*
Return: ~
(boolean) True if the {node} contains the {range}
-preview_query() *vim.treesitter.preview_query()*
- Open a window for live editing of a treesitter query.
-
- Can also be shown with `:PreviewQuery`. *:PreviewQuery*
-
start({bufnr}, {lang}) *vim.treesitter.start()*
Starts treesitter highlighting for a buffer
@@ -746,13 +740,12 @@ start({bufnr}, {lang}) *vim.treesitter.start()*
the call to `start`.
Example: >lua
-
- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
- callback = function(args)
- vim.treesitter.start(args.buf, 'latex')
- vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed
- end
- })
+ vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
+ callback = function(args)
+ vim.treesitter.start(args.buf, 'latex')
+ vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed
+ end
+ })
<
Parameters: ~
@@ -860,6 +853,18 @@ add_predicate({name}, {handler}, {force})
meanings
• {force} (boolean|nil)
+edit({lang}) *vim.treesitter.query.edit()*
+ Open a window for live editing of a treesitter query.
+
+ Can also be shown with `:EditQuery`. *:EditQuery*
+
+ Note that the editor opens a scratch buffer, and so queries aren't
+ persisted on disk.
+
+ Parameters: ~
+ • {lang} (string|nil) language to open the query editor for. If
+ omitted, inferred from the current buffer's filetype.
+
get({lang}, {query_name}) *vim.treesitter.query.get()*
Returns the runtime query {query_name} for {lang}.
@@ -922,7 +927,7 @@ omnifunc({findstart}, {base}) *vim.treesitter.query.omnifunc()*
Omnifunc for completing node names and predicates in treesitter queries.
Use via >lua
- vim.bo.omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
+ vim.bo.omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
<
parse({lang}, {query}) *vim.treesitter.query.parse()*
@@ -958,14 +963,13 @@ Query:iter_captures({node}, {source}, {start}, {stop})
The iterator returns three values: a numeric id identifying the capture,
the captured node, and metadata from any directives processing the match.
The following example shows how to get captures by name: >lua
-
- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
- local name = query.captures[id] -- name of the capture in the query
- -- typically useful info about the node:
- local type = node:type() -- type of the captured node
- local row1, col1, row2, col2 = node:range() -- range of the capture
- -- ... use the info here ...
- end
+ for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
+ local name = query.captures[id] -- name of the capture in the query
+ -- typically useful info about the node:
+ local type = node:type() -- type of the captured node
+ local row1, col1, row2, col2 = node:range() -- range of the capture
+ -- ... use the info here ...
+ end
<
Parameters: ~
@@ -976,8 +980,8 @@ Query:iter_captures({node}, {source}, {start}, {stop})
• {stop} (integer) Stopping line for the search (end-exclusive)
Return: ~
- (fun(): integer, TSNode, TSMetadata): capture id, capture node,
- metadata
+ (fun(end_line: integer|nil): integer, TSNode, TSMetadata): capture id,
+ capture node, metadata
*Query:iter_matches()*
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
@@ -988,18 +992,18 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
(1-based) index of the pattern in the query, a table mapping capture
indices to nodes, and metadata from any directives processing the match.
If the query has more than one pattern, the capture table might be sparse
- and e.g. `pairs()` method should be used over `ipairs` . Here is an example iterating over all captures in every match: >lua
-
- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
- for id, node in pairs(match) do
- local name = query.captures[id]
- -- `node` was captured by the `name` capture in the match
-
- local node_data = metadata[id] -- Node level metadata
-
- -- ... use the info here ...
- end
- end
+ and e.g. `pairs()` method should be used over `ipairs`. Here is an example
+ iterating over all captures in every match: >lua
+ for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
+ for id, node in pairs(match) do
+ local name = query.captures[id]
+ -- `node` was captured by the `name` capture in the match
+
+ local node_data = metadata[id] -- Node level metadata
+
+ -- ... use the info here ...
+ end
+ end
<
Parameters: ~
@@ -1039,11 +1043,8 @@ inject other languages, recursively. For example a Lua buffer containing
some Vimscript commands needs multiple parsers to fully understand its
contents.
-To create a LanguageTree (parser object) for a given buffer and language, use:
-
->lua
-
- local parser = vim.treesitter.get_parser(bufnr, lang)
+To create a LanguageTree (parser object) for a given buffer and language, use: >lua
+ local parser = vim.treesitter.get_parser(bufnr, lang)
<
@@ -1052,11 +1053,8 @@ Note: currently the parser is retained for the lifetime of a buffer but
this may change; a plugin should keep a reference to the parser object if
it wants incremental updates.
-Whenever you need to access the current syntax tree, parse the buffer:
-
->lua
-
- local tree = parser:parse({ start_row, end_row })
+Whenever you need to access the current syntax tree, parse the buffer: >lua
+ local tree = parser:parse({ start_row, end_row })
<
@@ -1094,15 +1092,6 @@ LanguageTree:destroy() *LanguageTree:destroy()*
Note: This DOES NOT remove this tree from a parent. Instead,
`remove_child` must be called on the parent to remove it.
- *LanguageTree:for_each_child()*
-LanguageTree:for_each_child({fn}, {include_self})
- Invokes the callback for each |LanguageTree| and its children recursively
-
- Parameters: ~
- • {fn} fun(tree: LanguageTree, lang: string)
- • {include_self} (boolean|nil) Whether to include the invoking tree in
- the results
-
LanguageTree:for_each_tree({fn}) *LanguageTree:for_each_tree()*
Invokes the callback for each |LanguageTree| recursively.
@@ -1112,10 +1101,12 @@ LanguageTree:for_each_tree({fn}) *LanguageTree:for_each_tree()*
• {fn} fun(tree: TSTree, ltree: LanguageTree)
LanguageTree:included_regions() *LanguageTree:included_regions()*
- Gets the set of included regions
+ Gets the set of included regions managed by this LanguageTree . This can be different from the regions set by injection query, because a
+ partial |LanguageTree:parse()| drops the regions outside the requested
+ range.
Return: ~
- Range6[][]
+ table<integer, Range6[]>
LanguageTree:invalidate({reload}) *LanguageTree:invalidate()*
Invalidates this parser and all its children
@@ -1124,10 +1115,12 @@ LanguageTree:invalidate({reload}) *LanguageTree:invalidate()*
• {reload} (boolean|nil)
LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()*
- Determines whether this tree is valid. If the tree is invalid, call `parse()` . This will return the updated tree.
+ Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest state of the
+ source. If invalid, user should call |LanguageTree:parse()|.
Parameters: ~
- • {exclude_children} (boolean|nil)
+ • {exclude_children} (boolean|nil) whether to ignore the validity of
+ children (default `false`)
Return: ~
(boolean)
@@ -1176,7 +1169,7 @@ LanguageTree:parse({range}) *LanguageTree:parse()*
injections).
Return: ~
- TSTree[]
+ table<integer, TSTree>
*LanguageTree:register_cbs()*
LanguageTree:register_cbs({cbs}, {recursive})
@@ -1218,7 +1211,12 @@ LanguageTree:tree_for_range({range}, {opts})
TSTree|nil
LanguageTree:trees() *LanguageTree:trees()*
- Returns all trees this language tree contains. Does not include child
- languages.
+ Returns all trees of the regions parsed by this parser. Does not include
+ child languages. The result is list-like if
+ • this LanguageTree is the root, in which case the result is empty or a singleton list; or
+ • the root LanguageTree is fully parsed.
+
+ Return: ~
+ table<integer, TSTree>
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/ftplugin/forth.vim b/runtime/ftplugin/forth.vim
index 5343784a21..d28c8484e1 100644
--- a/runtime/ftplugin/forth.vim
+++ b/runtime/ftplugin/forth.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Forth
" Maintainer: Johan Kotlinski <kotlinski@gmail.com>
-" Last Change: 2023 Aug 08
+" Last Change: 2023 Sep 15
" URL: https://github.com/jkotlinski/forth.vim
if exists("b:did_ftplugin")
@@ -58,6 +58,7 @@ if exists("loaded_matchit") && !exists("b:match_words")
let b:match_ignorecase = 1
let b:match_words = s:matchit_patterns[1:]->join(',')
let b:undo_ftplugin ..= "| unlet! b:match_ignorecase b:match_words"
+ unlet s:matchit_patterns
endif
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
@@ -68,4 +69,4 @@ endif
let &cpo = s:cpo_save
unlet s:cpo_save
-unlet s:define_patterns s:include_patterns s:matchit_patterns
+unlet s:define_patterns s:include_patterns
diff --git a/runtime/ftplugin/kotlin.vim b/runtime/ftplugin/kotlin.vim
new file mode 100644
index 0000000000..b21de603ea
--- /dev/null
+++ b/runtime/ftplugin/kotlin.vim
@@ -0,0 +1,33 @@
+" Vim filetype plugin file
+" Language: Kotlin
+" Maintainer: Alexander Udalov
+" URL: https://github.com/udalov/kotlin-vim
+" Last Change: 7 November 2021
+" 2023 Sep 17 by Vim Project (browsefilter)
+
+if exists('b:did_ftplugin') | finish | endif
+let b:did_ftplugin = 1
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
+setlocal commentstring=//\ %s
+
+setlocal formatoptions-=t formatoptions+=croqnl
+silent! setlocal formatoptions+=j
+
+setlocal includeexpr=substitute(v:fname,'\\.','/','g')
+setlocal suffixesadd=.kt
+
+let b:undo_ftplugin = "setlocal comments< commentstring< ".
+ \ "formatoptions< includeexpr< suffixesadd<"
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Kotlin Source Files (*.kt, *kts)\t*.kt;*.kts\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin .= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
diff --git a/runtime/indent/kotlin.vim b/runtime/indent/kotlin.vim
new file mode 100644
index 0000000000..590a5074d1
--- /dev/null
+++ b/runtime/indent/kotlin.vim
@@ -0,0 +1,60 @@
+" Vim indent file
+" Language: Kotlin
+" Maintainer: Alexander Udalov
+" URL: https://github.com/udalov/kotlin-vim
+" Last Change: 7 November 2021
+" 2023 Sep 17 by Vim Project (undo_indent)
+
+if exists('b:did_indent')
+ finish
+endif
+let b:did_indent = 1
+
+setlocal cinoptions& cinoptions+=j1,L0
+setlocal indentexpr=GetKotlinIndent()
+setlocal indentkeys=0},0),!^F,o,O,e,<CR>
+setlocal autoindent " TODO ?
+
+let b:undo_indent = "setlocal autoindent< cinoptions< indentexpr< indentkeys<"
+
+" TODO teach it to count bracket balance, etc.
+function! GetKotlinIndent()
+ if v:lnum == 0
+ return 0
+ endif
+
+ let prev_num = prevnonblank(v:lnum - 1)
+ let prev = getline(prev_num)
+ let prev_indent = indent(prev_num)
+ let cur = getline(v:lnum)
+
+ if cur =~ '^\s*\*'
+ return cindent(v:lnum)
+ endif
+
+ if prev =~ '^\s*\*/'
+ let st = prev
+ while st > 1
+ if getline(st) =~ '^\s*/\*'
+ break
+ endif
+ let st = st - 1
+ endwhile
+ return indent(st)
+ endif
+
+ let prev_open_paren = prev =~ '^.*(\s*$'
+ let cur_close_paren = cur =~ '^\s*).*$'
+ let prev_open_brace = prev =~ '^.*\({\|->\)\s*$'
+ let cur_close_brace = cur =~ '^\s*}.*$'
+
+ if prev_open_paren && !cur_close_paren || prev_open_brace && !cur_close_brace
+ return prev_indent + shiftwidth()
+ endif
+
+ if cur_close_paren && !prev_open_paren || cur_close_brace && !prev_open_brace
+ return prev_indent - shiftwidth()
+ endif
+
+ return prev_indent
+endfunction
diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua
index 16c834b371..5ed60ca8ab 100644
--- a/runtime/lua/vim/F.lua
+++ b/runtime/lua/vim/F.lua
@@ -5,13 +5,14 @@ local F = {}
--- If all arguments are nil, returns nil.
---
--- Examples:
---- <pre>
+---
+--- ```lua
--- local a = nil
--- local b = nil
--- local c = 42
--- local d = true
--- assert(vim.F.if_nil(a, b, c, d) == 42)
---- </pre>
+--- ```
---
---@param ... any
---@return any
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 64aeb64736..0215cae0cb 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -70,23 +70,24 @@ vim.log = {
--- Run a system command
---
--- Examples:
---- <pre>lua
---
---- local on_exit = function(obj)
---- print(obj.code)
---- print(obj.signal)
---- print(obj.stdout)
---- print(obj.stderr)
---- end
+--- ```lua
---
---- -- Run asynchronously
---- vim.system({'echo', 'hello'}, { text = true }, on_exit)
+--- local on_exit = function(obj)
+--- print(obj.code)
+--- print(obj.signal)
+--- print(obj.stdout)
+--- print(obj.stderr)
+--- end
---
---- -- Run synchronously
---- local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
---- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
+--- -- Run asynchronously
+--- vim.system({'echo', 'hello'}, { text = true }, on_exit)
---
---- </pre>
+--- -- Run synchronously
+--- local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
+--- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
+---
+--- ```
---
--- See |uv.spawn()| for more details.
---
@@ -200,7 +201,8 @@ do
--- (such as the |TUI|) pastes text into the editor.
---
--- Example: To remove ANSI color codes when pasting:
- --- <pre>lua
+ ---
+ --- ```lua
--- vim.paste = (function(overridden)
--- return function(lines, phase)
--- for i,line in ipairs(lines) do
@@ -210,7 +212,7 @@ do
--- overridden(lines, phase)
--- end
--- end)(vim.paste)
- --- </pre>
+ --- ```
---
---@see |paste|
---@alias paste_phase -1 | 1 | 2 | 3
@@ -361,32 +363,33 @@ local VIM_CMD_ARG_MAX = 20
--- command.
---
--- Example:
---- <pre>lua
---- vim.cmd('echo 42')
---- vim.cmd([[
---- augroup My_group
---- autocmd!
---- autocmd FileType c setlocal cindent
---- augroup END
---- ]])
---
---- -- Ex command :echo "foo"
---- -- Note string literals need to be double quoted.
---- vim.cmd('echo "foo"')
---- vim.cmd { cmd = 'echo', args = { '"foo"' } }
---- vim.cmd.echo({ args = { '"foo"' } })
---- vim.cmd.echo('"foo"')
+--- ```lua
+--- vim.cmd('echo 42')
+--- vim.cmd([[
+--- augroup My_group
+--- autocmd!
+--- autocmd FileType c setlocal cindent
+--- augroup END
+--- ]])
+---
+--- -- Ex command :echo "foo"
+--- -- Note string literals need to be double quoted.
+--- vim.cmd('echo "foo"')
+--- vim.cmd { cmd = 'echo', args = { '"foo"' } }
+--- vim.cmd.echo({ args = { '"foo"' } })
+--- vim.cmd.echo('"foo"')
---
---- -- Ex command :write! myfile.txt
---- vim.cmd('write! myfile.txt')
---- vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true }
---- vim.cmd.write { args = { "myfile.txt" }, bang = true }
---- vim.cmd.write { "myfile.txt", bang = true }
+--- -- Ex command :write! myfile.txt
+--- vim.cmd('write! myfile.txt')
+--- vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true }
+--- vim.cmd.write { args = { "myfile.txt" }, bang = true }
+--- vim.cmd.write { "myfile.txt", bang = true }
---
---- -- Ex command :colorscheme blue
---- vim.cmd('colorscheme blue')
---- vim.cmd.colorscheme('blue')
---- </pre>
+--- -- Ex command :colorscheme blue
+--- vim.cmd('colorscheme blue')
+--- vim.cmd.colorscheme('blue')
+--- ```
---
---@param command string|table Command(s) to execute.
--- If a string, executes multiple lines of Vim script at once. In this
@@ -871,9 +874,10 @@ end
--- "Pretty prints" the given arguments and returns them unmodified.
---
--- Example:
---- <pre>lua
---- local hl_normal = vim.print(vim.api.nvim_get_hl_by_name('Normal', true))
---- </pre>
+---
+--- ```lua
+--- local hl_normal = vim.print(vim.api.nvim_get_hl_by_name('Normal', true))
+--- ```
---
--- @see |vim.inspect()|
--- @see |:=|
@@ -900,10 +904,12 @@ end
--- Translate keycodes.
---
--- Example:
---- <pre>lua
---- local k = vim.keycode
---- vim.g.mapleader = k'<bs>'
---- </pre>
+---
+--- ```lua
+--- local k = vim.keycode
+--- vim.g.mapleader = k'<bs>'
+--- ```
+---
--- @param str string String to be converted.
--- @return string
--- @see |nvim_replace_termcodes()|
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 392f733112..e74a33d921 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -127,11 +127,16 @@ function vim.api.nvim__unpack(str) end
function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end
--- Activates buffer-update events on a channel, or as Lua callbacks.
---- Example (Lua): capture buffer updates in a global `events` variable (use "vim.print(events)" to see its contents):
+--- Example (Lua): capture buffer updates in a global `events` variable (use
+--- "vim.print(events)" to see its contents):
+---
--- ```lua
---- events = {}
---- vim.api.nvim_buf_attach(0, false, {
---- on_lines=function(...) table.insert(events, {...}) end})
+--- events = {}
+--- vim.api.nvim_buf_attach(0, false, {
+--- on_lines = function(...)
+--- table.insert(events, {...})
+--- end,
+--- })
--- ```
---
--- @param buffer integer Buffer handle, or 0 for current buffer
@@ -314,29 +319,32 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- Region can be given as (row,col) tuples, or valid extmark ids (whose
--- positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
--- respectively, thus the following are equivalent:
+---
--- ```lua
---- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
---- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+--- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+--- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
--- ```
+---
--- If `end` is less than `start`, traversal works backwards. (Useful with
--- `limit`, to get the first marks prior to a given position.)
--- Note: when using extmark ranges (marks with a end_row/end_col position)
--- the `overlap` option might be useful. Otherwise only the start position of
--- an extmark will be considered.
--- Example:
+---
--- ```lua
---- local api = vim.api
---- local pos = api.nvim_win_get_cursor(0)
---- local ns = api.nvim_create_namespace('my-plugin')
---- -- Create new extmark at line 1, column 1.
---- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
---- -- Create new extmark at line 3, column 1.
---- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
---- -- Get extmarks only from line 3.
---- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
---- -- Get all marks in this buffer + namespace.
---- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
---- vim.print(ms)
+--- local api = vim.api
+--- local pos = api.nvim_win_get_cursor(0)
+--- local ns = api.nvim_create_namespace('my-plugin')
+--- -- Create new extmark at line 1, column 1.
+--- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+--- -- Create new extmark at line 3, column 1.
+--- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+--- -- Get extmarks only from line 3.
+--- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+--- -- Get all marks in this buffer + namespace.
+--- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+--- vim.print(ms)
--- ```
---
--- @param buffer integer Buffer handle, or 0 for current buffer
@@ -775,6 +783,7 @@ function vim.api.nvim_command_output(command) end
--- Create or get an autocommand group `autocmd-groups`.
--- To get an existing group id, do:
+---
--- ```lua
--- local id = vim.api.nvim_create_augroup("MyGroup", {
--- clear = false
@@ -790,6 +799,7 @@ function vim.api.nvim_create_augroup(name, opts) end
--- Creates an `autocommand` event handler, defined by `callback` (Lua function or Vimscript function name string) or `command` (Ex command string).
--- Example using Lua callback:
+---
--- ```lua
--- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
--- pattern = {"*.c", "*.h"},
@@ -798,17 +808,21 @@ function vim.api.nvim_create_augroup(name, opts) end
--- end
--- })
--- ```
+---
--- Example using an Ex command as the handler:
+---
--- ```lua
--- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
--- pattern = {"*.c", "*.h"},
--- command = "echo 'Entering a C or C++ file'",
--- })
--- ```
---- Note: `pattern` is NOT automatically expanded (unlike with `:autocmd`), thus names like
---- "$HOME" and "~" must be expanded explicitly:
+---
+--- Note: `pattern` is NOT automatically expanded (unlike with `:autocmd`),
+--- thus names like "$HOME" and "~" must be expanded explicitly:
+---
--- ```lua
---- pattern = vim.fn.expand("~") .. "/some/path/*.py"
+--- pattern = vim.fn.expand("~") .. "/some/path/*.py"
--- ```
---
--- @param event any (string|array) Event(s) that will trigger the handler
@@ -870,10 +884,11 @@ function vim.api.nvim_create_namespace(name) end
--- Creates a global `user-commands` command.
--- For Lua usage see `lua-guide-commands-create`.
--- Example:
+---
--- ```vim
---- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
---- :SayHello
---- Hello world!
+--- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
+--- :SayHello
+--- Hello world!
--- ```
---
--- @param name string Name of the new user command. Must begin with an uppercase
@@ -1084,6 +1099,7 @@ function vim.api.nvim_execute_lua(code, args) end
--- with escape_ks=false) to replace `keycodes`, then pass the result to
--- nvim_feedkeys().
--- Example:
+---
--- ```vim
--- :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
--- :call nvim_feedkeys(key, 'n', v:false)
@@ -1111,19 +1127,21 @@ function vim.api.nvim_get_api_info() end
--- Get all autocommands that match the corresponding {opts}.
--- These examples will get autocommands matching ALL the given criteria:
+---
--- ```lua
---- -- Matches all criteria
---- autocommands = vim.api.nvim_get_autocmds({
---- group = "MyGroup",
---- event = {"BufEnter", "BufWinEnter"},
---- pattern = {"*.c", "*.h"}
---- })
----
---- -- All commands from one group
---- autocommands = vim.api.nvim_get_autocmds({
---- group = "MyGroup",
---- })
+--- -- Matches all criteria
+--- autocommands = vim.api.nvim_get_autocmds({
+--- group = "MyGroup",
+--- event = {"BufEnter", "BufWinEnter"},
+--- pattern = {"*.c", "*.h"}
+--- })
+---
+--- -- All commands from one group
+--- autocommands = vim.api.nvim_get_autocmds({
+--- group = "MyGroup",
+--- })
--- ```
+---
--- NOTE: When multiple patterns or events are provided, it will find all the
--- autocommands that match any combination of them.
---
@@ -1149,6 +1167,7 @@ function vim.api.nvim_get_chan_info(chan) end
--- Returns the 24-bit RGB value of a `nvim_get_color_map()` color name or
--- "#rrggbb" hexadecimal string.
--- Example:
+---
--- ```vim
--- :echo nvim_get_color_by_name("Pink")
--- :echo nvim_get_color_by_name("#cbcbcb")
@@ -1471,14 +1490,18 @@ function vim.api.nvim_open_term(buffer, opts) end
--- could let floats hover outside of the main window like a tooltip, but this
--- should not be used to specify arbitrary WM screen positions.
--- Example (Lua): window-relative float
+---
--- ```lua
--- vim.api.nvim_open_win(0, false,
--- {relative='win', row=3, col=3, width=12, height=3})
--- ```
+---
--- Example (Lua): buffer-relative float (travels as buffer is scrolled)
+---
--- ```lua
--- vim.api.nvim_open_win(0, false,
--- {relative='win', width=12, height=3, bufpos={100,10}})
+--- })
--- ```
---
--- @param buffer integer Buffer to display, or 0 for current buffer
@@ -1858,10 +1881,13 @@ function vim.api.nvim_set_hl_ns_fast(ns_id) end
--- Unlike `:map`, leading/trailing whitespace is accepted as part of the
--- {lhs} or {rhs}. Empty {rhs} is `<Nop>`. `keycodes` are replaced as usual.
--- Example:
+---
--- ```vim
--- call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
--- ```
+---
--- is equivalent to:
+---
--- ```vim
--- nmap <nowait> <Space><NL> <Nop>
--- ```
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index d7b76a803c..0a6dd3e151 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -131,7 +131,8 @@ function vim.str_utf_pos(str) end
--- The result can be added to {index} to get the starting byte of a character.
---
--- Examples:
---- <pre>lua
+---
+--- ```lua
--- -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
---
--- -- Returns 0 because the index is pointing at the first byte of a character
@@ -139,7 +140,7 @@ function vim.str_utf_pos(str) end
---
--- -- Returns -1 because the index is pointing at the second byte of a character
--- vim.str_utf_start('æ', 2)
---- </pre>
+--- ```
---
--- @param str string
--- @param index number
@@ -150,7 +151,8 @@ function vim.str_utf_start(str, index) end
--- to.
---
--- Examples:
---- <pre>lua
+---
+--- ```lua
--- -- The character 'æ' is stored as the bytes '\xc3\xa6' (using UTF-8)
---
--- -- Returns 0 because the index is pointing at the last byte of a character
@@ -158,7 +160,7 @@ function vim.str_utf_start(str, index) end
---
--- -- Returns 1 because the index is pointing at the penultimate byte of a character
--- vim.str_utf_end('æ', 1)
---- </pre>
+--- ```
---
--- @param str string
--- @param index number
@@ -204,7 +206,8 @@ function vim.schedule(callback) end
--- this time.
---
--- Examples:
---- <pre>lua
+---
+--- ```lua
---
--- ---
--- -- Wait for 100 ms, allowing other events to process
@@ -226,7 +229,7 @@ function vim.schedule(callback) end
--- if vim.wait(10000, function() return vim.g.timer_result end) then
--- print('Only waiting a little bit of time!')
--- end
---- </pre>
+--- ```
---
--- @param time integer Number of milliseconds to wait
--- @param callback? fun(): boolean Optional callback. Waits until {callback} returns true
@@ -258,22 +261,23 @@ function vim.wait(time, callback, interval, fast_only) end
--- likewise experimental).
---
--- Example (stub for a |ui-popupmenu| implementation):
---- <pre>lua
----
---- ns = vim.api.nvim_create_namespace('my_fancy_pum')
----
---- vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
---- if event == "popupmenu_show" then
---- local items, selected, row, col, grid = ...
---- print("display pum ", #items)
---- elseif event == "popupmenu_select" then
---- local selected = ...
---- print("selected", selected)
---- elseif event == "popupmenu_hide" then
---- print("FIN")
---- end
---- end)
---- </pre>
+---
+--- ```lua
+--- ns = vim.api.nvim_create_namespace('my_fancy_pum')
+---
+--- vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...)
+--- if event == "popupmenu_show" then
+--- local items, selected, row, col, grid = ...
+--- print("display pum ", #items)
+--- elseif event == "popupmenu_select" then
+--- local selected = ...
+--- print("selected", selected)
+--- elseif event == "popupmenu_hide" then
+--- print("FIN")
+--- end
+--- end)
+--- ```
+---
--- @param ns integer
--- @param options table<string, any>
--- @param callback fun()
diff --git a/runtime/lua/vim/_meta/diff.lua b/runtime/lua/vim/_meta/diff.lua
index 246ac0c75a..f265139448 100644
--- a/runtime/lua/vim/_meta/diff.lua
+++ b/runtime/lua/vim/_meta/diff.lua
@@ -6,24 +6,25 @@
--- either directly or via callback arguments, are 1-based.
---
--- Examples:
---- <pre>lua
---- vim.diff('a\\n', 'b\\nc\\n')
---- -- =>
---- -- @@ -1 +1,2 @@
---- -- -a
---- -- +b
---- -- +c
---
---- vim.diff('a\\n', 'b\\nc\\n', {result_type = 'indices'})
---- -- =>
---- -- {
---- -- {1, 1, 1, 2}
---- -- }
---- </pre>
+--- ```lua
+--- vim.diff('a\n', 'b\nc\n')
+--- -- =>
+--- -- @@ -1 +1,2 @@
+--- -- -a
+--- -- +b
+--- -- +c
---
---- @param a string First string to compare
---- @param b string Second string to compare
---- @param opts table<string,any> Optional parameters:
+--- vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
+--- -- =>
+--- -- {
+--- -- {1, 1, 1, 2}
+--- -- }
+--- ```
+---
+---@param a string First string to compare
+---@param b string Second string to compare
+---@param opts table<string,any> Optional parameters:
--- - `on_hunk` (callback):
--- Invoked for each hunk in the diff. Return a negative number
--- to cancel the callback for any remaining hunks.
@@ -64,6 +65,6 @@
--- Use the indent heuristic for the internal
--- diff library.
---
---- @return string|table|nil
+---@return string|table|nil
--- See {opts.result_type}. `nil` if {opts.on_hunk} is given.
function vim.diff(a, b, opts) end
diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index 76a6c7b733..edf905c7c6 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -1,8 +1,8 @@
---- @meta
+---@meta
-- luacheck: no unused args
---- @defgroup vim.json
+---@defgroup vim.json
---
--- This module provides encoding and decoding of Lua objects to and
--- from JSON-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
@@ -14,24 +14,23 @@
--- - Decodes empty array as `{}` (empty Lua table).
---
--- Example:
---- <pre>lua
---- :lua vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}'))
---- --> { bar = {}, foo = vim.empty_dict(), zub = vim.NIL }
---- </pre>
---- Parameters: ~
---- • {str} Stringified JSON data.
---- • {opts} Options map keys:
---- • luanil: { object: bool, array: bool }
---- • `luanil.object=true` converts `null` in JSON objects to
---- Lua `nil` instead of `vim.NIL`.
---- • `luanil.array=true` converts `null` in JSON arrays to Lua
---- `nil` instead of `vim.NIL`.
---- @param str string
---- @param opts? table<string, any>
---- @return any
+---
+--- ```lua
+--- vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}'))
+--- -- { bar = {}, foo = vim.empty_dict(), zub = vim.NIL }
+--- ```
+---
+---@param str string Stringified JSON data.
+---@param opts? table<string,any> Options table with keys:
+--- - luanil: (table) Table with keys:
+--- * object: (boolean) When true, converts `null` in JSON objects
+--- to Lua `nil` instead of |vim.NIL|.
+--- * array: (boolean) When true, converts `null` in JSON arrays
+--- to Lua `nil` instead of |vim.NIL|.
+---@return any
function vim.json.decode(str, opts) end
--- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
---- @param obj any
---- @return string
+---@param obj any
+---@return string
function vim.json.encode(obj) end
diff --git a/runtime/lua/vim/_meta/misc.lua b/runtime/lua/vim/_meta/misc.lua
index 8a76755962..0d70e16314 100644
--- a/runtime/lua/vim/_meta/misc.lua
+++ b/runtime/lua/vim/_meta/misc.lua
@@ -5,9 +5,11 @@
--- Invokes |vim-function| or |user-function| {func} with arguments {...}.
--- See also |vim.fn|.
--- Equivalent to:
---- <pre>lua
---- vim.fn[func]({...})
---- </pre>
+---
+--- ```lua
+--- vim.fn[func]({...})
+--- ```
+---
--- @param func fun()
--- @param ... any
function vim.call(func, ...) end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 92dc4b2c7f..af676fa961 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -141,6 +141,7 @@ vim.bo.ai = vim.bo.autoindent
--- :set autoread<
--- ```
---
+---
--- @type boolean
vim.o.autoread = true
vim.o.ar = vim.o.autoread
@@ -354,6 +355,7 @@ vim.go.bkc = vim.go.backupcopy
--- ```
--- :set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces
--- ```
+---
--- See also 'backup' and 'writebackup' options.
--- If you want to hide your backup files on Unix, consider this value:
--- ```
@@ -409,9 +411,9 @@ vim.go.bex = vim.go.backupext
---
--- Note that environment variables are not expanded. If you want to use
--- $HOME you must expand it explicitly, e.g.:
---- ```
---- :let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
---
+--- ```vim
+--- :let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
--- ```
--- Note that the default also makes sure that "crontab -e" works (when a
--- backup would be made by renaming the original file crontab won't see
@@ -833,6 +835,7 @@ vim.bo.cino = vim.bo.cinoptions
--- set cinscopedecls+=signals,public\ slots,private\ slots
--- ```
---
+---
--- @type string
vim.o.cinscopedecls = "public,protected,private"
vim.o.cinsd = vim.o.cinscopedecls
@@ -916,11 +919,11 @@ vim.go.cwh = vim.go.cmdwinheight
--- The screen column can be an absolute number, or a number preceded with
--- '+' or '-', which is added to or subtracted from 'textwidth'.
--- ```
----
--- :set cc=+1 " highlight column after 'textwidth'
--- :set cc=+1,+2,+3 " highlight three columns after 'textwidth'
--- :hi ColorColumn ctermbg=lightgrey guibg=lightgrey
--- ```
+---
--- When 'textwidth' is zero then the items with '-' and '+' are not used.
--- A maximum of 256 columns are highlighted.
---
@@ -1418,6 +1421,7 @@ vim.wo.crb = vim.wo.cursorbind
--- au WinEnter * set cursorline cursorcolumn
--- ```
---
+---
--- @type boolean
vim.o.cursorcolumn = false
vim.o.cuc = vim.o.cursorcolumn
@@ -1499,6 +1503,7 @@ vim.go.debug = vim.o.debug
--- let &l:define = '^\s*\ze\k\+\s*=\s*function('
--- ```
---
+---
--- @type string
vim.o.define = ""
vim.o.def = vim.o.define
@@ -1679,6 +1684,7 @@ vim.go.dex = vim.go.diffexpr
--- :set diffopt-=internal " do NOT use the internal diff parser
--- ```
---
+---
--- @type string
vim.o.diffopt = "internal,filler,closeoff"
vim.o.dip = vim.o.diffopt
@@ -1729,6 +1735,7 @@ vim.go.dg = vim.go.digraph
--- ```
--- :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces
--- ```
+---
--- Editing the same file twice will result in a warning. Using "/tmp" on
--- is discouraged: if the system crashes you lose the swap file. And
--- others on the computer may be able to see the files.
@@ -1917,6 +1924,7 @@ vim.go.efm = vim.go.errorformat
--- :set ei=WinEnter,WinLeave
--- ```
---
+---
--- @type string
vim.o.eventignore = ""
vim.o.ei = vim.o.eventignore
@@ -2634,14 +2642,12 @@ vim.go.gp = vim.go.grepprg
--- To disable cursor-styling, reset the option:
--- ```
--- :set guicursor=
----
--- ```
--- To enable mode shapes, "Cursor" highlight, and blinking:
--- ```
--- :set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50
--- \,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor
--- \,sm:block-blinkwait175-blinkoff150-blinkon175
----
--- ```
--- The option is a comma-separated list of parts. Each part consists of a
--- mode-list and an argument-list:
@@ -2719,6 +2725,7 @@ vim.go.gp = vim.go.grepprg
--- :highlight Cursor gui=NONE guifg=bg guibg=fg
--- ```
---
+---
--- @type string
vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"
vim.o.gcr = vim.o.guicursor
@@ -2790,6 +2797,7 @@ vim.go.gcr = vim.go.guicursor
--- :set guifont=Andale_Mono:h7.5:w4.5
--- ```
---
+---
--- @type string
vim.o.guifont = ""
vim.o.gfn = vim.o.guifont
@@ -2944,6 +2952,7 @@ vim.go.gtl = vim.go.guitablabel
--- :let &guitabtooltip = "line one\nline two"
--- ```
---
+---
--- @type string
vim.o.guitabtooltip = ""
vim.o.gtt = vim.o.guitabtooltip
@@ -3206,6 +3215,7 @@ vim.go.inc = vim.go.include
--- ```
--- :setlocal includeexpr=tr(v:fname,'.','/')
--- ```
+---
--- Also used for the `gf` command if an unmodified file name can't be
--- found. Allows doing "gf" on the name after an 'include' statement.
--- Also used for `<cfile>`.
@@ -3258,6 +3268,7 @@ vim.bo.inex = vim.bo.includeexpr
--- autocmd CmdlineLeave /,\? :set nohlsearch
--- augroup END
--- ```
+---
--- CTRL-L can be used to add one character from after the current match
--- to the command line. If 'ignorecase' and 'smartcase' are set and the
--- command line has no uppercase characters, the added character is
@@ -3565,6 +3576,7 @@ vim.go.kp = vim.go.keywordprg
--- ```
--- :set langmap=zy,yz,ZY,YZ
--- ```
+---
--- The 'langmap' option is a list of parts, separated with commas. Each
--- part can be in one of two forms:
--- 1. A list of pairs. Each pair is a "from" character immediately
@@ -3762,6 +3774,7 @@ vim.go.lw = vim.go.lispwords
--- ```
--- :set list lcs=tab:\ \
--- ```
+---
--- Note that list mode will also affect formatting (set with 'textwidth'
--- or 'wrapmargin') when 'cpoptions' includes 'L'. See 'listchars' for
--- changing the way tabs are displayed.
@@ -3790,6 +3803,7 @@ vim.wo.list = vim.o.list
--- >--
--- etc.
--- ```
+---
--- tab:xyz The 'z' is always used, then 'x' is prepended, and
--- then 'y' is used as many times as will fit. Thus
--- "tab:<->" displays:
@@ -3801,6 +3815,7 @@ vim.wo.list = vim.o.list
--- <-->
--- etc.
--- ```
+---
--- When "tab:" is omitted, a tab is shown as ^I.
--- *lcs-space*
--- space:c Character to show for a space. When omitted, spaces
@@ -3816,6 +3831,7 @@ vim.wo.list = vim.o.list
--- ```
--- ---+---+--
--- ```
+---
--- *lcs-lead*
--- lead:c Character to show for leading spaces. When omitted,
--- leading spaces are blank. Overrides the "space" and
@@ -3824,6 +3840,7 @@ vim.wo.list = vim.o.list
--- ```
--- :set listchars+=tab:>-,lead:.
--- ```
+---
--- *lcs-leadmultispace*
--- leadmultispace:c...
--- Like the `lcs-multispace` value, but for leading
@@ -3834,6 +3851,7 @@ vim.wo.list = vim.o.list
--- ```
--- ---+---+--XXX
--- ```
+---
--- Where "XXX" denotes the first non-blank characters in
--- the line.
--- *lcs-trail*
@@ -3941,6 +3959,7 @@ vim.go.mef = vim.go.makeef
--- :set makeencoding=char " system locale is used
--- ```
---
+---
--- @type string
vim.o.makeencoding = ""
vim.o.menc = vim.o.makeencoding
@@ -3986,13 +4005,11 @@ vim.go.mp = vim.go.makeprg
--- '>' (for HTML):
--- ```
--- :set mps+=<:>
----
--- ```
--- A more exotic example, to jump between the '=' and ';' in an
--- assignment, useful for languages like C and Java:
--- ```
--- :au FileType c,cpp,java set mps+==:;
----
--- ```
--- For a more advanced way of using "%", see the matchit.vim plugin in
--- the $VIMRUNTIME/plugin directory. `add-local-help`
@@ -4078,6 +4095,7 @@ vim.go.mis = vim.go.menuitems
--- ```
--- {start},{inc},{added}
--- ```
+---
--- For most languages the uncompressed word tree fits in memory. {start}
--- gives the amount of memory in Kbyte that can be used before any
--- compression is done. It should be a bit smaller than the amount of
@@ -4196,6 +4214,7 @@ vim.go.more = vim.o.more
--- ```
--- :set mouse=nv
--- ```
+---
--- To temporarily disable mouse support, hold the shift key while using
--- the mouse.
---
@@ -4299,6 +4318,7 @@ vim.go.mh = vim.go.mousehide
--- :map <4-S-LeftDrag> <4-RightDrag>
--- :map <4-S-LeftRelease> <4-RightRelease>
--- ```
+---
--- Mouse commands requiring the CTRL modifier can be simulated by typing
--- the "g" key before using the mouse:
--- "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
@@ -4477,6 +4497,7 @@ vim.bo.nf = vim.bo.nrformats
--- |there | 4 there | 1 there | 1 there
--- ```
---
+---
--- @type boolean
vim.o.number = false
vim.o.nu = vim.o.number
@@ -4710,10 +4731,10 @@ vim.wo.pvw = vim.wo.previewwindow
--- the popupmenu using `highlight-blend`. For instance, to enable
--- transparency but force the current selected element to be fully opaque:
--- ```
----
--- :set pumblend=15
--- :hi PmenuSel blend=0
--- ```
+---
--- UI-dependent. Works best with RGB colors. 'termguicolors'
---
--- @type integer
@@ -4986,6 +5007,7 @@ vim.go.ru = vim.go.ruler
--- :set rulerformat=%15(%c%V\ %p%%%)
--- ```
---
+---
--- @type string
vim.o.rulerformat = ""
vim.o.ruf = vim.o.rulerformat
@@ -5363,6 +5385,7 @@ vim.go.ssop = vim.go.sessionoptions
--- ```
--- :set shada='50,<1000,s100,:0,n~/nvim/shada
--- ```
+---
--- '50 Marks will be remembered for the last 50 files you
--- edited.
--- <1000 Contents of registers (up to 1000 lines each) will be
@@ -5449,7 +5472,6 @@ vim.go.sdf = vim.go.shadafile
--- let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode'
--- let &shellpipe = '2>&1 | %%{ "$_" } | tee %s; exit $LastExitCode'
--- set shellquote= shellxquote=
----
--- ```
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
@@ -5726,6 +5748,7 @@ vim.go.shm = vim.go.shortmess
--- :setlocal showbreak=NONE
--- ```
---
+---
--- @type string
vim.o.showbreak = ""
vim.o.sbr = vim.o.showbreak
@@ -5858,15 +5881,16 @@ vim.go.ss = vim.go.sidescroll
--- setlocal sidescrolloff<
--- setlocal sidescrolloff=-1
--- ```
+---
--- Example: Try this together with 'sidescroll' and 'listchars' as
--- in the following example to never allow the cursor to move
--- onto the "extends" character:
--- ```
----
--- :set nowrap sidescroll=1 listchars=extends:>,precedes:<
--- :set sidescrolloff=1
--- ```
---
+---
--- @type integer
vim.o.sidescrolloff = 0
vim.o.siso = vim.o.sidescrolloff
@@ -6171,6 +6195,7 @@ vim.bo.spo = vim.bo.spelloptions
--- ```
--- :set sps=file:~/.config/nvim/sugg,best,expr:MySuggest()
--- ```
+---
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
---
@@ -6263,6 +6288,7 @@ vim.go.sol = vim.go.startofline
--- handler should be written with this in mind.
---
--- Examples:
+---
--- ```vim
--- " Relative number with bar separator and click handlers:
--- :set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T
@@ -6282,7 +6308,6 @@ vim.go.sol = vim.go.startofline
--- :let &stc='%#NonText#%{&nu?v:lnum:""}' .
--- '%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' .
--- '%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}'
----
--- ```
--- WARNING: this expression is evaluated for each screen line so defining
--- an expensive expression can negatively affect render performance.
@@ -6522,6 +6547,7 @@ vim.wo.stc = vim.wo.statuscolumn
--- :endfunction
--- ```
---
+---
--- @type string
vim.o.statusline = ""
vim.o.stl = vim.o.statusline
@@ -6553,6 +6579,7 @@ vim.go.su = vim.go.suffixes
--- :set suffixesadd=.java
--- ```
---
+---
--- @type string
vim.o.suffixesadd = ""
vim.o.sua = vim.o.suffixesadd
@@ -7445,6 +7472,7 @@ vim.go.ww = vim.go.whichwrap
--- :set wc=<Tab>
--- ```
---
+---
--- @type integer
vim.o.wildchar = 9
vim.o.wc = vim.o.wildchar
@@ -7533,6 +7561,7 @@ vim.go.wic = vim.go.wildignorecase
--- :cnoremap <Left> <Space><BS><Left>
--- :cnoremap <Right> <Space><BS><Right>
--- ```
+---
--- `hl-WildMenu` highlights the current match.
---
--- @type boolean
@@ -7762,6 +7791,7 @@ vim.go.wh = vim.go.winheight
--- set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC
--- ```
---
+---
--- @type string
vim.o.winhighlight = ""
vim.o.winhl = vim.o.winhighlight
diff --git a/runtime/lua/vim/_meta/spell.lua b/runtime/lua/vim/_meta/spell.lua
index d55867f769..57f2180895 100644
--- a/runtime/lua/vim/_meta/spell.lua
+++ b/runtime/lua/vim/_meta/spell.lua
@@ -10,13 +10,14 @@
--- the buffer. Consider calling this with |nvim_buf_call()|.
---
--- Example:
---- <pre>lua
---- vim.spell.check("the quik brown fox")
---- -- =>
---- -- {
---- -- {'quik', 'bad', 5}
---- -- }
---- </pre>
+---
+--- ```lua
+--- vim.spell.check("the quik brown fox")
+--- -- =>
+--- -- {
+--- -- {'quik', 'bad', 5}
+--- -- }
+--- ```
---
--- @param str string
--- @return {[1]: string, [2]: string, [3]: string}[]
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index efb779ad41..2062e96b21 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1630,9 +1630,9 @@ function vim.fn.executable(expr) end
--- To execute a command in another window than the current one
--- use `win_execute()`.
---
---- @param command any
---- @param silent? boolean
---- @return any
+--- @param command string|string[]
+--- @param silent? ''|'silent'|'silent!'
+--- @return string
function vim.fn.execute(command, silent) end
--- Returns the full path of {expr} if it is an executable and
diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
index e1c125baf2..6a3413b597 100644
--- a/runtime/lua/vim/_options.lua
+++ b/runtime/lua/vim/_options.lua
@@ -7,11 +7,12 @@
---"references". |lua-guide-variables| For example, using \`vim.fn.remove()\` on
---a Lua list copies the list object to Vimscript and does NOT modify the Lua
---list:
----<pre>lua
---- local list = { 1, 2, 3 }
---- vim.fn.remove(list, 0)
---- vim.print(list) --> "{ 1, 2, 3 }"
----</pre>
+---
+--- ```lua
+--- local list = { 1, 2, 3 }
+--- vim.fn.remove(list, 0)
+--- vim.print(list) --> "{ 1, 2, 3 }"
+--- ```
---@addtogroup lua-vimscript
---@brief <pre>help
@@ -131,13 +132,17 @@ local function get_options_info(name)
return info
end
----Environment variables defined in the editor session.
----See |expand-env| and |:let-environment| for the Vimscript behavior.
----Invalid or unset key returns `nil`.
----Example: <pre>lua
---- vim.env.FOO = 'bar'
---- print(vim.env.TERM)
----</pre>
+--- Environment variables defined in the editor session.
+--- See |expand-env| and |:let-environment| for the Vimscript behavior.
+--- Invalid or unset key returns `nil`.
+---
+--- Example:
+---
+--- ```lua
+--- vim.env.FOO = 'bar'
+--- print(vim.env.TERM)
+--- ```
+---
---@param var string
vim.env = setmetatable({}, {
__index = function(_, k)
@@ -226,16 +231,18 @@ end
---global value of a |global-local| option, see |:setglobal|.
---</pre>
----Get or set |options|. Like `:set`. Invalid key is an error.
+--- Get or set |options|. Like `:set`. Invalid key is an error.
---
----Note: this works on both buffer-scoped and window-scoped options using the
----current buffer and window.
+--- Note: this works on both buffer-scoped and window-scoped options using the
+--- current buffer and window.
---
----Example: <pre>lua
---- vim.o.cmdheight = 4
---- print(vim.o.columns)
---- print(vim.o.foo) -- error: invalid key
----</pre>
+--- Example:
+---
+--- ```lua
+--- vim.o.cmdheight = 4
+--- print(vim.o.columns)
+--- print(vim.o.foo) -- error: invalid key
+--- ```
vim.o = setmetatable({}, {
__index = function(_, k)
return api.nvim_get_option_value(k, {})
@@ -245,18 +252,20 @@ vim.o = setmetatable({}, {
end,
})
----Get or set global |options|. Like `:setglobal`. Invalid key is
----an error.
+--- Get or set global |options|. Like `:setglobal`. Invalid key is
+--- an error.
---
----Note: this is different from |vim.o| because this accesses the global
----option value and thus is mostly useful for use with |global-local|
----options.
+--- Note: this is different from |vim.o| because this accesses the global
+--- option value and thus is mostly useful for use with |global-local|
+--- options.
---
----Example: <pre>lua
---- vim.go.cmdheight = 4
---- print(vim.go.columns)
---- print(vim.go.bar) -- error: invalid key
----</pre>
+--- Example:
+---
+--- ```lua
+--- vim.go.cmdheight = 4
+--- print(vim.go.columns)
+--- print(vim.go.bar) -- error: invalid key
+--- ```
vim.go = setmetatable({}, {
__index = function(_, k)
return api.nvim_get_option_value(k, { scope = 'global' })
@@ -266,37 +275,39 @@ vim.go = setmetatable({}, {
end,
})
----Get or set buffer-scoped |options| for the buffer with number {bufnr}.
----Like `:set` and `:setlocal`. If [{bufnr}] is omitted then the current
----buffer is used. Invalid {bufnr} or key is an error.
+--- Get or set buffer-scoped |options| for the buffer with number {bufnr}.
+--- Like `:set` and `:setlocal`. If [{bufnr}] is omitted then the current
+--- buffer is used. Invalid {bufnr} or key is an error.
---
----Note: this is equivalent to both `:set` and `:setlocal`.
+--- Note: this is equivalent to both `:set` and `:setlocal`.
---
----Example: <pre>lua
---- local bufnr = vim.api.nvim_get_current_buf()
---- vim.bo[bufnr].buflisted = true -- same as vim.bo.buflisted = true
---- print(vim.bo.comments)
---- print(vim.bo.baz) -- error: invalid key
----</pre>
----@param bufnr integer|nil
+--- Example:
+---
+--- ```lua
+--- local bufnr = vim.api.nvim_get_current_buf()
+--- vim.bo[bufnr].buflisted = true -- same as vim.bo.buflisted = true
+--- print(vim.bo.comments)
+--- print(vim.bo.baz) -- error: invalid key
+--- ```
vim.bo = new_buf_opt_accessor()
----Get or set window-scoped |options| for the window with handle {winid} and
----buffer with number {bufnr}. Like `:setlocal` if {bufnr} is provided, like
----`:set` otherwise. If [{winid}] is omitted then the current window is
----used. Invalid {winid}, {bufnr} or key is an error.
+--- Get or set window-scoped |options| for the window with handle {winid} and
+--- buffer with number {bufnr}. Like `:setlocal` if {bufnr} is provided, like
+--- `:set` otherwise. If [{winid}] is omitted then the current window is
+--- used. Invalid {winid}, {bufnr} or key is an error.
---
----Note: only {bufnr} with value `0` (the current buffer in the window) is
----supported.
+--- Note: only {bufnr} with value `0` (the current buffer in the window) is
+--- supported.
---
----Example: <pre>lua
---- local winid = vim.api.nvim_get_current_win()
---- vim.wo[winid].number = true -- same as vim.wo.number = true
---- print(vim.wo.foldmarker)
---- print(vim.wo.quux) -- error: invalid key
---- vim.wo[winid][0].spell = false -- like ':setlocal nospell'
+--- Example:
---
----</pre>
+--- ```lua
+--- local winid = vim.api.nvim_get_current_win()
+--- vim.wo[winid].number = true -- same as vim.wo.number = true
+--- print(vim.wo.foldmarker)
+--- print(vim.wo.quux) -- error: invalid key
+--- vim.wo[winid][0].spell = false -- like ':setlocal nospell'
+--- ```
vim.wo = new_win_opt_accessor()
---@brief [[
@@ -716,7 +727,10 @@ local function create_option_accessor(scope)
return setmetatable({}, {
__index = function(_, k)
- return make_option(k, api.nvim_get_option_value(k, {}))
+ -- vim.opt_global must get global value only
+ -- vim.opt_local may fall back to global value like vim.opt
+ local opts = { scope = scope == 'global' and 'global' or nil }
+ return make_option(k, api.nvim_get_option_value(k, opts))
end,
__newindex = function(_, k, v)
@@ -795,77 +809,93 @@ end
--- @diagnostic disable-next-line:unused-local used for gen_vimdoc
local Option = {} -- luacheck: no unused
----Returns a Lua-representation of the option. Boolean, number and string
----values will be returned in exactly the same fashion.
+--- Returns a Lua-representation of the option. Boolean, number and string
+--- values will be returned in exactly the same fashion.
---
----For values that are comma-separated lists, an array will be returned with
----the values as entries in the array: <pre>lua
---- vim.cmd [[set wildignore=*.pyc,*.o]]
+--- For values that are comma-separated lists, an array will be returned with
+--- the values as entries in the array:
---
---- vim.print(vim.opt.wildignore:get())
---- -- { "*.pyc", "*.o", }
+--- ```lua
+--- vim.cmd [[set wildignore=*.pyc,*.o]]
---
---- for _, ignore_pattern in ipairs(vim.opt.wildignore:get()) do
---- print("Will ignore:", ignore_pattern)
---- end
---- -- Will ignore: *.pyc
---- -- Will ignore: *.o
----</pre>
+--- vim.print(vim.opt.wildignore:get())
+--- -- { "*.pyc", "*.o", }
---
----For values that are comma-separated maps, a table will be returned with
----the names as keys and the values as entries: <pre>lua
---- vim.cmd [[set listchars=space:_,tab:>~]]
+--- for _, ignore_pattern in ipairs(vim.opt.wildignore:get()) do
+--- print("Will ignore:", ignore_pattern)
+--- end
+--- -- Will ignore: *.pyc
+--- -- Will ignore: *.o
+--- ```
---
---- vim.print(vim.opt.listchars:get())
---- -- { space = "_", tab = ">~", }
+--- For values that are comma-separated maps, a table will be returned with
+--- the names as keys and the values as entries:
---
---- for char, representation in pairs(vim.opt.listchars:get()) do
---- print(char, "=>", representation)
---- end
----</pre>
+--- ```lua
+--- vim.cmd [[set listchars=space:_,tab:>~]]
---
----For values that are lists of flags, a set will be returned with the flags
----as keys and `true` as entries. <pre>lua
---- vim.cmd [[set formatoptions=njtcroql]]
+--- vim.print(vim.opt.listchars:get())
+--- -- { space = "_", tab = ">~", }
---
---- vim.print(vim.opt.formatoptions:get())
---- -- { n = true, j = true, c = true, ... }
+--- for char, representation in pairs(vim.opt.listchars:get()) do
+--- print(char, "=>", representation)
+--- end
+--- ```
+---
+--- For values that are lists of flags, a set will be returned with the flags
+--- as keys and `true` as entries.
+---
+--- ```lua
+--- vim.cmd [[set formatoptions=njtcroql]]
+---
+--- vim.print(vim.opt.formatoptions:get())
+--- -- { n = true, j = true, c = true, ... }
+---
+--- local format_opts = vim.opt.formatoptions:get()
+--- if format_opts.j then
+--- print("J is enabled!")
+--- end
+--- ```
---
---- local format_opts = vim.opt.formatoptions:get()
---- if format_opts.j then
---- print("J is enabled!")
---- end
----</pre>
---@return string|integer|boolean|nil value of option
---@diagnostic disable-next-line:unused-local used for gen_vimdoc
function Option:get() end
----Append a value to string-style options. See |:set+=|
+--- Append a value to string-style options. See |:set+=|
+---
+--- These are equivalent:
+---
+--- ```lua
+--- vim.opt.formatoptions:append('j')
+--- vim.opt.formatoptions = vim.opt.formatoptions + 'j'
+--- ```
---
----These are equivalent: <pre>lua
---- vim.opt.formatoptions:append('j')
---- vim.opt.formatoptions = vim.opt.formatoptions + 'j'
----</pre>
---@param value string Value to append
---- @diagnostic disable-next-line:unused-local used for gen_vimdoc
+---@diagnostic disable-next-line:unused-local used for gen_vimdoc
function Option:append(value) end -- luacheck: no unused
----Prepend a value to string-style options. See |:set^=|
+--- Prepend a value to string-style options. See |:set^=|
+---
+--- These are equivalent:
+---
+--- ```lua
+--- vim.opt.wildignore:prepend('*.o')
+--- vim.opt.wildignore = vim.opt.wildignore ^ '*.o'
+--- ```
---
----These are equivalent: <pre>lua
---- vim.opt.wildignore:prepend('*.o')
---- vim.opt.wildignore = vim.opt.wildignore ^ '*.o'
----</pre>
---@param value string Value to prepend
---@diagnostic disable-next-line:unused-local used for gen_vimdoc
function Option:prepend(value) end -- luacheck: no unused
----Remove a value from string-style options. See |:set-=|
+--- Remove a value from string-style options. See |:set-=|
+---
+--- These are equivalent:
+---
+--- ```lua
+--- vim.opt.wildignore:remove('*.pyc')
+--- vim.opt.wildignore = vim.opt.wildignore - '*.pyc'
+--- ```
---
----These are equivalent: <pre>lua
---- vim.opt.wildignore:remove('*.pyc')
---- vim.opt.wildignore = vim.opt.wildignore - '*.pyc'
----</pre>
---@param value string Value to remove
---@diagnostic disable-next-line:unused-local used for gen_vimdoc
function Option:remove(value) end -- luacheck: no unused
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index a1f3020c88..b8d3906b7f 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -553,14 +553,16 @@ end
--- followed by namespace configuration, and finally global configuration.
---
--- For example, if a user enables virtual text globally with
---- <pre>lua
---- vim.diagnostic.config({ virtual_text = true })
---- </pre>
+---
+--- ```lua
+--- vim.diagnostic.config({ virtual_text = true })
+--- ```
---
--- and a diagnostic producer sets diagnostics with
---- <pre>lua
---- vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
---- </pre>
+---
+--- ```lua
+--- vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
+--- ```
---
--- then virtual text will not be enabled for those diagnostics.
---
@@ -1601,18 +1603,20 @@ end
--- Parse a diagnostic from a string.
---
--- For example, consider a line of output from a linter:
---- <pre>
+---
+--- ```
--- WARNING filename:27:3: Variable 'foo' does not exist
---- </pre>
+--- ```
---
--- This can be parsed into a diagnostic |diagnostic-structure|
--- with:
---- <pre>lua
---- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
---- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
---- local groups = { "severity", "lnum", "col", "message" }
---- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
---- </pre>
+---
+--- ```lua
+--- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+--- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+--- local groups = { "severity", "lnum", "col", "message" }
+--- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+--- ```
---
---@param str string String to parse diagnostics from.
---@param pat string Lua pattern with capture groups.
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 6fb6f475bc..c7f025f9e7 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -592,6 +592,7 @@ local extension = {
ly = 'lilypond',
ily = 'lilypond',
liquid = 'liquid',
+ liq = 'liquidsoap',
cl = 'lisp',
L = 'lisp',
lisp = 'lisp',
@@ -2081,43 +2082,45 @@ end
--- See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
---
--- Example:
---- <pre>lua
---- vim.filetype.add({
---- extension = {
---- foo = 'fooscript',
---- bar = function(path, bufnr)
---- if some_condition() then
---- return 'barscript', function(bufnr)
---- -- Set a buffer variable
---- vim.b[bufnr].barscript_version = 2
---- end
---- end
---- return 'bar'
---- end,
---- },
---- filename = {
---- ['.foorc'] = 'toml',
---- ['/etc/foo/config'] = 'toml',
---- },
---- pattern = {
---- ['.*/etc/foo/.*'] = 'fooscript',
---- -- Using an optional priority
---- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
---- -- A pattern containing an environment variable
---- ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
---- ['README.(%a+)$'] = function(path, bufnr, ext)
---- if ext == 'md' then
---- return 'markdown'
---- elseif ext == 'rst' then
---- return 'rst'
---- end
---- end,
---- },
---- })
---- </pre>
+---
+--- ```lua
+--- vim.filetype.add({
+--- extension = {
+--- foo = 'fooscript',
+--- bar = function(path, bufnr)
+--- if some_condition() then
+--- return 'barscript', function(bufnr)
+--- -- Set a buffer variable
+--- vim.b[bufnr].barscript_version = 2
+--- end
+--- end
+--- return 'bar'
+--- end,
+--- },
+--- filename = {
+--- ['.foorc'] = 'toml',
+--- ['/etc/foo/config'] = 'toml',
+--- },
+--- pattern = {
+--- ['.*/etc/foo/.*'] = 'fooscript',
+--- -- Using an optional priority
+--- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
+--- -- A pattern containing an environment variable
+--- ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
+--- ['README.(%a+)$'] = function(path, bufnr, ext)
+--- if ext == 'md' then
+--- return 'markdown'
+--- elseif ext == 'rst' then
+--- return 'rst'
+--- end
+--- end,
+--- },
+--- })
+--- ```
---
--- To add a fallback match on contents, use
---- <pre>lua
+---
+--- ```lua
--- vim.filetype.add {
--- pattern = {
--- ['.*'] = {
@@ -2133,7 +2136,7 @@ end
--- },
--- },
--- }
---- </pre>
+--- ```
---
---@param filetypes vim.filetype.add.filetypes A table containing new filetype maps (see example).
function M.add(filetypes)
@@ -2256,19 +2259,19 @@ end
--- Each of the three options is specified using a key to the single argument of this function.
--- Example:
---
---- <pre>lua
---- -- Using a buffer number
---- vim.filetype.match({ buf = 42 })
+--- ```lua
+--- -- Using a buffer number
+--- vim.filetype.match({ buf = 42 })
---
---- -- Override the filename of the given buffer
---- vim.filetype.match({ buf = 42, filename = 'foo.c' })
+--- -- Override the filename of the given buffer
+--- vim.filetype.match({ buf = 42, filename = 'foo.c' })
---
---- -- Using a filename without a buffer
---- vim.filetype.match({ filename = 'main.lua' })
+--- -- Using a filename without a buffer
+--- vim.filetype.match({ filename = 'main.lua' })
---
---- -- Using file contents
---- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
---- </pre>
+--- -- Using file contents
+--- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
+--- ```
---
---@param args vim.filetype.match.args Table specifying which matching strategy to use.
--- Accepted keys are:
@@ -2404,9 +2407,10 @@ end
--- is set, meaning it should respect all FileType autocmds and ftplugin files.
---
--- Example:
---- <pre>lua
---- vim.filetype.get_option('vim', 'commentstring')
---- </pre>
+---
+--- ```lua
+--- vim.filetype.get_option('vim', 'commentstring')
+--- ```
---
--- Note: this uses |nvim_get_option_value()| but caches the result.
--- This means |ftplugin| and |FileType| autocommands are only
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 7cd9e3cf04..22612a7255 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -5,7 +5,8 @@ local iswin = vim.uv.os_uname().sysname == 'Windows_NT'
--- Iterate over all the parents of the given path.
---
--- Example:
---- <pre>lua
+---
+--- ```lua
--- local root_dir
--- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
--- if vim.fn.isdirectory(dir .. "/.git") == 1 then
@@ -17,7 +18,7 @@ local iswin = vim.uv.os_uname().sysname == 'Windows_NT'
--- if root_dir then
--- print("Found git repository at", root_dir)
--- end
---- </pre>
+--- ```
---
---@param start (string) Initial path.
---@return fun(_, dir: string): string? # Iterator
@@ -165,7 +166,8 @@ end
--- to narrow the search to find only that type.
---
--- Examples:
---- <pre>lua
+---
+--- ```lua
--- -- location of Cargo.toml from the current buffer's path
--- local cargo = vim.fs.find('Cargo.toml', {
--- upward = true,
@@ -183,7 +185,7 @@ end
--- local cpp_hpp = vim.fs.find(function(name, path)
--- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$')
--- end, {limit = math.huge, type = 'file'})
---- </pre>
+--- ```
---
---@param names (string|string[]|fun(name: string, path: string): boolean) Names of the items to find.
--- Must be base names, paths and globs are not supported when {names} is a string or a table.
@@ -322,16 +324,17 @@ end
--- variables are also expanded.
---
--- Examples:
---- <pre>lua
---- vim.fs.normalize('C:\\\\Users\\\\jdoe')
---- --> 'C:/Users/jdoe'
---
---- vim.fs.normalize('~/src/neovim')
---- --> '/home/jdoe/src/neovim'
+--- ```lua
+--- vim.fs.normalize('C:\\\\Users\\\\jdoe')
+--- -- 'C:/Users/jdoe'
+---
+--- vim.fs.normalize('~/src/neovim')
+--- -- '/home/jdoe/src/neovim'
---
---- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
---- --> '/Users/jdoe/.config/nvim/init.vim'
---- </pre>
+--- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+--- -- '/Users/jdoe/.config/nvim/init.vim'
+--- ```
---
---@param path (string) Path to normalize
---@param opts table|nil Options:
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index fd4fb54a5b..fc2fd43c97 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -1,23 +1,24 @@
---@defgroup vim.highlight
---
----@brief
----Nvim includes a function for highlighting a selection on yank.
+--- Nvim includes a function for highlighting a selection on yank.
---
----To enable it, add the following to your `init.vim`:
----<pre>vim
---- au TextYankPost * silent! lua vim.highlight.on_yank()
----</pre>
+--- To enable it, add the following to your `init.vim`:
---
----You can customize the highlight group and the duration of
----the highlight via:
----<pre>vim
---- au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
----</pre>
+--- ```vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank()
+--- ```
---
----If you want to exclude visual selections from highlighting on yank, use:
----<pre>vim
---- au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
----</pre>
+--- You can customize the highlight group and the duration of the highlight via:
+---
+--- ```vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
+--- ```
+---
+--- If you want to exclude visual selections from highlighting on yank, use:
+---
+--- ```vim
+--- au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
+--- ```
local api = vim.api
diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua
index 2b55ddc787..df593be097 100644
--- a/runtime/lua/vim/keymap.lua
+++ b/runtime/lua/vim/keymap.lua
@@ -2,20 +2,21 @@ local keymap = {}
--- Adds a new |mapping|.
--- Examples:
---- <pre>lua
---- -- Map to a Lua function:
---- vim.keymap.set('n', 'lhs', function() print("real lua function") end)
---- -- Map to multiple modes:
---- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true })
---- -- Buffer-local mapping:
---- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 })
---- -- Expr mapping:
---- vim.keymap.set('i', '<Tab>', function()
---- return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
---- end, { expr = true })
---- -- <Plug> mapping:
---- vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
---- </pre>
+---
+--- ```lua
+--- -- Map to a Lua function:
+--- vim.keymap.set('n', 'lhs', function() print("real lua function") end)
+--- -- Map to multiple modes:
+--- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true })
+--- -- Buffer-local mapping:
+--- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 })
+--- -- Expr mapping:
+--- vim.keymap.set('i', '<Tab>', function()
+--- return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
+--- end, { expr = true })
+--- -- <Plug> mapping:
+--- vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
+--- ```
---
---@param mode string|table Mode short-name, see |nvim_set_keymap()|.
--- Can also be list of modes to create mapping on multiple modes.
@@ -80,11 +81,13 @@ end
--- Remove an existing mapping.
--- Examples:
---- <pre>lua
---- vim.keymap.del('n', 'lhs')
---
---- vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 })
---- </pre>
+--- ```lua
+--- vim.keymap.del('n', 'lhs')
+---
+--- vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 })
+--- ```
+---
---@param opts table|nil A table of optional arguments:
--- - "buffer": (number|boolean) Remove a mapping from the given buffer.
--- When `0` or `true`, use the current buffer.
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index b0caf5af35..f68ca7e88c 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -809,13 +809,14 @@ end
--- Attaches the current buffer to the client.
---
--- Example:
---- <pre>lua
+---
+--- ```lua
--- vim.lsp.start({
--- name = 'my-server-name',
--- cmd = {'name-of-language-server-executable'},
--- root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
--- })
---- </pre>
+--- ```
---
--- See |vim.lsp.start_client()| for all available options. The most important are:
---
@@ -1955,8 +1956,6 @@ function lsp.buf_detach_client(bufnr, client_id)
local namespace = lsp.diagnostic.get_namespace(client_id)
vim.diagnostic.reset(namespace, bufnr)
-
- vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
end
--- Checks if a buffer is attached for a particular client.
@@ -1990,9 +1989,10 @@ end
---
--- You can also use the `stop()` function on a |vim.lsp.client| object.
--- To stop all clients:
---- <pre>lua
+---
+--- ```lua
--- vim.lsp.stop_client(vim.lsp.get_clients())
---- </pre>
+--- ```
---
--- By default asks the server to shutdown, unless stop was requested
--- already for this client, then force-shutdown is attempted.
@@ -2504,12 +2504,7 @@ end
---@param bufnr integer Buffer number
---@param fn function Function to run on each client attached to buffer
--- {bufnr}. The function takes the client, client ID, and
---- buffer number as arguments. Example:
---- <pre>lua
---- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
---- vim.print(client)
---- end)
---- </pre>
+--- buffer number as arguments.
---@deprecated use lsp.get_clients({ bufnr = bufnr }) with regular loop
function lsp.for_each_buffer_client(bufnr, fn)
return for_each_buffer_client(bufnr, fn)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6cd0aa1e95..8a29fac2b5 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -168,13 +168,11 @@ end
---
--- - filter (function|nil):
--- Predicate used to filter clients. Receives a client as argument and must return a
---- boolean. Clients matching the predicate are included. Example:
----
---- <pre>lua
---- -- Never request typescript-language-server for formatting
---- vim.lsp.buf.format {
---- filter = function(client) return client.name ~= "tsserver" end
---- }
+--- boolean. Clients matching the predicate are included. Example: <pre>lua
+--- -- Never request typescript-language-server for formatting
+--- vim.lsp.buf.format {
+--- filter = function(client) return client.name ~= "tsserver" end
+--- }
--- </pre>
---
--- - async boolean|nil
@@ -555,11 +553,12 @@ end
--- Send request to the server to resolve document highlights for the current
--- text document position. This request can be triggered by a key mapping or
--- by events such as `CursorHold`, e.g.:
---- <pre>vim
---- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
---- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
---- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
---- </pre>
+---
+--- ```vim
+--- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
+--- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
+--- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
+--- ```
---
--- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups
--- to be defined or you won't be able to see the actual highlights.
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index d581eb985f..384d09ee48 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -255,10 +255,10 @@ end
--- It is recommended to trigger this using an autocmd or via keymap.
---
--- Example:
---- <pre>vim
---- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
---- </pre>
---
+--- ```vim
+--- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh()
+--- ```
function M.refresh()
local params = {
textDocument = util.make_text_document_params(),
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 2a77992c4d..73ffa1a46c 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -203,7 +203,8 @@ end
---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific
--- configuration can be set using |vim.lsp.with()|:
---- <pre>lua
+---
+--- ```lua
--- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
--- vim.lsp.diagnostic.on_publish_diagnostics, {
--- -- Enable underline, use default values
@@ -221,7 +222,7 @@ end
--- update_in_insert = false,
--- }
--- )
---- </pre>
+--- ```
---
---@param config table Configuration table (see |vim.diagnostic.config()|).
function M.on_publish_diagnostics(_, result, ctx, config)
@@ -263,7 +264,8 @@ end
---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific
--- configuration can be set using |vim.lsp.with()|:
---- <pre>lua
+---
+--- ```lua
--- vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with(
--- vim.lsp.diagnostic.on_diagnostic, {
--- -- Enable underline, use default values
@@ -281,7 +283,7 @@ end
--- update_in_insert = false,
--- }
--- )
---- </pre>
+--- ```
---
---@param config table Configuration table (see |vim.diagnostic.config()|).
function M.on_diagnostic(_, result, ctx, config)
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 81d4d6cceb..4ea3dde81c 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -342,16 +342,18 @@ M[ms.textDocument_completion] = function(_, result, _, _)
end
--- |lsp-handler| for the method "textDocument/hover"
---- <pre>lua
---- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
---- vim.lsp.handlers.hover, {
---- -- Use a sharp border with `FloatBorder` highlights
---- border = "single",
---- -- add the title in hover float window
---- title = "hover"
---- }
---- )
---- </pre>
+---
+--- ```lua
+--- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+--- vim.lsp.handlers.hover, {
+--- -- Use a sharp border with `FloatBorder` highlights
+--- border = "single",
+--- -- add the title in hover float window
+--- title = "hover"
+--- }
+--- )
+--- ```
+---
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
@@ -430,15 +432,20 @@ M[ms.textDocument_typeDefinition] = location_handler
M[ms.textDocument_implementation] = location_handler
--- |lsp-handler| for the method "textDocument/signatureHelp".
+---
--- The active parameter is highlighted with |hl-LspSignatureActiveParameter|.
---- <pre>lua
---- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
---- vim.lsp.handlers.signature_help, {
---- -- Use a sharp border with `FloatBorder` highlights
---- border = "single"
---- }
---- )
---- </pre>
+---
+--- ```lua
+--- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+--- vim.lsp.handlers.signature_help, {
+--- -- Use a sharp border with `FloatBorder` highlights
+--- border = "single"
+--- }
+--- )
+--- ```
+---
+---@param result table Response from the language server
+---@param ctx table Client context
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 5b20344bd3..a5831c0beb 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -555,9 +555,10 @@ local M = {}
--- delete the semanticTokensProvider table from the {server_capabilities} of
--- your client in your |LspAttach| callback or your configuration's
--- `on_attach` callback:
---- <pre>lua
---- client.server_capabilities.semanticTokensProvider = nil
---- </pre>
+---
+--- ```lua
+--- client.server_capabilities.semanticTokensProvider = nil
+--- ```
---
---@param bufnr integer
---@param client_id integer
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 422d49d746..0c38fa955a 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -65,19 +65,21 @@ end
--- (as opposed to |vim.split()| which is "eager").
---
--- Example:
---- <pre>lua
---- for s in vim.gsplit(':aa::b:', ':', {plain=true}) do
---- print(s)
---- end
---- </pre>
+---
+--- ```lua
+--- for s in vim.gsplit(':aa::b:', ':', {plain=true}) do
+--- print(s)
+--- end
+--- ```
---
--- If you want to also inspect the separator itself (instead of discarding it), use
--- |string.gmatch()|. Example:
---- <pre>lua
---- for word, num in ('foo111bar222'):gmatch('([^0-9]*)(%d*)') do
---- print(('word: %s num: %s'):format(word, num))
---- end
---- </pre>
+---
+--- ```lua
+--- for word, num in ('foo111bar222'):gmatch('([^0-9]*)(%d*)') do
+--- print(('word: %s num: %s'):format(word, num))
+--- end
+--- ```
---
--- @see |string.gmatch()|
--- @see |vim.split()|
@@ -165,12 +167,13 @@ end
--- |vim.gsplit()|).
---
--- Examples:
---- <pre>lua
---- split(":aa::b:", ":") --> {'','aa','','b',''}
---- split("axaby", "ab?") --> {'','x','y'}
---- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
---- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
---- </pre>
+---
+--- ```lua
+--- split(":aa::b:", ":") --> {'','aa','','b',''}
+--- split("axaby", "ab?") --> {'','x','y'}
+--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
+--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
+--- ```
---
---@see |vim.gsplit()|
---@see |string.gmatch()|
@@ -259,12 +262,13 @@ end
--- a predicate that is checked for each value.
---
--- Example:
---- <pre>lua
---- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
---- return vim.deep_equal(v, { 'b', 'c' })
---- end, { predicate = true })
---- -- true
---- </pre>
+---
+--- ```lua
+--- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+--- return vim.deep_equal(v, { 'b', 'c' })
+--- end, { predicate = true })
+--- -- true
+--- ```
---
---@see |vim.list_contains()| for checking values in list-like tables
---
@@ -455,10 +459,11 @@ end
--- Return `nil` if the key does not exist.
---
--- Examples:
---- <pre>lua
---- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
---- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
---- </pre>
+---
+--- ```lua
+--- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
+--- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
+--- ```
---
---@param o table Table to index
---@param ... any Optional keys (0 or more, variadic) via which to index the table
@@ -626,10 +631,10 @@ end
--- Counts the number of non-nil values in table `t`.
---
---- <pre>lua
+--- ```lua
--- vim.tbl_count({ a=1, b=2 }) --> 2
--- vim.tbl_count({ 1, 2 }) --> 2
---- </pre>
+--- ```
---
---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua
---@param t table Table
@@ -703,38 +708,41 @@ end
--- Validates a parameter specification (types and values).
---
--- Usage example:
---- <pre>lua
---- function user.new(name, age, hobbies)
---- vim.validate{
---- name={name, 'string'},
---- age={age, 'number'},
---- hobbies={hobbies, 'table'},
---- }
---- ...
---- end
---- </pre>
+---
+--- ```lua
+--- function user.new(name, age, hobbies)
+--- vim.validate{
+--- name={name, 'string'},
+--- age={age, 'number'},
+--- hobbies={hobbies, 'table'},
+--- }
+--- ...
+--- end
+--- ```
---
--- Examples with explicit argument values (can be run directly):
---- <pre>lua
---- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
---- --> NOP (success)
---
---- vim.validate{arg1={1, 'table'}}
---- --> error('arg1: expected table, got number')
+--- ```lua
+--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
+--- --> NOP (success)
---
---- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
---- --> error('arg1: expected even number, got 3')
---- </pre>
+--- vim.validate{arg1={1, 'table'}}
+--- --> error('arg1: expected table, got number')
+---
+--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
+--- --> error('arg1: expected even number, got 3')
+--- ```
---
--- If multiple types are valid they can be given as a list.
---- <pre>lua
---- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
---- --> NOP (success)
---
---- vim.validate{arg1={1, {'string', 'table'}}}
---- --> error('arg1: expected string|table, got number')
+--- ```lua
+--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
+--- -- NOP (success)
+---
+--- vim.validate{arg1={1, {'string', 'table'}}}
+--- -- error('arg1: expected string|table, got number')
---
---- </pre>
+--- ```
---
---@param opt table Names of parameters to validate. Each key is a parameter
--- name; each value is a tuple in one of these forms:
@@ -866,10 +874,10 @@ end
--- If {create} is `nil`, this will create a defaulttable whose constructor function is
--- this function, effectively allowing to create nested tables on the fly:
---
---- <pre>lua
+--- ```lua
--- local a = vim.defaulttable()
--- a.b.c = 1
---- </pre>
+--- ```
---
---@param create function?(key:any):any The function called to create a missing value.
---@return table Empty table with metamethod
@@ -938,7 +946,7 @@ do
--- Create a ring buffer limited to a maximal number of items.
--- Once the buffer is full, adding a new entry overrides the oldest entry.
---
- --- <pre>
+ --- ```lua
--- local ringbuf = vim.ringbuf(4)
--- ringbuf:push("a")
--- ringbuf:push("b")
@@ -952,7 +960,7 @@ do
--- for val in ringbuf do
--- print(val)
--- end
- --- </pre>
+ --- ```
---
--- Returns a Ringbuf instance with the following methods:
---
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 4f84fc2e0f..cc8be72670 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -441,14 +441,15 @@ end
--- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`.
---
--- Example:
---- <pre>lua
+---
+--- ```lua
--- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
--- callback = function(args)
--- vim.treesitter.start(args.buf, 'latex')
--- vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed
--- end
--- })
---- </pre>
+--- ```
---
---@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer)
---@param lang (string|nil) Language of the parser (default: buffer filetype)
@@ -472,7 +473,7 @@ end
--- Open a window that displays a textual representation of the nodes in the language tree.
---
--- While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the
---- display of the source language of each node, "o" to toggle the query previewer, and press
+--- display of the source language of each node, "o" to toggle the query editor, and press
--- <Enter> to jump to the node under the cursor in the source buffer.
---
--- Can also be shown with `:InspectTree`. *:InspectTree*
@@ -494,17 +495,12 @@ function M.inspect_tree(opts)
require('vim.treesitter.dev').inspect_tree(opts)
end
---- Open a window for live editing of a treesitter query.
----
---- Can also be shown with `:PreviewQuery`. *:PreviewQuery*
-function M.preview_query()
- require('vim.treesitter.dev').preview_query()
-end
-
--- Returns the fold level for {lnum} in the current buffer. Can be set directly to 'foldexpr':
---- <pre>lua
+---
+--- ```lua
--- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
---- </pre>
+--- ```
+---
---@param lnum integer|nil Line number to calculate fold level for
---@return string
function M.foldexpr(lnum)
diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua
index 9a94f12c16..d01b7be3b0 100644
--- a/runtime/lua/vim/treesitter/_meta.lua
+++ b/runtime/lua/vim/treesitter/_meta.lua
@@ -50,7 +50,8 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end
---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
---@class TSParser
----@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean?): TSTree, integer[]
+---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: true): TSTree, Range6[]
+---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: false|nil): TSTree, Range4[]
---@field reset fun(self: TSParser)
---@field included_ranges fun(self: TSParser, include_bytes: boolean?): integer[]
---@field set_included_ranges fun(self: TSParser, ranges: (Range6|TSNode)[])
diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua
index 3dd0177a81..abf0bf345d 100644
--- a/runtime/lua/vim/treesitter/_query_linter.lua
+++ b/runtime/lua/vim/treesitter/_query_linter.lua
@@ -1,8 +1,6 @@
local api = vim.api
local namespace = api.nvim_create_namespace('vim.treesitter.query_linter')
--- those node names exist for every language
-local BUILT_IN_NODE_NAMES = { '_', 'ERROR' }
local M = {}
@@ -10,11 +8,13 @@ local M = {}
--- @field langs string[]
--- @field clear boolean
+--- @alias vim.treesitter.ParseError {msg: string, range: Range4}
+
--- @private
--- Caches parse results for queries for each language.
--- Entries of parse_cache[lang][query_text] will either be true for successful parse or contain the
---- error message of the parse
---- @type table<string,table<string,string|true>>
+--- message and range of the parse error.
+--- @type table<string,table<string,vim.treesitter.ParseError|true>>
local parse_cache = {}
--- Contains language dependent context for the query linter
@@ -26,20 +26,16 @@ local parse_cache = {}
--- @private
--- Adds a diagnostic for node in the query buffer
--- @param diagnostics Diagnostic[]
---- @param node TSNode
---- @param buf integer
+--- @param range Range4
--- @param lint string
--- @param lang string?
-local function add_lint_for_node(diagnostics, node, buf, lint, lang)
- local node_text = vim.treesitter.get_node_text(node, buf):gsub('\n', ' ')
- --- @type string
- local message = lint .. ': ' .. node_text
- local error_range = { node:range() }
+local function add_lint_for_node(diagnostics, range, lint, lang)
+ local message = lint:gsub('\n', ' ')
diagnostics[#diagnostics + 1] = {
- lnum = error_range[1],
- end_lnum = error_range[3],
- col = error_range[2],
- end_col = error_range[4],
+ lnum = range[1],
+ end_lnum = range[3],
+ col = range[2],
+ end_col = range[4],
severity = vim.diagnostic.ERROR,
message = message,
source = lang,
@@ -92,6 +88,31 @@ local lint_query = [[;; query
]]
--- @private
+--- @param err string
+--- @param node TSNode
+--- @return vim.treesitter.ParseError
+local function get_error_entry(err, node)
+ local start_line, start_col = node:range()
+ local line_offset, col_offset, msg = err:gmatch('.-:%d+: Query error at (%d+):(%d+)%. ([^:]+)')() ---@type string, string, string
+ start_line, start_col =
+ start_line + tonumber(line_offset) - 1, start_col + tonumber(col_offset) - 1
+ local end_line, end_col = start_line, start_col
+ if msg:match('^Invalid syntax') or msg:match('^Impossible') then
+ -- Use the length of the underlined node
+ local underlined = vim.split(err, '\n')[2]
+ end_col = end_col + #underlined
+ elseif msg:match('^Invalid') then
+ -- Use the length of the problematic type/capture/field
+ end_col = end_col + #msg:match('"([^"]+)"')
+ end
+
+ return {
+ msg = msg,
+ range = { start_line, start_col, end_line, end_col },
+ }
+end
+
+--- @private
--- @param node TSNode
--- @param buf integer
--- @param lang string
@@ -106,104 +127,19 @@ local function check_toplevel(node, buf, lang, diagnostics)
local lang_cache = parse_cache[lang]
if lang_cache[query_text] == nil then
- local ok, err = pcall(vim.treesitter.query.parse, lang, query_text)
+ local cache_val, err = pcall(vim.treesitter.query.parse, lang, query_text) ---@type boolean|vim.treesitter.ParseError, string|Query
- if not ok and type(err) == 'string' then
- err = err:match('.-:%d+: (.+)')
+ if not cache_val and type(err) == 'string' then
+ cache_val = get_error_entry(err, node)
end
- lang_cache[query_text] = ok or err
+ lang_cache[query_text] = cache_val
end
local cache_entry = lang_cache[query_text]
- if type(cache_entry) == 'string' then
- add_lint_for_node(diagnostics, node, buf, cache_entry, lang)
- end
-end
-
---- @private
---- @param node TSNode
---- @param buf integer
---- @param lang string
---- @param parser_info table
---- @param diagnostics Diagnostic[]
-local function check_field(node, buf, lang, parser_info, diagnostics)
- local field_name = vim.treesitter.get_node_text(node, buf)
- if not vim.tbl_contains(parser_info.fields, field_name) then
- add_lint_for_node(diagnostics, node, buf, 'Invalid field', lang)
- end
-end
-
---- @private
---- @param node TSNode
---- @param buf integer
---- @param lang string
---- @param parser_info (table)
---- @param diagnostics Diagnostic[]
-local function check_node(node, buf, lang, parser_info, diagnostics)
- local node_type = vim.treesitter.get_node_text(node, buf)
- local is_named = node_type:sub(1, 1) ~= '"'
-
- if not is_named then
- node_type = node_type:gsub('"(.*)".*$', '%1'):gsub('\\(.)', '%1')
- end
-
- local found = vim.tbl_contains(BUILT_IN_NODE_NAMES, node_type)
- or vim.tbl_contains(parser_info.symbols, function(s)
- return vim.deep_equal(s, { node_type, is_named })
- end, { predicate = true })
-
- if not found then
- add_lint_for_node(diagnostics, node, buf, 'Invalid node type', lang)
- end
-end
-
---- @private
---- @param node TSNode
---- @param buf integer
---- @param is_predicate boolean
---- @return string
-local function get_predicate_name(node, buf, is_predicate)
- local name = vim.treesitter.get_node_text(node, buf)
- if is_predicate then
- if vim.startswith(name, 'not-') then
- --- @type string
- name = name:sub(string.len('not-') + 1)
- end
- return name .. '?'
- end
- return name .. '!'
-end
-
---- @private
---- @param predicate_node TSNode
---- @param predicate_type_node TSNode
---- @param buf integer
---- @param lang string?
---- @param diagnostics Diagnostic[]
-local function check_predicate(predicate_node, predicate_type_node, buf, lang, diagnostics)
- local type_string = vim.treesitter.get_node_text(predicate_type_node, buf)
-
- -- Quirk of the query grammar that directives are also predicates!
- if type_string == '?' then
- if
- not vim.tbl_contains(
- vim.treesitter.query.list_predicates(),
- get_predicate_name(predicate_node, buf, true)
- )
- then
- add_lint_for_node(diagnostics, predicate_node, buf, 'Unknown predicate', lang)
- end
- elseif type_string == '!' then
- if
- not vim.tbl_contains(
- vim.treesitter.query.list_directives(),
- get_predicate_name(predicate_node, buf, false)
- )
- then
- add_lint_for_node(diagnostics, predicate_node, buf, 'Unknown directive', lang)
- end
+ if type(cache_entry) ~= 'boolean' then
+ add_lint_for_node(diagnostics, cache_entry.range, cache_entry.msg, lang)
end
end
@@ -214,8 +150,6 @@ end
--- @param lang_context QueryLinterLanguageContext
--- @param diagnostics Diagnostic[]
local function lint_match(buf, match, query, lang_context, diagnostics)
- local predicate --- @type TSNode
- local predicate_type --- @type TSNode
local lang = lang_context.lang
local parser_info = lang_context.parser_info
@@ -223,31 +157,16 @@ local function lint_match(buf, match, query, lang_context, diagnostics)
local cap_id = query.captures[id]
-- perform language-independent checks only for first lang
- if lang_context.is_first_lang then
- if cap_id == 'error' then
- add_lint_for_node(diagnostics, node, buf, 'Syntax error')
- elseif cap_id == 'predicate.name' then
- predicate = node
- elseif cap_id == 'predicate.type' then
- predicate_type = node
- end
+ if lang_context.is_first_lang and cap_id == 'error' then
+ local node_text = vim.treesitter.get_node_text(node, buf):gsub('\n', ' ')
+ add_lint_for_node(diagnostics, { node:range() }, 'Syntax error: ' .. node_text)
end
-- other checks rely on Neovim parser introspection
- if lang and parser_info then
- if cap_id == 'toplevel' then
- check_toplevel(node, buf, lang, diagnostics)
- elseif cap_id == 'field' then
- check_field(node, buf, lang, parser_info, diagnostics)
- elseif cap_id == 'node.named' or cap_id == 'node.anonymous' then
- check_node(node, buf, lang, parser_info, diagnostics)
- end
+ if lang and parser_info and cap_id == 'toplevel' then
+ check_toplevel(node, buf, lang, diagnostics)
end
end
-
- if predicate and predicate_type then
- check_predicate(predicate, predicate_type, buf, lang, diagnostics)
- end
end
--- @private
@@ -339,7 +258,7 @@ function M.omnifunc(findstart, base)
end
end
for _, s in pairs(parser_info.symbols) do
- local text = s[2] and s[1] or '"' .. s[1]:gsub([[\]], [[\\]]) .. '"'
+ local text = s[2] and s[1] or '"' .. s[1]:gsub([[\]], [[\\]]) .. '"' ---@type string
if text:find(base, 1, true) then
table.insert(items, text)
end
diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua
index e7af259d28..7f24ba8590 100644
--- a/runtime/lua/vim/treesitter/dev.lua
+++ b/runtime/lua/vim/treesitter/dev.lua
@@ -351,11 +351,11 @@ function M.inspect_tree(opts)
end,
})
api.nvim_buf_set_keymap(b, 'n', 'o', '', {
- desc = 'Toggle query previewer',
+ desc = 'Toggle query editor',
callback = function()
- local preview_w = vim.b[buf].dev_preview
- if not preview_w or not close_win(preview_w) then
- M.preview_query()
+ local edit_w = vim.b[buf].dev_edit
+ if not edit_w or not close_win(edit_w) then
+ M.edit_query()
end
end,
})
@@ -464,16 +464,16 @@ function M.inspect_tree(opts)
})
end
-local preview_ns = api.nvim_create_namespace('treesitter/dev-preview')
+local edit_ns = api.nvim_create_namespace('treesitter/dev-edit')
---@param query_win integer
---@param base_win integer
-local function update_preview_highlights(query_win, base_win)
+---@param lang string
+local function update_editor_highlights(query_win, base_win, lang)
local base_buf = api.nvim_win_get_buf(base_win)
local query_buf = api.nvim_win_get_buf(query_win)
- local parser = vim.treesitter.get_parser(base_buf)
- local lang = parser:lang()
- api.nvim_buf_clear_namespace(base_buf, preview_ns, 0, -1)
+ local parser = vim.treesitter.get_parser(base_buf, lang)
+ api.nvim_buf_clear_namespace(base_buf, edit_ns, 0, -1)
local query_content = table.concat(api.nvim_buf_get_lines(query_buf, 0, -1, false), '\n')
local ok_query, query = pcall(vim.treesitter.query.parse, lang, query_content)
@@ -493,7 +493,7 @@ local function update_preview_highlights(query_win, base_win)
local capture_name = query.captures[id]
if capture_name == cursor_word then
local lnum, col, end_lnum, end_col = node:range()
- api.nvim_buf_set_extmark(base_buf, preview_ns, lnum, col, {
+ api.nvim_buf_set_extmark(base_buf, edit_ns, lnum, col, {
end_row = end_lnum,
end_col = end_col,
hl_group = 'Visual',
@@ -506,17 +506,18 @@ local function update_preview_highlights(query_win, base_win)
end
--- @private
-function M.preview_query()
+--- @param lang? string language to open the query editor for.
+function M.edit_query(lang)
local buf = api.nvim_get_current_buf()
local win = api.nvim_get_current_win()
- -- Close any existing previewer window
- if vim.b[buf].dev_preview then
- close_win(vim.b[buf].dev_preview)
+ -- Close any existing editor window
+ if vim.b[buf].dev_edit then
+ close_win(vim.b[buf].dev_edit)
end
local cmd = '60vnew'
- -- If the inspector is open, place the previewer above it.
+ -- If the inspector is open, place the editor above it.
local base_win = vim.b[buf].dev_base ---@type integer?
local base_buf = base_win and api.nvim_win_get_buf(base_win)
local inspect_win = base_buf and vim.b[base_buf].dev_inspect
@@ -528,29 +529,29 @@ function M.preview_query()
end
vim.cmd(cmd)
- local ok, parser = pcall(vim.treesitter.get_parser, buf)
+ local ok, parser = pcall(vim.treesitter.get_parser, buf, lang)
if not ok then
return nil, 'No parser available for the given buffer'
end
- local lang = parser:lang()
+ lang = parser:lang()
local query_win = api.nvim_get_current_win()
local query_buf = api.nvim_win_get_buf(query_win)
- vim.b[buf].dev_preview = query_win
+ vim.b[buf].dev_edit = query_win
vim.bo[query_buf].omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
set_dev_properties(query_win, query_buf)
-- Note that omnifunc guesses the language based on the containing folder,
-- so we add the parser's language to the buffer's name so that omnifunc
-- can infer the language later.
- api.nvim_buf_set_name(query_buf, string.format('%s/query_previewer.scm', lang))
+ api.nvim_buf_set_name(query_buf, string.format('%s/query_editor.scm', lang))
- local group = api.nvim_create_augroup('treesitter/dev-preview', {})
+ local group = api.nvim_create_augroup('treesitter/dev-edit', {})
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, {
group = group,
buffer = query_buf,
- desc = 'Update query previewer diagnostics when the query changes',
+ desc = 'Update query editor diagnostics when the query changes',
callback = function()
vim.treesitter.query.lint(query_buf, { langs = lang, clear = false })
end,
@@ -558,37 +559,37 @@ function M.preview_query()
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave', 'CursorMoved', 'BufEnter' }, {
group = group,
buffer = query_buf,
- desc = 'Update query previewer highlights when the cursor moves',
+ desc = 'Update query editor highlights when the cursor moves',
callback = function()
if api.nvim_win_is_valid(win) then
- update_preview_highlights(query_win, win)
+ update_editor_highlights(query_win, win, lang)
end
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
buffer = query_buf,
- desc = 'Clear the query previewer highlights when leaving the previewer',
+ desc = 'Clear highlights when leaving the query editor',
callback = function()
- api.nvim_buf_clear_namespace(buf, preview_ns, 0, -1)
+ api.nvim_buf_clear_namespace(buf, edit_ns, 0, -1)
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
buffer = buf,
- desc = 'Clear the query previewer highlights when leaving the source buffer',
+ desc = 'Clear the query editor highlights when leaving the source buffer',
callback = function()
if not api.nvim_buf_is_loaded(query_buf) then
return true
end
- api.nvim_buf_clear_namespace(query_buf, preview_ns, 0, -1)
+ api.nvim_buf_clear_namespace(query_buf, edit_ns, 0, -1)
end,
})
api.nvim_create_autocmd('BufHidden', {
group = group,
buffer = buf,
- desc = 'Close the previewer window when the source buffer is hidden',
+ desc = 'Close the editor window when the source buffer is hidden',
once = true,
callback = function()
close_win(query_win)
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 8d4d6a9337..496193c6ed 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -2,7 +2,7 @@ local api = vim.api
local query = vim.treesitter.query
local Range = require('vim.treesitter._range')
----@alias TSHlIter fun(): integer, TSNode, TSMetadata
+---@alias TSHlIter fun(end_line: integer|nil): integer, TSNode, TSMetadata
---@class TSHighlightState
---@field next_row integer
@@ -241,40 +241,43 @@ local function on_line_impl(self, buf, line, is_spell_nav)
end
while line >= state.next_row do
- local capture, node, metadata = state.iter()
+ local capture, node, metadata = state.iter(line)
- if capture == nil then
- break
+ local range = { root_end_row + 1, 0, root_end_row + 1, 0 }
+ if node then
+ range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
end
-
- local range = vim.treesitter.get_range(node, buf, metadata[capture])
local start_row, start_col, end_row, end_col = Range.unpack4(range)
- local hl = highlighter_query.hl_cache[capture]
-
- local capture_name = highlighter_query:query().captures[capture]
- local spell = nil ---@type boolean?
- if capture_name == 'spell' then
- spell = true
- elseif capture_name == 'nospell' then
- spell = false
- end
- -- Give nospell a higher priority so it always overrides spell captures.
- local spell_pri_offset = capture_name == 'nospell' and 1 or 0
-
- if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
- local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter)
- + spell_pri_offset
- api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
- end_line = end_row,
- end_col = end_col,
- hl_group = hl,
- ephemeral = true,
- priority = priority,
- conceal = metadata.conceal,
- spell = spell,
- })
+ if capture then
+ local hl = highlighter_query.hl_cache[capture]
+
+ local capture_name = highlighter_query:query().captures[capture]
+ local spell = nil ---@type boolean?
+ if capture_name == 'spell' then
+ spell = true
+ elseif capture_name == 'nospell' then
+ spell = false
+ end
+
+ -- Give nospell a higher priority so it always overrides spell captures.
+ local spell_pri_offset = capture_name == 'nospell' and 1 or 0
+
+ if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
+ local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter)
+ + spell_pri_offset
+ api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
+ end_line = end_row,
+ end_col = end_col,
+ hl_group = hl,
+ ephemeral = true,
+ priority = priority,
+ conceal = metadata.conceal,
+ spell = spell,
+ })
+ end
end
+
if start_row > line then
state.next_row = start_row
end
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index 9695e2c41c..15bf666a1e 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -82,9 +82,8 @@ function M.add(lang, opts)
filetype = { filetype, { 'string', 'table' }, true },
})
- M.register(lang, filetype)
-
if vim._ts_has_language(lang) then
+ M.register(lang, filetype)
return
end
@@ -102,6 +101,7 @@ function M.add(lang, opts)
end
vim._ts_add_language(path, lang, symbol_name)
+ M.register(lang, filetype)
end
--- @param x string|string[]
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 6037b17b20..f931291ed7 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -7,9 +7,9 @@
---
--- To create a LanguageTree (parser object) for a given buffer and language, use:
---
---- <pre>lua
---- local parser = vim.treesitter.get_parser(bufnr, lang)
---- </pre>
+--- ```lua
+--- local parser = vim.treesitter.get_parser(bufnr, lang)
+--- ```
---
--- (where `bufnr=0` means current buffer). `lang` defaults to 'filetype'.
--- Note: currently the parser is retained for the lifetime of a buffer but this may change;
@@ -17,9 +17,9 @@
---
--- Whenever you need to access the current syntax tree, parse the buffer:
---
---- <pre>lua
---- local tree = parser:parse({ start_row, end_row })
---- </pre>
+--- ```lua
+--- local tree = parser:parse({ start_row, end_row })
+--- ```
---
--- This returns a table of immutable |treesitter-tree| objects representing the current state of
--- the buffer. When the plugin wants to access the state after a (possible) edit it must call
@@ -78,13 +78,14 @@ local TSCallbackNames = {
---@field private _opts table Options
---@field private _parser TSParser Parser for language
---@field private _has_regions boolean
----@field private _regions Range6[][]?
+---@field private _regions table<integer, Range6[]>?
---List of regions this tree should manage and parse. If nil then regions are
---taken from _trees. This is mostly a short-lived cache for included_regions()
---@field private _lang string Language name
---@field private _parent_lang? string Parent language name
---@field private _source (integer|string) Buffer or string to parse
----@field private _trees TSTree[] Reference to parsed tree (one for each language)
+---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language).
+---Each key is the index of region, which is synced with _regions and _valid.
---@field private _valid boolean|table<integer,boolean> If the parsed tree is valid
---@field private _logger? fun(logtype: string, msg: string)
---@field private _logfile? file*
@@ -211,7 +212,7 @@ function LanguageTree:_log(...)
end
local info = debug.getinfo(2, 'nl')
- local nregions = #self:included_regions()
+ local nregions = vim.tbl_count(self:included_regions())
local prefix =
string.format('%s:%d: (#regions=%d) ', info.name or '???', info.currentline or 0, nregions)
@@ -244,8 +245,13 @@ function LanguageTree:invalidate(reload)
end
end
---- Returns all trees this language tree contains.
+--- Returns all trees of the regions parsed by this parser.
--- Does not include child languages.
+--- The result is list-like if
+--- * this LanguageTree is the root, in which case the result is empty or a singleton list; or
+--- * the root LanguageTree is fully parsed.
+---
+---@return table<integer, TSTree>
function LanguageTree:trees()
return self._trees
end
@@ -255,16 +261,15 @@ function LanguageTree:lang()
return self._lang
end
---- Determines whether this tree is valid.
---- If the tree is invalid, call `parse()`.
---- This will return the updated tree.
----@param exclude_children boolean|nil
+--- Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest
+--- state of the source. If invalid, user should call |LanguageTree:parse()|.
+---@param exclude_children boolean|nil whether to ignore the validity of children (default `false`)
---@return boolean
function LanguageTree:is_valid(exclude_children)
local valid = self._valid
if type(valid) == 'table' then
- for i = 1, #self:included_regions() do
+ for i, _ in pairs(self:included_regions()) do
if not valid[i] then
return false
end
@@ -328,7 +333,7 @@ end
--- @private
--- @param range boolean|Range?
---- @return integer[] changes
+--- @return Range6[] changes
--- @return integer no_regions_parsed
--- @return number total_parse_time
function LanguageTree:_parse_regions(range)
@@ -370,7 +375,7 @@ function LanguageTree:_add_injections()
local seen_langs = {} ---@type table<string,boolean>
local query_time, injections_by_lang = tcall(self._get_injections, self)
- for lang, injection_ranges in pairs(injections_by_lang) do
+ for lang, injection_regions in pairs(injections_by_lang) do
local has_lang = pcall(language.add, lang)
-- Child language trees should just be ignored if not found, since
@@ -383,7 +388,7 @@ function LanguageTree:_add_injections()
child = self:add_child(lang)
end
- child:set_included_regions(injection_ranges)
+ child:set_included_regions(injection_regions)
seen_langs[lang] = true
end
end
@@ -408,14 +413,14 @@ end
--- Set to `true` to run a complete parse of the source (Note: Can be slow!)
--- Set to `false|nil` to only parse regions with empty ranges (typically
--- only the root tree without injections).
---- @return TSTree[]
+--- @return table<integer, TSTree>
function LanguageTree:parse(range)
if self:is_valid() then
self:_log('valid')
return self._trees
end
- local changes --- @type Range6?
+ local changes --- @type Range6[]?
-- Collect some stats
local no_regions_parsed = 0
@@ -451,11 +456,14 @@ function LanguageTree:parse(range)
return self._trees
end
+---@deprecated Misleading name. Use `LanguageTree:children()` (non-recursive) instead,
+--- add recursion yourself if needed.
--- Invokes the callback for each |LanguageTree| and its children recursively
---
---@param fn fun(tree: LanguageTree, lang: string)
---@param include_self boolean|nil Whether to include the invoking tree in the results
function LanguageTree:for_each_child(fn, include_self)
+ vim.deprecate('LanguageTree:for_each_child()', 'LanguageTree:children()', '0.11')
if include_self then
fn(self, self._lang)
end
@@ -562,7 +570,7 @@ function LanguageTree:_iter_regions(fn)
local all_valid = true
- for i, region in ipairs(self:included_regions()) do
+ for i, region in pairs(self:included_regions()) do
if was_valid or self._valid[i] then
self._valid[i] = fn(i, region)
if not self._valid[i] then
@@ -598,7 +606,7 @@ end
--- nodes, which is useful for templating languages like ERB and EJS.
---
---@private
----@param new_regions Range6[][] List of regions this tree should manage and parse.
+---@param new_regions (Range4|Range6|TSNode)[][] List of regions this tree should manage and parse.
function LanguageTree:set_included_regions(new_regions)
self._has_regions = true
@@ -606,16 +614,20 @@ function LanguageTree:set_included_regions(new_regions)
for _, region in ipairs(new_regions) do
for i, range in ipairs(region) do
if type(range) == 'table' and #range == 4 then
- region[i] = Range.add_bytes(self._source, range)
+ region[i] = Range.add_bytes(self._source, range --[[@as Range4]])
elseif type(range) == 'userdata' then
region[i] = { range:range(true) }
end
end
end
+ -- included_regions is not guaranteed to be list-like, but this is still sound, i.e. if
+ -- new_regions is different from included_regions, then outdated regions in included_regions are
+ -- invalidated. For example, if included_regions = new_regions ++ hole ++ outdated_regions, then
+ -- outdated_regions is invalidated by _iter_regions in else branch.
if #self:included_regions() ~= #new_regions then
-- TODO(lewis6991): inefficient; invalidate trees incrementally
- for _, t in ipairs(self._trees) do
+ for _, t in pairs(self._trees) do
self:_do_callback('changedtree', t:included_ranges(true), t)
end
self._trees = {}
@@ -629,20 +641,22 @@ function LanguageTree:set_included_regions(new_regions)
self._regions = new_regions
end
----Gets the set of included regions
----@return Range6[][]
+---Gets the set of included regions managed by this LanguageTree. This can be different from the
+---regions set by injection query, because a partial |LanguageTree:parse()| drops the regions
+---outside the requested range.
+---@return table<integer, Range6[]>
function LanguageTree:included_regions()
if self._regions then
return self._regions
end
- if not self._has_regions or #self._trees == 0 then
+ if not self._has_regions or next(self._trees) == nil then
-- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1}
return { {} }
end
local regions = {} ---@type Range6[][]
- for i, _ in ipairs(self._trees) do
+ for i, _ in pairs(self._trees) do
regions[i] = self._trees[i]:included_ranges(true)
end
@@ -731,7 +745,8 @@ local has_parser = function(lang)
or #vim.api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0
end
---- Return parser name for language (if exists) or filetype (if registered and exists)
+--- Return parser name for language (if exists) or filetype (if registered and exists).
+--- Also attempts with the input lower-cased.
---
---@param alias string language or filetype name
---@return string? # resolved parser name
@@ -740,10 +755,19 @@ local function resolve_lang(alias)
return alias
end
+ if has_parser(alias:lower()) then
+ return alias:lower()
+ end
+
local lang = vim.treesitter.language.get_lang(alias)
if lang and has_parser(lang) then
return lang
end
+
+ lang = vim.treesitter.language.get_lang(alias:lower())
+ if lang and has_parser(lang) then
+ return lang
+ end
end
---@private
@@ -755,9 +779,10 @@ end
function LanguageTree:_get_injection(match, metadata)
local ranges = {} ---@type Range6[]
local combined = metadata['injection.combined'] ~= nil
+ local injection_lang = metadata['injection.language'] --[[@as string?]]
local lang = metadata['injection.self'] ~= nil and self:lang()
or metadata['injection.parent'] ~= nil and self._parent_lang
- or metadata['injection.language'] --[[@as string?]]
+ or (injection_lang and resolve_lang(injection_lang))
local include_children = metadata['injection.include-children'] ~= nil
for id, node in pairs(match) do
@@ -765,7 +790,7 @@ function LanguageTree:_get_injection(match, metadata)
-- Lang should override any other language tag
if name == 'injection.language' then
local text = vim.treesitter.get_node_text(node, self._source, { metadata = metadata[id] })
- lang = resolve_lang(text) or resolve_lang(text:lower())
+ lang = resolve_lang(text)
elseif name == 'injection.content' then
ranges = get_node_ranges(node, self._source, metadata[id], include_children)
end
@@ -774,7 +799,20 @@ function LanguageTree:_get_injection(match, metadata)
return lang, combined, ranges
end
---- Gets language injection points by language.
+--- Can't use vim.tbl_flatten since a range is just a table.
+---@param regions Range6[][]
+---@return Range6[]
+local function combine_regions(regions)
+ local result = {} ---@type Range6[]
+ for _, region in ipairs(regions) do
+ for _, range in ipairs(region) do
+ result[#result + 1] = range
+ end
+ end
+ return result
+end
+
+--- Gets language injection regions by language.
---
--- This is where most of the injection processing occurs.
---
@@ -819,11 +857,7 @@ function LanguageTree:_get_injections()
for _, entry in pairs(patterns) do
if entry.combined then
- ---@diagnostic disable-next-line:no-unknown
- local regions = vim.tbl_map(function(e)
- return vim.tbl_flatten(e)
- end, entry.regions)
- table.insert(result[lang], regions)
+ table.insert(result[lang], combine_regions(entry.regions))
else
for _, ranges in pairs(entry.regions) do
table.insert(result[lang], ranges)
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 3093657313..44ed37d64e 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -514,7 +514,10 @@ local directive_handlers = {
-- Example: (#trim! @fold)
-- TODO(clason): generalize to arbitrary whitespace removal
['trim!'] = function(match, _, bufnr, pred, metadata)
- local node = match[pred[2]]
+ local capture_id = pred[2]
+ assert(type(capture_id) == 'number')
+
+ local node = match[capture_id]
if not node then
return
end
@@ -526,9 +529,9 @@ local directive_handlers = {
return
end
- while true do
+ while end_row >= start_row do
-- As we only care when end_col == 0, always inspect one line above end_row.
- local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
+ local end_line = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
if end_line ~= '' then
break
@@ -539,7 +542,8 @@ local directive_handlers = {
-- If this produces an invalid range, we just skip it.
if start_row < end_row or (start_row == end_row and start_col <= end_col) then
- metadata.range = { start_row, start_col, end_row, end_col }
+ metadata[capture_id] = metadata[capture_id] or {}
+ metadata[capture_id].range = { start_row, start_col, end_row, end_col }
end
end,
}
@@ -692,7 +696,8 @@ end
--- The iterator returns three values: a numeric id identifying the capture,
--- the captured node, and metadata from any directives processing the match.
--- The following example shows how to get captures by name:
---- <pre>lua
+---
+--- ```lua
--- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
--- local name = query.captures[id] -- name of the capture in the query
--- -- typically useful info about the node:
@@ -700,14 +705,15 @@ end
--- local row1, col1, row2, col2 = node:range() -- range of the capture
--- -- ... use the info here ...
--- end
---- </pre>
+--- ```
---
---@param node TSNode under which the search will occur
---@param source (integer|string) Source buffer or string to extract text from
---@param start integer Starting line for the search
---@param stop integer Stopping line for the search (end-exclusive)
---
----@return (fun(): integer, TSNode, TSMetadata): capture id, capture node, metadata
+---@return (fun(end_line: integer|nil): integer, TSNode, TSMetadata):
+--- capture id, capture node, metadata
function Query:iter_captures(node, source, start, stop)
if type(source) == 'number' and source == 0 then
source = api.nvim_get_current_buf()
@@ -716,7 +722,7 @@ function Query:iter_captures(node, source, start, stop)
start, stop = value_or_node_range(start, stop, node)
local raw_iter = node:_rawquery(self.query, true, start, stop)
- local function iter()
+ local function iter(end_line)
local capture, captured_node, match = raw_iter()
local metadata = {}
@@ -724,7 +730,10 @@ function Query:iter_captures(node, source, start, stop)
local active = self:match_preds(match, match.pattern, source)
match.active = active
if not active then
- return iter() -- tail call: try next match
+ if end_line and captured_node:range() > end_line then
+ return nil, captured_node, nil
+ end
+ return iter(end_line) -- tail call: try next match
end
self:apply_directives(match, match.pattern, source, metadata)
@@ -743,7 +752,8 @@ end
--- If the query has more than one pattern, the capture table might be sparse
--- and e.g. `pairs()` method should be used over `ipairs`.
--- Here is an example iterating over all captures in every match:
---- <pre>lua
+---
+--- ```lua
--- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
--- for id, node in pairs(match) do
--- local name = query.captures[id]
@@ -754,7 +764,7 @@ end
--- -- ... use the info here ...
--- end
--- end
---- </pre>
+--- ```
---
---@param node TSNode under which the search will occur
---@param source (integer|string) Source buffer or string to search
@@ -824,11 +834,24 @@ end
--- Omnifunc for completing node names and predicates in treesitter queries.
---
--- Use via
---- <pre>lua
---- vim.bo.omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
---- </pre>
+---
+--- ```lua
+--- vim.bo.omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
+--- ```
+---
function M.omnifunc(findstart, base)
return require('vim.treesitter._query_linter').omnifunc(findstart, base)
end
+--- Open a window for live editing of a treesitter query.
+---
+--- Can also be shown with `:EditQuery`. *:EditQuery*
+---
+--- Note that the editor opens a scratch buffer, and so queries aren't persisted on disk.
+---
+--- @param lang? string language to open the query editor for. If omitted, inferred from the current buffer's filetype.
+function M.edit(lang)
+ require('vim.treesitter.dev').edit_query(lang)
+end
+
return M
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index cd90886489..b6ddf337ce 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -3,6 +3,23 @@ local M = {}
--- Prompts the user to pick from a list of items, allowing arbitrary (potentially asynchronous)
--- work until `on_choice`.
---
+--- Example:
+---
+--- ```lua
+--- vim.ui.select({ 'tabs', 'spaces' }, {
+--- prompt = 'Select tabs or spaces:',
+--- format_item = function(item)
+--- return "I'd like to choose " .. item
+--- end,
+--- }, function(choice)
+--- if choice == 'spaces' then
+--- vim.o.expandtab = true
+--- else
+--- vim.o.expandtab = false
+--- end
+--- end)
+--- ```
+---
---@param items table Arbitrary items
---@param opts table Additional options
--- - prompt (string|nil)
@@ -19,23 +36,6 @@ local M = {}
--- Called once the user made a choice.
--- `idx` is the 1-based index of `item` within `items`.
--- `nil` if the user aborted the dialog.
----
----
---- Example:
---- <pre>lua
---- vim.ui.select({ 'tabs', 'spaces' }, {
---- prompt = 'Select tabs or spaces:',
---- format_item = function(item)
---- return "I'd like to choose " .. item
---- end,
---- }, function(choice)
---- if choice == 'spaces' then
---- vim.o.expandtab = true
---- else
---- vim.o.expandtab = false
---- end
---- end)
---- </pre>
function M.select(items, opts, on_choice)
vim.validate({
items = { items, 'table', false },
@@ -58,6 +58,14 @@ end
--- Prompts the user for input, allowing arbitrary (potentially asynchronous) work until
--- `on_confirm`.
---
+--- Example:
+---
+--- ```lua
+--- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
+--- vim.o.shiftwidth = tonumber(input)
+--- end)
+--- ```
+---
---@param opts table Additional options. See |input()|
--- - prompt (string|nil)
--- Text of the prompt
@@ -77,13 +85,6 @@ end
--- `input` is what the user typed (it might be
--- an empty string if nothing was entered), or
--- `nil` if the user aborted the dialog.
----
---- Example:
---- <pre>lua
---- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
---- vim.o.shiftwidth = tonumber(input)
---- end)
---- </pre>
function M.input(opts, on_confirm)
vim.validate({
on_confirm = { on_confirm, 'function', false },
@@ -110,11 +111,12 @@ end
--- Expands "~/" and environment variables in filesystem paths.
---
--- Examples:
---- <pre>lua
+---
+--- ```lua
--- vim.ui.open("https://neovim.io/")
--- vim.ui.open("~/path/to/file")
--- vim.ui.open("$VIMRUNTIME")
---- </pre>
+--- ```
---
---@param path string Path or URL to open
---
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index 056e1678ff..306eef90d3 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -5,12 +5,13 @@
--- available tools and dependencies on the current system.
---
--- Example:
---- <pre>lua
---- local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
---- if vim.version.gt(v, {3, 2, 0}) then
---- -- ...
---- end
---- </pre>
+---
+--- ```lua
+--- local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
+--- if vim.version.gt(v, {3, 2, 0}) then
+--- -- ...
+--- end
+--- ```
---
--- \*vim.version()\* returns the version of the current Nvim process.
---
@@ -21,35 +22,36 @@
---
--- Supported range specs are shown in the following table.
--- Note: suffixed versions (1.2.3-rc1) are not matched.
---- <pre>
---- 1.2.3 is 1.2.3
---- =1.2.3 is 1.2.3
---- >1.2.3 greater than 1.2.3
---- <1.2.3 before 1.2.3
---- >=1.2.3 at least 1.2.3
---- ~1.2.3 is >=1.2.3 <1.3.0 "reasonably close to 1.2.3"
---- ^1.2.3 is >=1.2.3 <2.0.0 "compatible with 1.2.3"
---- ^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
---- ^0.0.1 is =0.0.1 (0.0.x is special)
---- ^1.2 is >=1.2.0 <2.0.0 (like ^1.2.0)
---- ~1.2 is >=1.2.0 <1.3.0 (like ~1.2.0)
---- ^1 is >=1.0.0 <2.0.0 "compatible with 1"
---- ~1 same "reasonably close to 1"
---- 1.x same
---- 1.* same
---- 1 same
---- * any version
---- x same
---
---- 1.2.3 - 2.3.4 is >=1.2.3 <=2.3.4
+--- ```
+--- 1.2.3 is 1.2.3
+--- =1.2.3 is 1.2.3
+--- >1.2.3 greater than 1.2.3
+--- <1.2.3 before 1.2.3
+--- >=1.2.3 at least 1.2.3
+--- ~1.2.3 is >=1.2.3 <1.3.0 "reasonably close to 1.2.3"
+--- ^1.2.3 is >=1.2.3 <2.0.0 "compatible with 1.2.3"
+--- ^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
+--- ^0.0.1 is =0.0.1 (0.0.x is special)
+--- ^1.2 is >=1.2.0 <2.0.0 (like ^1.2.0)
+--- ~1.2 is >=1.2.0 <1.3.0 (like ~1.2.0)
+--- ^1 is >=1.0.0 <2.0.0 "compatible with 1"
+--- ~1 same "reasonably close to 1"
+--- 1.x same
+--- 1.* same
+--- 1 same
+--- * any version
+--- x same
+---
+--- 1.2.3 - 2.3.4 is >=1.2.3 <=2.3.4
---
---- Partial right: missing pieces treated as x (2.3 => 2.3.x).
---- 1.2.3 - 2.3 is >=1.2.3 <2.4.0
---- 1.2.3 - 2 is >=1.2.3 <3.0.0
+--- Partial right: missing pieces treated as x (2.3 => 2.3.x).
+--- 1.2.3 - 2.3 is >=1.2.3 <2.4.0
+--- 1.2.3 - 2 is >=1.2.3 <3.0.0
---
---- Partial left: missing pieces treated as 0 (1.2 => 1.2.0).
---- 1.2 - 2.3.0 is 1.2.0 - 2.3.0
---- </pre>
+--- Partial left: missing pieces treated as 0 (1.2 => 1.2.0).
+--- 1.2 - 2.3.0 is 1.2.0 - 2.3.0
+--- ```
local M = {}
@@ -237,29 +239,32 @@ function VersionRange:has(version)
end
--- Parses a semver |version-range| "spec" and returns a range object:
---- <pre>
---- {
---- from: Version
---- to: Version
---- has(v: string|Version)
---- }
---- </pre>
+---
+--- ```
+--- {
+--- from: Version
+--- to: Version
+--- has(v: string|Version)
+--- }
+--- ```
---
--- `:has()` checks if a version is in the range (inclusive `from`, exclusive `to`).
---
--- Example:
---- <pre>lua
---- local r = vim.version.range('1.0.0 - 2.0.0')
---- print(r:has('1.9.9')) -- true
---- print(r:has('2.0.0')) -- false
---- print(r:has(vim.version())) -- check against current Nvim version
---- </pre>
+---
+--- ```lua
+--- local r = vim.version.range('1.0.0 - 2.0.0')
+--- print(r:has('1.9.9')) -- true
+--- print(r:has('2.0.0')) -- false
+--- print(r:has(vim.version())) -- check against current Nvim version
+--- ```
---
--- Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly:
---- <pre>lua
---- local r = vim.version.range('1.0.0 - 2.0.0')
---- print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to))
---- </pre>
+---
+--- ```lua
+--- local r = vim.version.range('1.0.0 - 2.0.0')
+--- print(vim.version.gt({1,0,3}, r.from) and vim.version.lt({1,0,3}, r.to))
+--- ```
---
--- @see # https://github.com/npm/node-semver#ranges
---
@@ -345,16 +350,17 @@ end
--- specified literally as a `{major, minor, patch}` tuple, e.g. `{1, 0, 3}`).
---
--- Example:
---- <pre>lua
---- if vim.version.cmp({1,0,3}, {0,2,1}) == 0 then
---- -- ...
---- end
---- local v1 = vim.version.parse('1.0.3-pre')
---- local v2 = vim.version.parse('0.2.1')
---- if vim.version.cmp(v1, v2) == 0 then
---- -- ...
---- end
---- </pre>
+---
+--- ```lua
+--- if vim.version.cmp({1,0,3}, {0,2,1}) == 0 then
+--- -- ...
+--- end
+--- local v1 = vim.version.parse('1.0.3-pre')
+--- local v2 = vim.version.parse('0.2.1')
+--- if vim.version.cmp(v1, v2) == 0 then
+--- -- ...
+--- end
+--- ```
---
--- @note Per semver, build metadata is ignored when comparing two otherwise-equivalent versions.
---
@@ -399,9 +405,10 @@ end
--- Parses a semantic version string and returns a version object which can be used with other
--- `vim.version` functions. For example "1.0.1-rc1+build.2" returns:
---- <pre>
---- { major = 1, minor = 0, patch = 1, prerelease = "rc1", build = "build.2" }
---- </pre>
+---
+--- ```
+--- { major = 1, minor = 0, patch = 1, prerelease = "rc1", build = "build.2" }
+--- ```
---
--- @see # https://semver.org/spec/v2.0.0.html
---
diff --git a/runtime/plugin/nvim.lua b/runtime/plugin/nvim.lua
index 2ddccfcff6..743d3044b6 100644
--- a/runtime/plugin/nvim.lua
+++ b/runtime/plugin/nvim.lua
@@ -19,6 +19,6 @@ vim.api.nvim_create_user_command('InspectTree', function(cmd)
end
end, { desc = 'Inspect treesitter language tree for buffer', count = true })
-vim.api.nvim_create_user_command('PreviewQuery', function()
- vim.treesitter.preview_query()
-end, { desc = 'Preview treesitter query' })
+vim.api.nvim_create_user_command('EditQuery', function(cmd)
+ vim.treesitter.query.edit(cmd.fargs[1])
+end, { desc = 'Edit treesitter query', nargs = '?' })
diff --git a/runtime/syntax/kotlin.vim b/runtime/syntax/kotlin.vim
new file mode 100644
index 0000000000..9b85b8ef5c
--- /dev/null
+++ b/runtime/syntax/kotlin.vim
@@ -0,0 +1,157 @@
+" Vim syntax file
+" Language: Kotlin
+" Maintainer: Alexander Udalov
+" URL: https://github.com/udalov/kotlin-vim
+" Last Change: 30 December 2022
+
+if exists('b:current_syntax')
+ finish
+endif
+
+syn keyword ktStatement break continue return
+syn keyword ktConditional if else when
+syn keyword ktRepeat do for while
+syn keyword ktOperator in is by
+syn keyword ktKeyword get set out super this where
+syn keyword ktException try catch finally throw
+
+syn keyword ktInclude import package
+
+" Generated stdlib class names {{{
+" The following is generated by https://github.com/udalov/kotlin-vim/blob/master/extra/generate-stdlib-class-names.main.kts
+syn keyword ktType AbstractCollection AbstractCoroutineContextElement AbstractCoroutineContextKey AbstractDoubleTimeSource AbstractIterator AbstractList AbstractLongTimeSource
+syn keyword ktType AbstractMap AbstractMutableCollection AbstractMutableList AbstractMutableMap AbstractMutableSet AbstractSet AccessDeniedException Accessor Annotation
+syn keyword ktType AnnotationRetention AnnotationTarget Any Appendable ArithmeticException Array ArrayDeque ArrayList AssertionError Boolean BooleanArray BooleanIterator
+syn keyword ktType BuilderInference Byte ByteArray ByteIterator CName CallsInPlace CancellationException Char CharArray CharCategory CharDirectionality CharIterator CharProgression
+syn keyword ktType CharRange CharSequence CharacterCodingException Charsets ClassCastException Cloneable ClosedFloatingPointRange ClosedRange Collection Comparable
+syn keyword ktType ComparableTimeMark Comparator ConcurrentModificationException ConditionalEffect ContextFunctionTypeParams Continuation ContinuationInterceptor ContractBuilder
+syn keyword ktType CopyActionContext CopyActionResult CoroutineContext DeepRecursiveFunction DeepRecursiveScope Delegates Deprecated DeprecatedSinceKotlin DeprecationLevel
+syn keyword ktType Destructured Double DoubleArray DoubleIterator DslMarker Duration DurationUnit Effect Element EmptyCoroutineContext Entry Enum EnumEntries Error Exception
+syn keyword ktType ExperimentalContracts ExperimentalJsExport ExperimentalMultiplatform ExperimentalObjCName ExperimentalObjCRefinement ExperimentalPathApi ExperimentalStdlibApi
+syn keyword ktType ExperimentalSubclassOptIn ExperimentalTime ExperimentalTypeInference ExperimentalUnsignedTypes ExtensionFunctionType FileAlreadyExistsException
+syn keyword ktType FileSystemException FileTreeWalk FileVisitorBuilder FileWalkDirection Float FloatArray FloatIterator FreezingIsDeprecated Function Function0 Function1 Function10
+syn keyword ktType Function11 Function12 Function13 Function14 Function15 Function16 Function17 Function18 Function19 Function2 Function20 Function21 Function22 Function3 Function4
+syn keyword ktType Function5 Function6 Function7 Function8 Function9 FunctionN Getter Grouping HashMap HashSet HiddenFromObjC HidesFromObjC Ignore IllegalArgumentException
+syn keyword ktType IllegalStateException IndexOutOfBoundsException IndexedValue Int IntArray IntIterator IntProgression IntRange InvocationKind Iterable Iterator JsExport JsName
+syn keyword ktType JvmDefault JvmDefaultWithCompatibility JvmDefaultWithoutCompatibility JvmField JvmInline JvmMultifileClass JvmName JvmOverloads JvmRecord JvmSerializableLambda
+syn keyword ktType JvmStatic JvmSuppressWildcards JvmSynthetic JvmWildcard KAnnotatedElement KCallable KClass KClassifier KDeclarationContainer KFunction KMutableProperty
+syn keyword ktType KMutableProperty0 KMutableProperty1 KMutableProperty2 KParameter KProperty KProperty0 KProperty1 KProperty2 KType KTypeParameter KTypeProjection KVariance
+syn keyword ktType KVisibility Key Kind KotlinNullPointerException KotlinReflectionNotSupportedError KotlinVersion Lazy LazyThreadSafetyMode Level LinkedHashMap LinkedHashSet List
+syn keyword ktType ListIterator Long LongArray LongIterator LongProgression LongRange Map MatchGroup MatchGroupCollection MatchNamedGroupCollection MatchResult Metadata Monotonic
+syn keyword ktType MustBeDocumented MutableCollection MutableEntry MutableIterable MutableIterator MutableList MutableListIterator MutableMap MutableSet NoSuchElementException
+syn keyword ktType NoSuchFileException NoWhenBranchMatchedException NotImplementedError Nothing NullPointerException Number NumberFormatException ObjCName ObservableProperty
+syn keyword ktType OnErrorAction OnErrorResult OpenEndRange OptIn OptionalExpectation OverloadResolutionByLambdaReturnType Pair ParameterName PathWalkOption
+syn keyword ktType PropertyDelegateProvider PublishedApi PurelyImplements Random RandomAccess ReadOnlyProperty ReadWriteProperty RefinesInSwift Regex RegexOption Repeatable
+syn keyword ktType ReplaceWith RequiresOptIn RestrictsSuspension Result Retention Returns ReturnsNotNull RuntimeException Sequence SequenceScope Set Setter SharedImmutable Short
+syn keyword ktType ShortArray ShortIterator ShouldRefineInSwift SimpleEffect SinceKotlin Strictfp String StringBuilder SubclassOptInRequired Suppress Synchronized Target
+syn keyword ktType TestTimeSource ThreadLocal Throwable Throws TimeMark TimeSource TimedValue Transient Triple TypeCastException Typography UByte UByteArray UInt UIntArray
+syn keyword ktType UIntProgression UIntRange ULong ULongArray ULongProgression ULongRange UShort UShortArray UninitializedPropertyAccessException Unit UnsafeVariance
+syn keyword ktType UnsupportedOperationException ValueTimeMark Volatile WithComparableMarks
+" }}}
+
+syn keyword ktModifier annotation companion enum inner abstract final open override sealed vararg dynamic expect actual suspend
+syn keyword ktStructure class object interface typealias fun val var constructor init
+
+syn keyword ktReservedKeyword typeof
+
+syn keyword ktBoolean true false
+syn keyword ktConstant null
+
+syn keyword ktModifier reified external inline noinline crossinline
+
+syn match ktModifier "\v<data>\ze\@=.*<(class|object)>"
+syn match ktModifier "\v<value>\ze\@=.*<class>"
+syn match ktModifier "\v<(tailrec|operator|infix)>\ze\@=.*<fun>"
+syn match ktModifier "\v<const>\ze\@=.*<val>"
+syn match ktModifier "\v<lateinit>\ze\@=.*<var>"
+syn match ktModifier "\v<(internal|private|protected|public)>\ze\@=.*<(class|object|interface|typealias|fun|val|var|constructor|get|set)>"
+
+syn match ktOperator "\v\?:|::|\<\=? | \>\=?|[!=]\=\=?|<as>\??|[-*+/%]\=?|[!&|]"
+
+syn keyword ktTodo TODO FIXME XXX contained
+syn match ktShebang "\v^#!.*$"
+syn match ktLineComment "\v//.*$" contains=ktTodo,@Spell
+syn region ktComment matchgroup=ktCommentMatchGroup start="/\*" end="\*/" contains=ktComment,ktTodo,@Spell
+
+syn region ktDocComment start="/\*\*" end="\*/" contains=ktDocTag,ktTodo,@Spell
+syn match ktDocTag "\v\@(author|constructor|receiver|return|since|suppress)>" contained
+syn match ktDocTag "\v\@(exception|param|property|throws|see|sample)>\s*\S+" contains=ktDocTagParam contained
+syn match ktDocTagParam "\v(\s|\[)\S+" contained
+syn match ktComment "/\*\*/"
+
+syn match ktSpecialCharError "\v\\." contained
+syn match ktSpecialChar "\v\\([tbnr'"$\\]|u\x{4})" contained
+syn region ktString start='"' skip='\\"' end='"' contains=ktSimpleInterpolation,ktComplexInterpolation,ktSpecialChar,ktSpecialCharError,@Spell
+syn region ktString start='"""' end='""""*' contains=ktSimpleInterpolation,ktComplexInterpolation,@Spell
+syn match ktCharacter "\v'[^']*'" contains=ktSpecialChar,ktSpecialCharError
+syn match ktCharacter "\v'\\''" contains=ktSpecialChar
+syn match ktCharacter "\v'[^\\]'"
+
+syn match ktAnnotation "\v(\w)@<!\@[[:alnum:]_.]*(:[[:alnum:]_.]*)?"
+syn match ktLabel "\v\w+\@"
+syn match ktLabel "\v(\w)@<=\@\w+"
+
+syn match ktSimpleInterpolation "\v\$\h\w*" contained
+syn region ktComplexInterpolation matchgroup=ktComplexInterpolationBrace start="\v\$\{" end="\v\}" contains=ALLBUT,ktSimpleInterpolation,ktTodo,ktSpecialCharError,ktSpecialChar,ktDocTag,ktDocTagParam
+
+syn match ktNumber "\v<\d+[_[:digit:]]*(uL?|UL?|[LFf])?"
+syn match ktNumber "\v<0[Xx]\x+[_[:xdigit:]]*(uL?|UL?|L)?"
+syn match ktNumber "\v<0[Bb][01]+[_01]*(uL?|UL?|L)?"
+syn match ktFloat "\v<\d*(\d[eE][-+]?\d+|\.\d+([eE][-+]?\d+)?)[Ff]?"
+
+syn match ktEscapedName "\v`.*`"
+
+syn match ktExclExcl "!!"
+syn match ktArrow "->"
+
+syn region ktFold start="{" end="}" transparent fold
+
+exec "syntax sync ccomment ktComment minlines=10"
+
+hi def link ktStatement Statement
+hi def link ktConditional Conditional
+hi def link ktRepeat Repeat
+hi def link ktOperator Operator
+hi def link ktKeyword Keyword
+hi def link ktException Exception
+hi def link ktReservedKeyword Error
+
+hi def link ktInclude Include
+
+hi def link ktType Type
+hi def link ktModifier StorageClass
+hi def link ktStructure Structure
+hi def link ktTypedef Typedef
+
+hi def link ktBoolean Boolean
+hi def link ktConstant Constant
+
+hi def link ktTodo Todo
+hi def link ktShebang Comment
+hi def link ktLineComment Comment
+hi def link ktComment Comment
+hi def link ktCommentMatchGroup Comment
+hi def link ktDocComment Comment
+hi def link ktDocTag Special
+hi def link ktDocTagParam Identifier
+
+hi def link ktSpecialChar SpecialChar
+hi def link ktSpecialCharError Error
+hi def link ktString String
+hi def link ktCharacter Character
+
+hi def link ktAnnotation Identifier
+hi def link ktLabel Identifier
+
+hi def link ktSimpleInterpolation Identifier
+hi def link ktComplexInterpolationBrace Identifier
+
+hi def link ktNumber Number
+hi def link ktFloat Float
+
+hi def link ktExclExcl Special
+hi def link ktArrow Structure
+
+let b:current_syntax = 'kotlin'
+
+" vim:foldmethod=marker
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index 3653271000..71b196d189 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -3,6 +3,7 @@
Neovim is a very powerful editor that has many commands, too many to explain in
a tutorial such as this. This tutorial is designed to describe enough of the
commands that you will be able to easily use Neovim as an all-purpose editor.
+
It is IMPORTANT to remember that this tutorial is set up to teach by use. That
means that you need to do the exercises to learn them properly. If you only
read the text, you will soon forget what is most important!
@@ -19,17 +20,16 @@ pressing [<Esc>](<Esc>) and then [u](u) will undo the latest change.
This tutorial is interactive, and there are a few things you should know.
- Type [<Enter>](<Enter>) on links [like this](holy-grail ) to open the linked help section.
- Or simply type [K](K) on any word to find its documentation!
-- You can close this help window with `:q`{vim}
-- Sometimes you will be required to modify text like
-
- this here
+- You can close this help window with `:q`{vim} `<Enter>`{normal}
+When there is a ✗ sign at the left, you will be required to modify text.
Once you have done the changes correctly, the ✗ sign at the left will change
to ✓. I imagine you can already see how neat Neovim can be.
+
Other times, you'll be prompted to run a command (I'll explain this later):
-~~~ cmd
- :help <Enter>
-~~~
+
+ `:help`{vim} `<Enter>`{normal}
+
or press a sequence of keys
~~~ normal
<Esc>0f<Space>d3wP$P
@@ -70,19 +70,19 @@ NOTE: The cursor keys should also work. But using hjkl you will be able to
2. Type:
- `:q!`{vim} `<Enter>`{normal}.
+ `:q!`{vim} `<Enter>`{normal}
This quits the editor, DISCARDING any changes you have made.
3. Open Neovim and get back here by executing the command that got you into
this tutorial. That might be:
- :Tutor <Enter>
+ `:Tutor`{vim} `<Enter>`{normal}
4. If you have these steps memorized and are confident, execute steps
1 through 3 to exit and re-enter the editor.
-NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
+NOTE: [:q!](:q) `<Enter>`{normal} discards any changes you made. In a few lessons you
will learn how to save the changes to a file.
5. Move the cursor down to Lesson 1.3.
diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json
index fc23a75dae..8f88a3897d 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor.json
+++ b/runtime/tutor/en/vim-01-beginner.tutor.json
@@ -1,6 +1,5 @@
{
"expect": {
- "25": -1,
"103": "The cow jumped over the moon.",
"125": "There is some text missing from this line.",
"126": "There is some text missing from this line.",
diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor
index c2d5268e3d..3156b94dbb 100644
--- a/runtime/tutor/tutor.tutor
+++ b/runtime/tutor/tutor.tutor
@@ -229,11 +229,11 @@ These elements are specified in separate JSON files like this
~~~ json
{
- "expect": {
- "1": "This is how this line should look.",
- "2": "This is how this line should look.",
- "3": -1
- }
+ "expect": {
+ "1": "This is how this line should look.",
+ "2": "This is how this line should look.",
+ "3": -1
+ }
}
~~~
diff --git a/runtime/tutor/tutor.tutor.json b/runtime/tutor/tutor.tutor.json
index e8628e2f0e..41995b28fa 100644
--- a/runtime/tutor/tutor.tutor.json
+++ b/runtime/tutor/tutor.tutor.json
@@ -1,35 +1,35 @@
{
- "expect": {
- "63": "This is text with **important information**",
- "64": "This is text with **important information**",
- "71": "TODO: Document '&variable'",
- "72": "TODO: Document '&variable'",
- "78": "# This is a level 1 header",
- "79": "# This is a level 1 header",
- "80": "### This is a level 3 header",
- "81": "### This is a level 3 header",
- "82": "# This is a header with a label {*label*}",
- "83": "# This is a header with a label {*label*}",
- "108": "A link to help for the ['breakindent']('breakindent') option",
- "109": "A link to help for the ['breakindent']('breakindent') option",
- "123": "A link to the [Links](*links*) section",
- "124": "A link to the [Links](*links*) section",
- "139": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
- "140": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
- "157": "~~~ viml",
- "158": "echom 'the value of &number is'.string(&number)",
- "159": "~~~",
- "161": "~~~ viml",
- "162": "echom 'the value of &number is'.string(&number)",
- "163": "~~~",
- "188": "~~~ normal",
- "189": "d2w",
- "190": "~~~",
- "192": "~~~ normal",
- "193": "d2w",
- "194": "~~~",
- "206": "`d2w`{normal}",
- "207": "`d2w`{normal}",
- "244": -1
- }
+ "expect": {
+ "63": "This is text with **important information**",
+ "64": "This is text with **important information**",
+ "71": "TODO: Document '&variable'",
+ "72": "TODO: Document '&variable'",
+ "78": "# This is a level 1 header",
+ "79": "# This is a level 1 header",
+ "80": "### This is a level 3 header",
+ "81": "### This is a level 3 header",
+ "82": "# This is a header with a label {*label*}",
+ "83": "# This is a header with a label {*label*}",
+ "108": "A link to help for the ['breakindent']('breakindent') option",
+ "109": "A link to help for the ['breakindent']('breakindent') option",
+ "123": "A link to the [Links](*links*) section",
+ "124": "A link to the [Links](*links*) section",
+ "139": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
+ "140": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
+ "157": "~~~ viml",
+ "158": "echom 'the value of &number is'.string(&number)",
+ "159": "~~~",
+ "161": "~~~ viml",
+ "162": "echom 'the value of &number is'.string(&number)",
+ "163": "~~~",
+ "188": "~~~ normal",
+ "189": "d2w",
+ "190": "~~~",
+ "192": "~~~ normal",
+ "193": "d2w",
+ "194": "~~~",
+ "206": "`d2w`{normal}",
+ "207": "`d2w`{normal}",
+ "244": -1
+ }
}
diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua
index 5d726315bd..1afe3d5f46 100755
--- a/scripts/gen_eval_files.lua
+++ b/scripts/gen_eval_files.lua
@@ -213,17 +213,20 @@ end
--- Convert vimdoc references to markdown literals
--- Convert vimdoc codeblocks to markdown codeblocks
+---
+--- Ensure code blocks have one empty line before the start fence and after the closing fence.
+---
--- @param x string
--- @return string
local function norm_text(x)
return (
x:gsub('|([^ ]+)|', '`%1`')
- :gsub('>lua', '\n```lua')
- :gsub('>vim', '\n```vim')
- :gsub('\n<$', '\n```')
- :gsub('\n<\n', '\n```\n')
- :gsub('%s+>\n', '\n```\n')
- :gsub('\n<%s+\n?', '\n```\n')
+ :gsub('\n*>lua', '\n\n```lua')
+ :gsub('\n*>vim', '\n\n```vim')
+ :gsub('\n+<$', '\n```')
+ :gsub('\n+<\n+', '\n```\n\n')
+ :gsub('%s+>\n+', '\n```\n')
+ :gsub('\n+<%s+\n?', '\n```\n')
)
end
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index d485e68e2f..ed156e1422 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -585,13 +585,12 @@ def render_node(n, text, prefix='', indent='', width=text_width - indentation,
text += '>{}{}\n<'.format(ensure_nl, o)
elif n.nodeName == 'programlisting': # codeblock (```)
o = get_text(n)
- filename = n.attributes['filename'].value
- if filename:
- text += '>{}'.format(filename.lstrip('.'))
- else:
- text += '>'
+ text += '>'
+ if 'filename' in n.attributes:
+ filename = n.attributes['filename'].value
+ text += filename.lstrip('.')
- text += '\n\n{}\n<'.format(textwrap.indent(o, ' ' * 4))
+ text += '\n{}\n<'.format(textwrap.indent(o, ' ' * 4))
elif is_inline(n):
text = doc_wrap(get_text(n), prefix=prefix, indent=indent, width=width)
elif n.nodeName == 'verbatim':
@@ -768,6 +767,27 @@ def para_as_map(parent, indent='', width=text_width - indentation, fmt_vimhelp=F
return chunks, xrefs
+def is_program_listing(para):
+ """
+ Return True if `para` contains a "programlisting" (i.e. a Markdown code
+ block ```).
+
+ Sometimes a <para> element will have only a single "programlisting" child
+ node, but othertimes it will have extra whitespace around the
+ "programlisting" node.
+
+ @param para XML <para> node
+ @return True if <para> is a programlisting
+ """
+
+ # Remove any child text nodes that are only whitespace
+ children = [
+ n for n in para.childNodes
+ if n.nodeType != n.TEXT_NODE or n.data.strip() != ''
+ ]
+
+ return len(children) == 1 and children[0].nodeName == 'programlisting'
+
def fmt_node_as_vimhelp(parent: Element, width=text_width - indentation, indent='',
fmt_vimhelp=False):
"""Renders (nested) Doxygen <para> nodes as Vim :help text.
@@ -799,10 +819,7 @@ def fmt_node_as_vimhelp(parent: Element, width=text_width - indentation, indent=
# 'programlisting' blocks are Markdown code blocks. Do not include
# these as a separate paragraph, but append to the last non-empty line
# in the text
- if (
- len(child.childNodes) == 1
- and child.childNodes[0].nodeName == 'programlisting'
- ):
+ if is_program_listing(child):
while rendered_blocks and rendered_blocks[-1] == '':
rendered_blocks.pop()
rendered_blocks[-1] += ' ' + para['text']
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index aa0c2695ad..2e4d2a622d 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -49,19 +49,20 @@ static int64_t next_autocmd_id = 1;
/// Get all autocommands that match the corresponding {opts}.
///
/// These examples will get autocommands matching ALL the given criteria:
-/// <pre>lua
-/// -- Matches all criteria
-/// autocommands = vim.api.nvim_get_autocmds({
-/// group = "MyGroup",
-/// event = {"BufEnter", "BufWinEnter"},
-/// pattern = {"*.c", "*.h"}
-/// })
///
-/// -- All commands from one group
-/// autocommands = vim.api.nvim_get_autocmds({
-/// group = "MyGroup",
-/// })
-/// </pre>
+/// ```lua
+/// -- Matches all criteria
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// event = {"BufEnter", "BufWinEnter"},
+/// pattern = {"*.c", "*.h"}
+/// })
+///
+/// -- All commands from one group
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// })
+/// ```
///
/// NOTE: When multiple patterns or events are provided, it will find all the autocommands that
/// match any combination of them.
@@ -344,28 +345,31 @@ cleanup:
/// function _name_ string) or `command` (Ex command string).
///
/// Example using Lua callback:
-/// <pre>lua
-/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
-/// pattern = {"*.c", "*.h"},
-/// callback = function(ev)
-/// print(string.format('event fired: \%s', vim.inspect(ev)))
-/// end
-/// })
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// callback = function(ev)
+/// print(string.format('event fired: %s', vim.inspect(ev)))
+/// end
+/// })
+/// ```
///
/// Example using an Ex command as the handler:
-/// <pre>lua
-/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
-/// pattern = {"*.c", "*.h"},
-/// command = "echo 'Entering a C or C++ file'",
-/// })
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// command = "echo 'Entering a C or C++ file'",
+/// })
+/// ```
///
/// Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like "$HOME"
/// and "~" must be expanded explicitly:
-/// <pre>lua
-/// pattern = vim.fn.expand("~") .. "/some/path/*.py"
-/// </pre>
+///
+/// ```lua
+/// pattern = vim.fn.expand("~") .. "/some/path/*.py"
+/// ```
///
/// @param event (string|array) Event(s) that will trigger the handler (`callback` or `command`).
/// @param opts Options dict:
@@ -619,11 +623,12 @@ cleanup:
/// Create or get an autocommand group |autocmd-groups|.
///
/// To get an existing group id, do:
-/// <pre>lua
-/// local id = vim.api.nvim_create_augroup("MyGroup", {
-/// clear = false
-/// })
-/// </pre>
+///
+/// ```lua
+/// local id = vim.api.nvim_create_augroup("MyGroup", {
+/// clear = false
+/// })
+/// ```
///
/// @param name String: The name of the group
/// @param opts Dictionary Parameters
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index baac694848..e8f9f809f2 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -85,11 +85,15 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
///
/// Example (Lua): capture buffer updates in a global `events` variable
/// (use "vim.print(events)" to see its contents):
-/// <pre>lua
-/// events = {}
-/// vim.api.nvim_buf_attach(0, false, {
-/// on_lines=function(...) table.insert(events, {...}) end})
-/// </pre>
+///
+/// ```lua
+/// events = {}
+/// vim.api.nvim_buf_attach(0, false, {
+/// on_lines = function(...)
+/// table.insert(events, {...})
+/// end,
+/// })
+/// ```
///
/// @see |nvim_buf_detach()|
/// @see |api-buffer-updates-lua|
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 2b09cfc4b2..808d4e0b8d 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -860,11 +860,12 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
/// For Lua usage see |lua-guide-commands-create|.
///
/// Example:
-/// <pre>vim
-/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
-/// :SayHello
-/// Hello world!
-/// </pre>
+///
+/// ```vim
+/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
+/// :SayHello
+/// Hello world!
+/// ```
///
/// @param name Name of the new user command. Must begin with an uppercase letter.
/// @param command Replacement command to execute when this user command is executed. When called
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index b76a275c0d..faab6e593c 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -300,10 +300,11 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
/// respectively, thus the following are equivalent:
-/// <pre>lua
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+/// ```
///
/// If `end` is less than `start`, traversal works backwards. (Useful
/// with `limit`, to get the first marks prior to a given position.)
@@ -313,20 +314,21 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// of an extmark will be considered.
///
/// Example:
-/// <pre>lua
-/// local api = vim.api
-/// local pos = api.nvim_win_get_cursor(0)
-/// local ns = api.nvim_create_namespace('my-plugin')
-/// -- Create new extmark at line 1, column 1.
-/// local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
-/// -- Create new extmark at line 3, column 1.
-/// local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
-/// -- Get extmarks only from line 3.
-/// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
-/// -- Get all marks in this buffer + namespace.
-/// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
-/// vim.print(ms)
-/// </pre>
+///
+/// ```lua
+/// local api = vim.api
+/// local pos = api.nvim_win_get_cursor(0)
+/// local ns = api.nvim_create_namespace('my-plugin')
+/// -- Create new extmark at line 1, column 1.
+/// local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+/// -- Create new extmark at line 3, column 1.
+/// local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+/// -- Get extmarks only from line 3.
+/// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+/// -- Get all marks in this buffer + namespace.
+/// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+/// vim.print(ms)
+/// ```
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace id from |nvim_create_namespace()| or -1 for all namespaces
@@ -1204,7 +1206,9 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
- *width = w;
+ if (width != NULL) {
+ *width = w;
+ }
return virt_text;
free_exit:
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index da10ab5bd4..916409b973 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -212,10 +212,11 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
/// nvim_feedkeys().
///
/// Example:
-/// <pre>vim
-/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
-/// :call nvim_feedkeys(key, 'n', v:false)
-/// </pre>
+///
+/// ```vim
+/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
+/// :call nvim_feedkeys(key, 'n', v:false)
+/// ```
///
/// @param keys to be typed
/// @param mode behavior flags, see |feedkeys()|
@@ -1280,10 +1281,11 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
/// "#rrggbb" hexadecimal string.
///
/// Example:
-/// <pre>vim
-/// :echo nvim_get_color_by_name("Pink")
-/// :echo nvim_get_color_by_name("#cbcbcb")
-/// </pre>
+///
+/// ```vim
+/// :echo nvim_get_color_by_name("Pink")
+/// :echo nvim_get_color_by_name("#cbcbcb")
+/// ```
///
/// @param name Color name or "#rrggbb" string
/// @return 24-bit RGB value, or -1 for invalid argument.
@@ -1420,14 +1422,16 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
///
/// Example:
-/// <pre>vim
-/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
-/// </pre>
+///
+/// ```vim
+/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+/// ```
///
/// is equivalent to:
-/// <pre>vim
-/// nmap <nowait> <Space><NL> <Nop>
-/// </pre>
+///
+/// ```vim
+/// nmap <nowait> <Space><NL> <Nop>
+/// ```
///
/// @param channel_id
/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 74c5228329..9bb7f14a72 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -56,16 +56,19 @@
/// this should not be used to specify arbitrary WM screen positions.
///
/// Example (Lua): window-relative float
-/// <pre>lua
-/// vim.api.nvim_open_win(0, false,
-/// {relative='win', row=3, col=3, width=12, height=3})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_open_win(0, false,
+/// {relative='win', row=3, col=3, width=12, height=3})
+/// ```
///
/// Example (Lua): buffer-relative float (travels as buffer is scrolled)
-/// <pre>lua
-/// vim.api.nvim_open_win(0, false,
-/// {relative='win', width=12, height=3, bufpos={100,10}})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_open_win(0, false,
+/// {relative='win', width=12, height=3, bufpos={100,10}})
+/// })
+/// ```
///
/// @param buffer Buffer to display, or 0 for current buffer
/// @param enter Enter the window (make it the current window)
@@ -192,7 +195,8 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
}
// autocmds in win_enter or win_set_buf below may close the window
if (win_valid(wp) && buffer > 0) {
- win_set_buf(wp, buf, fconfig.noautocmd, err);
+ Boolean noautocmd = !enter || fconfig.noautocmd;
+ win_set_buf(wp, buf, noautocmd, err);
}
if (!win_valid(wp)) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 10e6cde280..c09de30bc1 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1243,6 +1243,8 @@ struct window_S {
// this window, w_allbuf_opt is for all buffers in this window.
winopt_T w_onebuf_opt;
winopt_T w_allbuf_opt;
+ // 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.
uint32_t w_p_stl_flags; // flags for 'statusline'
@@ -1258,9 +1260,6 @@ struct window_S {
int w_briopt_list; // additional indent for lists
int w_briopt_vcol; // indent for specific column
- // transform a pointer to a "onebuf" option into a "allbuf" option
-#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
-
long w_scbind_pos;
ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary.
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 265bc11661..f4ca31040a 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -268,6 +268,28 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
kv_A(state->active, index) = range;
}
+/// Initialize the draw_col of a newly-added non-inline virtual text item.
+static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item)
+{
+ if (win_col < 0) {
+ item->draw_col = win_col;
+ } else if (item->decor.virt_text_pos == kVTOverlay) {
+ item->draw_col = (item->decor.virt_text_hide && hidden) ? INT_MIN : win_col;
+ } else {
+ item->draw_col = -1;
+ }
+}
+
+void decor_recheck_draw_col(int win_col, bool hidden, DecorState *state)
+{
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange *item = &kv_A(state->active, i);
+ if (item->draw_col == -3) {
+ decor_init_draw_col(win_col, hidden, item);
+ }
+ }
+}
+
int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
{
buf_T *buf = wp->w_buffer;
@@ -348,19 +370,10 @@ next_mark:
if (active && item.decor.spell != kNone) {
spell = item.decor.spell;
}
- if (item.start_row == state->row && decor_virt_pos(&item.decor)
- && item.draw_col != INT_MIN) {
- if (item.start_col <= col) {
- if (item.decor.virt_text_pos == kVTOverlay && item.draw_col == -1) {
- item.draw_col = (item.decor.virt_text_hide && hidden) ? INT_MIN : win_col;
- } else if (item.draw_col == -3) {
- item.draw_col = -1;
- }
- } else if (wp->w_p_wrap
- && (item.decor.virt_text_pos == kVTRightAlign
- || item.decor.virt_text_pos == kVTWinCol)) {
- item.draw_col = -3;
- }
+ if (item.start_row == state->row && item.start_col <= col
+ && decor_virt_pos(&item.decor) && item.draw_col == -1
+ && item.decor.virt_text_pos != kVTInline) {
+ decor_init_draw_col(win_col, hidden, &item);
}
if (keep) {
kv_A(state->active, j++) = item;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 3d16aa803e..0f191aa870 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -84,7 +84,7 @@ typedef struct {
bool virt_text_owned;
/// Screen column to draw the virtual text.
/// When -1, the virtual text may be drawn after deciding where.
- /// When -3, the virtual text should be drawn on a later screen line.
+ /// When -3, the virtual text should be drawn on the next screen line.
/// When INT_MIN, the virtual text should no longer be drawn.
int draw_col;
uint64_t ns_id;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 969021ef2c..811cfc1eb2 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -136,8 +136,6 @@ typedef struct {
///< or w_skipcol or concealing
int skipped_cells; ///< nr of skipped cells for virtual text
///< to be added to wlv.vcol later
- bool more_virt_inline_chunks; ///< indicates if there is more inline virtual text
- ///< after n_extra
} winlinevars_T;
/// for line_putchar. Contains the state that needs to be remembered from
@@ -868,10 +866,12 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv)
}
}
-/// Checks if there is more inline virtual text that need to be drawn
-/// and sets has_more_virt_inline_chunks to reflect that.
+/// Checks if there is more inline virtual text that need to be drawn.
static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v)
{
+ if (wlv->virt_inline_i < kv_size(wlv->virt_inline)) {
+ return true;
+ }
DecorState *state = &decor_state;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange *item = &kv_A(state->active, i);
@@ -911,7 +911,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
break;
}
}
- wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v);
if (!kv_size(wlv->virt_inline)) {
// no more inline virtual text here
break;
@@ -929,11 +928,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
wlv->c_final = NUL;
wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
wlv->n_attr = mb_charlen(vtc.text);
-
- // Checks if there is more inline virtual text chunks that need to be drawn.
- wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v)
- || wlv->virt_inline_i < kv_size(wlv->virt_inline);
-
// If the text didn't reach until the first window
// column we need to skip cells.
if (wlv->skip_cells > 0) {
@@ -1147,8 +1141,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
bool saved_search_attr_from_match = false;
int win_col_offset = 0; // offset for window columns
+ bool area_active = false; // whether in Visual selection, for virtual text
+ bool decor_need_recheck = false; // call decor_recheck_draw_col() at next char
char buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
+ VirtText fold_vt = VIRTTEXT_EMPTY;
// 'cursorlineopt' has "screenline" and cursor is in this line
bool cul_screenline = false;
@@ -1788,9 +1785,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
if (has_decor && wlv.n_extra == 0) {
- bool selected = (area_highlighting
- && ((wlv.vcol >= wlv.fromcol && wlv.vcol < wlv.tocol)
- || (noinvcur && wlv.vcol == wp->w_virtcol)));
+ // Duplicate the Visual area check after this block,
+ // but don't check inside p_extra here.
+ if (wlv.vcol == wlv.fromcol
+ || (wlv.vcol + 1 == wlv.fromcol
+ && (wlv.n_extra == 0 && utf_ptr2cells(ptr) > 1))
+ || (vcol_prev == fromcol_prev
+ && vcol_prev < wlv.vcol
+ && wlv.vcol < wlv.tocol)) {
+ area_active = true;
+ } else if (area_active
+ && (wlv.vcol == wlv.tocol
+ || (noinvcur && wlv.vcol == wp->w_virtcol))) {
+ area_active = false;
+ }
+
+ bool selected = (area_active || (area_highlighting && noinvcur
+ && wlv.vcol == wp->w_virtcol));
+ if (decor_need_recheck) {
+ decor_recheck_draw_col(wlv.off, selected, &decor_state);
+ decor_need_recheck = false;
+ }
extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state);
if (!has_fold && wp->w_buffer->b_virt_text_inline > 0) {
@@ -1824,10 +1839,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& vcol_prev < wlv.vcol // not at margin
&& wlv.vcol < wlv.tocol)) {
*area_attr_p = vi_attr; // start highlighting
+ area_active = true;
} else if (*area_attr_p != 0
&& (wlv.vcol == wlv.tocol
|| (noinvcur && wlv.vcol == wp->w_virtcol))) {
*area_attr_p = 0; // stop highlighting
+ area_active = false;
}
if (!has_fold && wlv.n_extra == 0) {
@@ -1900,7 +1917,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (draw_folded && wlv.n_extra == 0 && wlv.col == win_col_offset) {
linenr_T lnume = lnum + foldinfo.fi_lines - 1;
memset(buf_fold, ' ', FOLD_TEXT_LEN);
- wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold, &fold_vt);
wlv.n_extra = (int)strlen(wlv.p_extra);
if (wlv.p_extra != buf_fold) {
@@ -2865,6 +2882,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
}
+ if (kv_size(fold_vt) > 0) {
+ draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0);
+ }
draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row);
grid_put_linebuf(grid, wlv.row, 0, wlv.col, grid->cols, wp->w_p_rl, wp, bg_attr, false);
wlv.row++;
@@ -2890,15 +2910,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wlv.filler_todo <= 0
&& (wp->w_p_rl ? wlv.col == 0 : wlv.col == grid->cols - 1)
&& !has_fold) {
- if (*ptr == NUL && lcs_eol_one == 0 && has_decor) {
+ if (has_decor && *ptr == NUL && lcs_eol_one == 0) {
// Tricky: there might be a virtual text just _after_ the last char
decor_redraw_col(wp, (colnr_T)v, wlv.off, false, &decor_state);
- handle_inline_virtual_text(wp, &wlv, v);
}
if (*ptr != NUL
|| lcs_eol_one > 0
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
- || wlv.more_virt_inline_chunks) {
+ || has_more_inline_virt(&wlv, v)) {
c = wp->w_p_lcs_chars.ext;
wlv.char_attr = win_hl_attr(wp, HLF_AT);
mb_c = c;
@@ -3085,13 +3104,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.char_attr = saved_attr2;
}
- if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols)) && has_decor) {
+ if (has_decor && wlv.filler_todo <= 0
+ && (wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))) {
// At the end of screen line: might need to peek for decorations just after
- // this position. Without wrapping, we might need to display win_pos overlays
- // from the entire text line.
- colnr_T nextpos = wp->w_p_wrap ? (colnr_T)(ptr - line) : (colnr_T)strlen(line);
- decor_redraw_col(wp, nextpos, wlv.off, true, &decor_state);
- handle_inline_virtual_text(wp, &wlv, v);
+ // this position.
+ if (!has_fold && wp->w_p_wrap && wlv.n_extra == 0) {
+ decor_redraw_col(wp, (int)(ptr - line), -3, false, &decor_state);
+ // Check position/hiding of virtual text again on next screen line.
+ decor_need_recheck = true;
+ } else if (has_fold || !wp->w_p_wrap) {
+ // Without wrapping, we might need to display right_align and win_col
+ // virt_text for the entire text line.
+ decor_redraw_col(wp, MAXCOL, -1, true, &decor_state);
+ }
}
// At end of screen line and there is more to come: Display the line
@@ -3104,7 +3129,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& wlv.p_extra != at_end_str)
|| (wlv.n_extra != 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
- || wlv.more_virt_inline_chunks)) {
+ || has_more_inline_virt(&wlv, v))) {
bool wrap = wp->w_p_wrap // Wrapping enabled.
&& wlv.filler_todo <= 0 // Not drawing diff filler lines.
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character.
@@ -3117,7 +3142,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (virt_line_offset >= 0) {
draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line,
kHlModeReplace, grid->cols, 0);
- } else {
+ } else if (wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, wlv.row);
}
@@ -3186,6 +3211,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
} // for every character in the line
+ clear_virttext(&fold_vt);
kv_destroy(virt_lines);
xfree(wlv.p_extra_free);
xfree(wlv.saved_p_extra_free);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 06eb81be92..216f8a67db 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4429,9 +4429,8 @@ static bool ins_tab(void)
}
}
if (!(State & VREPLACE_FLAG)) {
- extmark_splice_cols(curbuf, (int)fpos.lnum - 1, change_col,
- cursor->col - change_col, fpos.col - change_col,
- kExtmarkUndo);
+ inserted_bytes(fpos.lnum, change_col,
+ cursor->col - change_col, fpos.col - change_col);
}
}
cursor->col -= i;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9be4cea059..21c7cdee7d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -12,7 +12,9 @@
#include <string.h>
#include "auto/config.h"
+#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@@ -947,38 +949,42 @@ int skip_expr(char **pp, evalarg_T *const evalarg)
return res;
}
+/// Convert "tv" to a string.
+///
+/// @param convert when true convert a List into a sequence of lines.
+///
+/// @return an allocated string.
+static char *typval2string(typval_T *tv, bool convert)
+{
+ if (convert && tv->v_type == VAR_LIST) {
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ if (tv->vval.v_list != NULL) {
+ tv_list_join(&ga, tv->vval.v_list, "\n");
+ if (tv_list_len(tv->vval.v_list) > 0) {
+ ga_append(&ga, NL);
+ }
+ }
+ ga_append(&ga, NUL);
+ return (char *)ga.ga_data;
+ }
+ return xstrdup(tv_get_string(tv));
+}
+
/// Top level evaluation function, returning a string.
///
-/// @param convert when true convert a List into a sequence of lines and convert
-/// a Float to a String.
+/// @param convert when true convert a List into a sequence of lines.
///
-/// @return pointer to allocated memory, or NULL for failure.
+/// @return pointer to allocated memory, or NULL for failure.
char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
- garray_T ga;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
- if (convert && tv.v_type == VAR_LIST) {
- ga_init(&ga, (int)sizeof(char), 80);
- if (tv.vval.v_list != NULL) {
- tv_list_join(&ga, tv.vval.v_list, "\n");
- if (tv_list_len(tv.vval.v_list) > 0) {
- ga_append(&ga, NL);
- }
- }
- ga_append(&ga, NUL);
- retval = (char *)ga.ga_data;
- } else if (convert && tv.v_type == VAR_FLOAT) {
- char numbuf[NUMBUFLEN];
- vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
- retval = xstrdup(numbuf);
- } else {
- retval = xstrdup(tv_get_string(&tv));
- }
+ retval = typval2string(&tv, convert);
tv_clear(&tv);
}
clear_evalarg(&EVALARG_EVALUATE, NULL);
@@ -990,7 +996,7 @@ char *eval_to_string(char *arg, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, int use_sandbox)
+char *eval_to_string_safe(char *arg, const bool use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -1263,11 +1269,13 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
/// it in "*cp". Doesn't give error messages.
-int eval_foldexpr(char *arg, int *cp)
+int eval_foldexpr(win_T *wp, int *cp)
{
- typval_T tv;
- varnumber_T retval;
- int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL);
+ const sctx_T saved_sctx = current_sctx;
+ const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL);
+
+ char *arg = wp->w_p_fde;
+ current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
emsg_off++;
if (use_sandbox) {
@@ -1275,6 +1283,9 @@ int eval_foldexpr(char *arg, int *cp)
}
textlock++;
*cp = NUL;
+
+ typval_T tv;
+ varnumber_T retval;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
@@ -1294,16 +1305,54 @@ int eval_foldexpr(char *arg, int *cp)
}
tv_clear(&tv);
}
+
emsg_off--;
if (use_sandbox) {
sandbox--;
}
textlock--;
clear_evalarg(&EVALARG_EVALUATE, NULL);
+ current_sctx = saved_sctx;
return (int)retval;
}
+/// Evaluate 'foldtext', returning an Array or a String (NULL_STRING on failure).
+Object eval_foldtext(win_T *wp)
+{
+ const bool use_sandbox = was_set_insecurely(wp, "foldtext", OPT_LOCAL);
+ char *arg = wp->w_p_fdt;
+ funccal_entry_T funccal_entry;
+
+ save_funccal(&funccal_entry);
+ if (use_sandbox) {
+ sandbox++;
+ }
+ textlock++;
+
+ typval_T tv;
+ Object retval;
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ retval = STRING_OBJ(NULL_STRING);
+ } else {
+ if (tv.v_type == VAR_LIST) {
+ retval = vim_to_object(&tv);
+ } else {
+ retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv)));
+ }
+ tv_clear(&tv);
+ }
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+
+ if (use_sandbox) {
+ sandbox--;
+ }
+ textlock--;
+ restore_funccal();
+
+ return retval;
+}
+
/// Get an lvalue
///
/// Lvalue may be
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e6efbe85d8..2137dc07e4 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -2111,7 +2111,11 @@ M.funcs = {
]=],
name = 'execute',
- params = { { 'command', 'any' }, { 'silent', 'boolean' } },
+ params = {
+ { 'command', 'string|string[]' },
+ { 'silent', "''|'silent'|'silent!'" }
+ },
+ returns = 'string',
signature = 'execute({command} [, {silent}])',
},
exepath = {
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index a6cb0b568c..1d5ba49301 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -12,12 +12,14 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/api/extmark.h"
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
@@ -1702,8 +1704,9 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker
/// @return the text for a closed fold
///
/// Otherwise the result is in allocated memory.
-char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char *buf)
- FUNC_ATTR_NONNULL_ARG(1)
+char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char *buf,
+ VirtText *vt)
+ FUNC_ATTR_NONNULL_ALL
{
char *text = NULL;
// an error occurred when evaluating 'fdt' setting
@@ -1750,8 +1753,22 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
current_sctx = wp->w_p_script_ctx[WV_FDT].script_ctx;
emsg_off++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt,
- was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+
+ Object obj = eval_foldtext(wp);
+ if (obj.type == kObjectTypeArray) {
+ Error err = ERROR_INIT;
+ *vt = parse_virt_text(obj.data.array, &err, NULL);
+ if (!ERROR_SET(&err)) {
+ *buf = NUL;
+ text = buf;
+ }
+ api_clear_error(&err);
+ } else if (obj.type == kObjectTypeString) {
+ text = obj.data.string.data;
+ obj = NIL;
+ }
+ api_free_object(obj);
+
emsg_off--;
if (text == NULL || did_emsg) {
@@ -2929,7 +2946,7 @@ static void foldlevelExpr(fline_T *flp)
const bool save_keytyped = KeyTyped;
int c;
- const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
+ const int n = eval_foldexpr(flp->wp, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -3320,10 +3337,20 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
foldinfo_T info = fold_info(curwin, lnum);
if (info.fi_lines > 0) {
- char *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
+ VirtText vt = VIRTTEXT_EMPTY;
+ char *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf, &vt);
if (text == buf) {
text = xstrdup(text);
}
+ if (kv_size(vt) > 0) {
+ assert(*text == NUL);
+ for (size_t i = 0; i < kv_size(vt); i++) {
+ char *new_text = concat_str(text, kv_A(vt, i).text);
+ xfree(text);
+ text = new_text;
+ }
+ }
+ clear_virttext(&vt);
rettv->vval.v_string = text;
}
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index b970e752bb..84cf19ba69 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -824,7 +824,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_link = 0;
}
- g->sg_gui = attrs.rgb_ae_attr;
+ g->sg_gui = attrs.rgb_ae_attr &~HL_DEFAULT;
g->sg_rgb_fg = attrs.rgb_fg_color;
g->sg_rgb_bg = attrs.rgb_bg_color;
@@ -851,7 +851,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
}
}
- g->sg_cterm = attrs.cterm_ae_attr;
+ g->sg_cterm = attrs.cterm_ae_attr &~HL_DEFAULT;
g->sg_cterm_bg = attrs.cterm_bg_color;
g->sg_cterm_fg = attrs.cterm_fg_color;
g->sg_cterm_bold = g->sg_cterm & HL_BOLD;
@@ -1441,7 +1441,7 @@ void restore_cterm_colors(void)
/// @param check_link if true also check for an existing link.
///
/// @return true if highlight group "idx" has any settings.
-static int hl_has_settings(int idx, bool check_link)
+static bool hl_has_settings(int idx, bool check_link)
{
return hl_table[idx].sg_cleared == 0
&& (hl_table[idx].sg_attr != 0
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index ab528f8865..35a728314c 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -148,9 +148,7 @@ mapblock_T *get_maphash(int index, buf_T *buf)
/// "mpp" is a pointer to the m_next field of the PREVIOUS entry!
static void mapblock_free(mapblock_T **mpp)
{
- mapblock_T *mp;
-
- mp = *mpp;
+ mapblock_T *mp = *mpp;
xfree(mp->m_keys);
if (!mp->m_simplified) {
NLUA_CLEAR_REF(mp->m_luaref);
@@ -212,8 +210,6 @@ static char *map_mode_to_chars(int mode)
/// @param local true for buffer-local map
static void showmap(mapblock_T *mp, bool local)
{
- size_t len = 1;
-
if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
&& (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
return;
@@ -226,12 +222,10 @@ static void showmap(mapblock_T *mp, bool local)
}
}
- {
- char *const mapchars = map_mode_to_chars(mp->m_mode);
- msg_puts(mapchars);
- len = strlen(mapchars);
- xfree(mapchars);
- }
+ char *const mapchars = map_mode_to_chars(mp->m_mode);
+ msg_puts(mapchars);
+ size_t len = strlen(mapchars);
+ xfree(mapchars);
while (++len <= 3) {
msg_putchar(' ');
@@ -572,33 +566,16 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
/// @param buf Target Buffer
static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
{
- mapblock_T *mp, **mpp;
- const char *p;
- int n;
int retval = 0;
- mapblock_T **abbr_table;
- mapblock_T **map_table;
- int noremap;
- map_table = maphash;
- abbr_table = &first_abbr;
+ // If <buffer> was given, we'll be searching through the buffer's
+ // mappings/abbreviations, not the globals.
+ mapblock_T **map_table = args->buffer ? buf->b_maphash : maphash;
+ mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
// For ":noremap" don't remap, otherwise do remap.
- if (maptype == MAPTYPE_NOREMAP) {
- noremap = REMAP_NONE;
- } else {
- noremap = REMAP_YES;
- }
-
- if (args->buffer) {
- // If <buffer> was given, we'll be searching through the buffer's
- // mappings/abbreviations, not the globals.
- map_table = buf->b_maphash;
- abbr_table = &buf->b_first_abbr;
- }
- if (args->script) {
- noremap = REMAP_SCRIPT;
- }
+ int noremap = args->script ? REMAP_SCRIPT :
+ maptype == MAPTYPE_NOREMAP ? REMAP_NONE : REMAP_YES;
const bool has_lhs = (args->lhs[0] != NUL);
const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
@@ -648,8 +625,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const int first = vim_iswordp(lhs);
int last = first;
- p = lhs + utfc_ptr2len(lhs);
- n = 1;
+ const char *p = lhs + utfc_ptr2len(lhs);
+ int n = 1;
while (p < lhs + len) {
n++; // nr of (multi-byte) chars
last = vim_iswordp(p); // type of last char
@@ -685,6 +662,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
&& maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
+ mapblock_T *mp;
if (is_abbrev) {
if (hash != 0) { // there is only one abbreviation list
break;
@@ -714,6 +692,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
+ mapblock_T *mp;
if (is_abbrev) {
if (hash != 0) { // there is only one abbreviation list
break;
@@ -729,7 +708,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
showmap(mp, true);
did_local = true;
} else {
- n = mp->m_keylen;
+ int n = mp->m_keylen;
if (strncmp(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
showmap(mp, true);
did_local = true;
@@ -759,8 +738,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
hash_end = 256;
}
for (int hash = hash_start; hash < hash_end && !got_int; hash++) {
- mpp = is_abbrev ? abbr_table : &(map_table[hash]);
- for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
+ mapblock_T **mpp = is_abbrev ? abbr_table : &(map_table[hash]);
+ for (mapblock_T *mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
if ((mp->m_mode & mode) == 0) {
// skip entries with wrong mode
mpp = &(mp->m_next);
@@ -772,6 +751,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
did_it = true;
}
} else { // do we have a match?
+ int n;
+ const char *p;
if (round) { // second round: Try unmap "rhs" string
n = (int)strlen(mp->m_str);
p = mp->m_str;
@@ -994,12 +975,10 @@ free_and_return:
/// Get the mapping mode from the command name.
static int get_map_mode(char **cmdp, bool forceit)
{
- char *p;
- int modec;
int mode;
- p = *cmdp;
- modec = (uint8_t)(*p++);
+ char *p = *cmdp;
+ int modec = (uint8_t)(*p++);
if (modec == 'i') {
mode = MODE_INSERT; // :imap
} else if (modec == 'l') {
@@ -1036,16 +1015,13 @@ static int get_map_mode(char **cmdp, bool forceit)
/// This function used to be called map_clear().
static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
{
- int mode;
- int local;
-
- local = (strcmp(arg, "<buffer>") == 0);
+ bool local = strcmp(arg, "<buffer>") == 0;
if (!local && *arg != NUL) {
emsg(_(e_invarg));
return;
}
- mode = get_map_mode(&cmdp, forceit);
+ int mode = get_map_mode(&cmdp, forceit);
map_clear_mode(curbuf, mode, local, abbr);
}
@@ -1057,11 +1033,8 @@ static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
/// @param abbr true for abbreviations
void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
{
- mapblock_T *mp, **mpp;
- int hash;
- int new_hash;
-
- for (hash = 0; hash < 256; hash++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T **mpp;
if (abbr) {
if (hash > 0) { // there is only one abbrlist
break;
@@ -1079,7 +1052,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
}
}
while (*mpp != NULL) {
- mp = *mpp;
+ mapblock_T *mp = *mpp;
if (mp->m_mode & mode) {
mp->m_mode &= ~mode;
if (mp->m_mode == 0) { // entry can be deleted
@@ -1087,7 +1060,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
continue;
}
// May need to put this entry into another hash list.
- new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
+ int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
if (!abbr && new_hash != hash) {
*mpp = mp->m_next;
if (local) {
@@ -1119,7 +1092,6 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
int mode = 0;
- int retval;
char *buf = NULL;
const char *const rhs = replace_termcodes(str, strlen(str), &buf, 0,
@@ -1141,7 +1113,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
MAPMODE(mode, modechars, 'c', MODE_CMDLINE);
#undef MAPMODE
- retval = map_to_exists_mode(rhs, mode, abbr);
+ int retval = map_to_exists_mode(rhs, mode, abbr);
xfree(buf);
return retval;
@@ -1159,13 +1131,12 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
/// @return true if there is at least one mapping with given parameters.
int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
{
- mapblock_T *mp;
- int hash;
bool exp_buffer = false;
// Do it twice: once for global maps and once for local maps.
while (true) {
- for (hash = 0; hash < 256; hash++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // There is only one abbr list.
break;
@@ -1486,10 +1457,7 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat
// Return true if there is an abbreviation, false if not.
bool check_abbr(int c, char *ptr, int col, int mincol)
{
- int scol; // starting column of the abbr.
uint8_t tb[MB_MAXBYTES + 4];
- mapblock_T *mp;
- mapblock_T *mp2;
int clen = 0; // length in characters
if (typebuf.tb_no_abbr_cnt) { // abbrev. are not recursive
@@ -1509,6 +1477,8 @@ bool check_abbr(int c, char *ptr, int col, int mincol)
return false;
}
+ int scol; // starting column of the abbr.
+
{
bool is_id = true;
bool vim_abbr;
@@ -1539,8 +1509,8 @@ bool check_abbr(int c, char *ptr, int col, int mincol)
if (scol < col) { // there is a word in front of the cursor
ptr += scol;
int len = col - scol;
- mp = curbuf->b_first_abbr;
- mp2 = first_abbr;
+ mapblock_T *mp = curbuf->b_first_abbr;
+ mapblock_T *mp2 = first_abbr;
if (mp == NULL) {
mp = mp2;
mp2 = NULL;
@@ -1715,18 +1685,13 @@ char *eval_map_expr(mapblock_T *mp, int c)
/// @param buf buffer for local mappings or NULL
int makemap(FILE *fd, buf_T *buf)
{
- mapblock_T *mp;
- char c1, c2, c3;
- char *p;
- char *cmd;
- int abbr;
- int hash;
bool did_cpo = false;
// Do the loop twice: Once for mappings, once for abbreviations.
// Then loop over all map hash lists.
- for (abbr = 0; abbr < 2; abbr++) {
- for (hash = 0; hash < 256; hash++) {
+ for (int abbr = 0; abbr < 2; abbr++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // there is only one abbr list
break;
@@ -1755,6 +1720,7 @@ int makemap(FILE *fd, buf_T *buf)
if (mp->m_luaref != LUA_NOREF) {
continue;
}
+ char *p;
for (p = mp->m_str; *p != NUL; p++) {
if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA
&& p[2] == KE_SNR) {
@@ -1768,14 +1734,10 @@ int makemap(FILE *fd, buf_T *buf)
// It's possible to create a mapping and then ":unmap" certain
// modes. We recreate this here by mapping the individual
// modes, which requires up to three of them.
- c1 = NUL;
- c2 = NUL;
- c3 = NUL;
- if (abbr) {
- cmd = "abbr";
- } else {
- cmd = "map";
- }
+ char c1 = NUL;
+ char c2 = NUL;
+ char c3 = NUL;
+ char *cmd = abbr ? "abbr" : "map";
switch (mp->m_mode) {
case MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
break;
@@ -1929,7 +1891,6 @@ int makemap(FILE *fd, buf_T *buf)
int put_escstr(FILE *fd, char *strstart, int what)
{
uint8_t *str = (uint8_t *)strstart;
- int c;
// :map xx <Nop>
if (*str == NUL && what == 1) {
@@ -1953,7 +1914,7 @@ int put_escstr(FILE *fd, char *strstart, int what)
continue;
}
- c = *str;
+ int c = *str;
// Special key codes have to be translated to be able to make sense
// when they are read back.
if (c == K_SPECIAL && what != 2) {
@@ -2030,14 +1991,13 @@ int put_escstr(FILE *fd, char *strstart, int what)
char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
int *local_ptr, int *rhs_lua)
{
- int len, minlen;
- mapblock_T *mp;
*rhs_lua = LUA_NOREF;
- len = (int)strlen(keys);
+ int len = (int)strlen(keys);
for (int local = 1; local >= 0; local--) {
// loop over all hash lists
for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // there is only one list.
break;
@@ -2062,7 +2022,7 @@ char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock
s += 3;
keylen -= 3;
}
- minlen = keylen < len ? keylen : len;
+ int minlen = keylen < len ? keylen : len;
if (strncmp(s, keys, (size_t)minlen) == 0) {
if (mp_ptr != NULL) {
*mp_ptr = mp;
@@ -2097,11 +2057,7 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
- if (map_to_exists(name, mode, abbr)) {
- rettv->vval.v_number = true;
- } else {
- rettv->vval.v_number = false;
- }
+ rettv->vval.v_number = map_to_exists(name, mode, abbr);
}
/// Fill a Dictionary with all applicable maparg() like dictionaries
@@ -2439,14 +2395,11 @@ void langmap_init(void)
/// changed at any time!
const char *did_set_langmap(optset_T *args)
{
- char *p;
- char *p2;
- int from, to;
-
- ga_clear(&langmap_mapga); // clear the previous map first
- langmap_init(); // back to one-to-one map
+ ga_clear(&langmap_mapga); // clear the previous map first
+ langmap_init(); // back to one-to-one map
- for (p = p_langmap; p[0] != NUL;) {
+ for (char *p = p_langmap; p[0] != NUL;) {
+ char *p2;
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
MB_PTR_ADV(p2)) {
if (p2[0] == '\\' && p2[1] != NUL) {
@@ -2466,8 +2419,8 @@ const char *did_set_langmap(optset_T *args)
if (p[0] == '\\' && p[1] != NUL) {
p++;
}
- from = utf_ptr2char(p);
- to = NUL;
+ int from = utf_ptr2char(p);
+ int to = NUL;
if (p2 == NULL) {
MB_PTR_ADV(p);
if (p[0] != ',') {
@@ -2524,9 +2477,8 @@ const char *did_set_langmap(optset_T *args)
static void do_exmap(exarg_T *eap, int isabbrev)
{
- int mode;
char *cmdp = eap->cmd;
- mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
+ int mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP
: (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP,
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index d8b8dbba29..627efa9e96 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -1178,6 +1178,9 @@ void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
}
if (internal) {
+ if (key.pos.row == newpos.row && key.pos.col == newpos.col) {
+ return;
+ }
key.pos = newpos;
bool match;
// tricky: could minimize movement in either direction better
@@ -1185,14 +1188,14 @@ void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
if (!match) {
new_i++;
}
- if (new_i == itr->i || key_cmp(key, x->key[new_i]) == 0) {
+ if (new_i == itr->i) {
x->key[itr->i].pos = newpos;
} else if (new_i < itr->i) {
memmove(&x->key[new_i + 1], &x->key[new_i], sizeof(MTKey) * (size_t)(itr->i - new_i));
x->key[new_i] = key;
} else if (new_i > itr->i) {
- memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(MTKey) * (size_t)(new_i - itr->i));
- x->key[new_i] = key;
+ memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(MTKey) * (size_t)(new_i - itr->i - 1));
+ x->key[new_i - 1] = key;
}
return;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ae4e7c1fda..803c5173ea 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1967,6 +1967,10 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
curbuf->b_p_script_ctx[indir & PV_MASK] = last_set;
} else if (indir & PV_WIN) {
curwin->w_p_script_ctx[indir & PV_MASK] = last_set;
+ if (both) {
+ // also setting the "all buffers" value
+ curwin->w_allbuf_opt.wo_script_ctx[indir & PV_MASK] = last_set;
+ }
}
}
}
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index f0666101b8..3221e5b6e9 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -566,7 +566,7 @@ return {
backups if you don't care about losing the file.
Note that environment variables are not expanded. If you want to use
- $HOME you must expand it explicitly, e.g.: >
+ $HOME you must expand it explicitly, e.g.: >vim
:let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
< Note that the default also makes sure that "crontab -e" works (when a
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
index abeb020645..763d30d4a2 100644
--- a/src/nvim/os/pty_process_win.c
+++ b/src/nvim/os/pty_process_win.c
@@ -256,9 +256,9 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
QUEUE_FOREACH(q, &args_q, {
ArgNode *arg_node = QUEUE_DATA(q, ArgNode, node);
xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len);
+ QUEUE_REMOVE(q);
xfree(arg_node->arg);
xfree(arg_node);
- QUEUE_REMOVE(q);
if (!QUEUE_EMPTY(&args_q)) {
xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len);
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 1d0a987780..14c7e56e97 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -59,11 +59,7 @@
#define un_Magic(x) ((x) + 256)
#define is_Magic(x) ((x) < 0)
-// We should define ftpr as a pointer to a function returning a pointer to
-// a function returning a pointer to a function ...
-// This is impossible, so we declare a pointer to a function returning a
-// pointer to a function returning void. This should work for all compilers.
-typedef void (*(*fptr_T)(int *, int))(void);
+typedef void (*fptr_T)(int *, int);
static int no_Magic(int x)
{
@@ -1494,34 +1490,14 @@ static inline char *cstrchr(const char *const s, const int c)
// regsub stuff //
////////////////////////////////////////////////////////////////
-// This stuff below really confuses cc on an SGI -- webb
-
-static fptr_T do_upper(int *d, int c)
+static void do_upper(int *d, int c)
{
*d = mb_toupper(c);
-
- return (fptr_T)NULL;
-}
-
-static fptr_T do_Upper(int *d, int c)
-{
- *d = mb_toupper(c);
-
- return (fptr_T)do_Upper;
}
-static fptr_T do_lower(int *d, int c)
+static void do_lower(int *d, int c)
{
*d = mb_tolower(c);
-
- return (fptr_T)NULL;
-}
-
-static fptr_T do_Lower(int *d, int c)
-{
- *d = mb_tolower(c);
-
- return (fptr_T)do_Lower;
}
/// regtilde(): Replace tildes in the pattern by the old pattern.
@@ -1886,16 +1862,16 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
} else if (vim_strchr("uUlLeE", (uint8_t)(*src))) {
switch (*src++) {
case 'u':
- func_one = (fptr_T)do_upper;
+ func_one = do_upper;
continue;
case 'U':
- func_all = (fptr_T)do_Upper;
+ func_all = do_upper;
continue;
case 'l':
- func_one = (fptr_T)do_lower;
+ func_one = do_lower;
continue;
case 'L':
- func_all = (fptr_T)do_Lower;
+ func_all = do_lower;
continue;
case 'e':
case 'E':
@@ -1954,11 +1930,13 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
} else {
c = utf_ptr2char(src - 1);
}
+
// Write to buffer, if copy is set.
if (func_one != NULL) {
- func_one = (fptr_T)(func_one(&cc, c));
+ func_one(&cc, c);
+ func_one = NULL;
} else if (func_all != NULL) {
- func_all = (fptr_T)(func_all(&cc, c));
+ func_all(&cc, c);
} else {
// just copy
cc = c;
@@ -2061,11 +2039,10 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
c = utf_ptr2char(s);
if (func_one != (fptr_T)NULL) {
- // Turbo C complains without the typecast
- func_one = (fptr_T)(func_one(&cc, c));
+ func_one(&cc, c);
+ func_one = NULL;
} else if (func_all != (fptr_T)NULL) {
- // Turbo C complains without the typecast
- func_all = (fptr_T)(func_all(&cc, c));
+ func_all(&cc, c);
} else { // just copy
cc = c;
}
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 72e21a9130..38e045a08b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1280,7 +1280,7 @@ static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_ln
decor_redraw_line(wp, lnum - 1, &decor_state);
*decor_lnum = lnum;
}
- decor_redraw_col(wp, col, col, false, &decor_state);
+ decor_redraw_col(wp, col, 0, false, &decor_state);
return decor_state.spell;
}
diff --git a/test/README.md b/test/README.md
index 42d0ec0323..3aafe1273e 100644
--- a/test/README.md
+++ b/test/README.md
@@ -102,9 +102,25 @@ Debugging tests
DBG 2022-06-15T18:37:45.229 T57.58016.0 read_cb:118: closing Stream (0x7fd5d700ea18): EOF (end of file)
INF 2022-06-15T18:37:45.229 T57.58016.0 on_process_exit:400: exited: pid=58017 status=0 stoptime=0
```
-- You can set `$GDB` to [run tests under gdbserver](https://github.com/neovim/neovim/pull/1527).
- And if `$VALGRIND` is set it will pass `--vgdb=yes` to valgrind instead of
+- You can set `$GDB` to [run functional tests under gdbserver](https://github.com/neovim/neovim/pull/1527):
+
+ ```sh
+ GDB=1 TEST_FILE=test/functional/api/buffer_spec.lua TEST_FILTER='nvim_buf_set_text works$' make functionaltest
+ ```
+
+ Read more about [filtering tests](#filtering-tests).
+
+ Then, in another terminal:
+
+ ```sh
+ gdb -ex 'target remote localhost:7777' build/bin/nvim
+ ```
+
+ If `$VALGRIND` is also set it will pass `--vgdb=yes` to valgrind instead of
starting gdbserver directly.
+
+ See [test/functional/helpers.lua](https://github.com/neovim/neovim/blob/9cadbf1d36b63f53f0de48c8c5ff6c752ff05d70/test/functional/helpers.lua#L52-L69) for details.
+
- Hanging tests can happen due to unexpected "press-enter" prompts. The
default screen width is 50 columns. Commands that try to print lines longer
than 50 columns in the command-line, e.g. `:edit very...long...path`, will
@@ -124,7 +140,7 @@ Filtering Tests
### Filter by name
-Another filter method is by setting a pattern of test name to `TEST_FILTER` or `TEST_FILTER_OUT`.
+Tests can be filtered by setting a pattern of test name to `TEST_FILTER` or `TEST_FILTER_OUT`.
``` lua
it('foo api',function()
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index a917432dab..c4449bc201 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -208,6 +208,23 @@ describe('API/extmarks', function()
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
end)
+ it('can undo with extmarks (#25147)', function()
+ feed('itest<esc>')
+ set_extmark(ns, 1, 0, 0)
+ set_extmark(ns, 2, 1, 0)
+ eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
+ feed('dd')
+ eq({ { 1, 1, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
+ curbufmeths.clear_namespace(ns, 0, -1)
+ eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
+ set_extmark(ns, 1, 0, 0, { right_gravity = false })
+ set_extmark(ns, 2, 1, 0, { right_gravity = false })
+ eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
+ feed('u')
+ eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
+ curbufmeths.clear_namespace(ns, 0, -1)
+ end)
+
it('querying for information and ranges', function()
--marks = {1, 2, 3}
--positions = {{0, 0,}, {0, 2}, {0, 3}}
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 492fd73223..5d6aaa57e6 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -608,4 +608,21 @@ describe('API: get highlight', function()
meths.set_hl(0, 'Tried', { fg = "#00ff00", default = true })
eq({ fg = tonumber('00ff00', 16), default = true }, meths.get_hl(0, { name = 'Tried' }))
end)
+
+ it('should not output empty gui and cterm #23474', function()
+ meths.set_hl(0, 'Foo', {default = true})
+ meths.set_hl(0, 'Bar', { default = true, fg = '#ffffff' })
+ meths.set_hl(0, 'FooBar', { default = true, fg = '#ffffff', cterm = {bold = true} })
+ meths.set_hl(0, 'FooBarA', { default = true, fg = '#ffffff', cterm = {bold = true,italic = true}})
+
+ eq('Foo xxx cleared',
+ exec_capture('highlight Foo'))
+ eq({default = true}, meths.get_hl(0, {name = 'Foo'}))
+ eq('Bar xxx guifg=#ffffff',
+ exec_capture('highlight Bar'))
+ eq('FooBar xxx cterm=bold guifg=#ffffff',
+ exec_capture('highlight FooBar'))
+ eq('FooBarA xxx cterm=bold,italic guifg=#ffffff',
+ exec_capture('highlight FooBarA'))
+ end)
end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index c19891a794..51e4548edb 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -826,53 +826,53 @@ describe('lua: nvim_buf_attach on_bytes', function()
feed("<esc>u")
check_events {
- { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
- { "test1", "bytes", 1, 8, 0, 0, 0, 0, 4, 4, 0, 0, 0 }
+ { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 9, 0, 0, 0, 0, 4, 4, 0, 0, 0 }
}
-- in REPLACE mode
feed("R<tab><tab>")
check_events {
- { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
- { "test1", "bytes", 1, 10, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
- { "test1", "bytes", 1, 11, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
- { "test1", "bytes", 1, 12, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
- { "test1", "bytes", 1, 13, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
+ { "test1", "bytes", 1, 10, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 11, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 12, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 13, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 14, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
}
feed("<esc>u")
check_events {
- { "test1", "bytes", 1, 14, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
- { "test1", "bytes", 1, 14, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
- { "test1", "bytes", 1, 14, 0, 0, 0, 0, 2, 2, 0, 1, 1 }
+ { "test1", "bytes", 1, 16, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 16, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
+ { "test1", "bytes", 1, 16, 0, 0, 0, 0, 2, 2, 0, 1, 1 }
}
-- in VISUALREPLACE mode
feed("gR<tab><tab>")
check_events {
- { "test1", "bytes", 1, 15, 0, 0, 0, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 16, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 17, 0, 2, 2, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 18, 0, 3, 3, 0, 1, 1, 0, 1, 1 };
- { "test1", "bytes", 1, 19, 0, 3, 3, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 20, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 22, 0, 2, 2, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 23, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 25, 0, 1, 1, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 26, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 28, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
- { "test1", "bytes", 1, 29, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 31, 0, 0, 0, 0, 4, 4, 0, 1, 1 };
+ { "test1", "bytes", 1, 17, 0, 0, 0, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 18, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 19, 0, 2, 2, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 20, 0, 3, 3, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 21, 0, 3, 3, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 22, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 24, 0, 2, 2, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 25, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 27, 0, 1, 1, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 28, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 30, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 31, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 33, 0, 0, 0, 0, 4, 4, 0, 1, 1 };
}
-- inserting tab after other tabs
command("set sw=4")
feed("<esc>0a<tab>")
check_events {
- { "test1", "bytes", 1, 32, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 33, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 34, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 35, 0, 4, 4, 0, 0, 0, 0, 1, 1 };
- { "test1", "bytes", 1, 36, 0, 1, 1, 0, 4, 4, 0, 1, 1 };
+ { "test1", "bytes", 1, 34, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 35, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 36, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 37, 0, 4, 4, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 38, 0, 1, 1, 0, 4, 4, 0, 1, 1 };
}
end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index 2c7b3ff324..6bdb9ed79d 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local uv = require('luv')
local clear = helpers.clear
local exec_lua = helpers.exec_lua
@@ -288,11 +289,12 @@ describe('vim.fs', function()
eq('/', exec_lua [[ return vim.fs.normalize('/') ]])
end)
it('works with ~', function()
- eq( exec_lua([[
- local home = ...
- return home .. '/src/foo'
- ]], is_os('win') and vim.fs.normalize(os.getenv('USERPROFILE')) or os.getenv('HOME')
- ) , exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
+ eq(
+ exec_lua([[
+ local home = ...
+ return home .. '/src/foo'
+ ]], vim.fs.normalize(uv.os_homedir())),
+ exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
end)
it('works with environment variables', function()
local xdg_config_home = test_build_dir .. '/.config'
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 9338e95d10..1dfb9a5e10 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -2257,8 +2257,8 @@ describe('lua stdlib', function()
end)
end) -- vim.opt
- describe('opt_local', function()
- it('should be able to append to an array list type option', function()
+ describe('vim.opt_local', function()
+ it('appends into global value when changing local option value', function()
eq({ "foo,bar,baz,qux" }, exec_lua [[
local result = {}
@@ -2273,6 +2273,19 @@ describe('lua stdlib', function()
end)
end)
+ describe('vim.opt_global', function()
+ it('gets current global option value', function()
+ eq({ "yes" }, exec_lua [[
+ local result = {}
+
+ vim.cmd "setglobal signcolumn=yes"
+ table.insert(result, vim.opt_global.signcolumn:get())
+
+ return result
+ ]])
+ end)
+ end)
+
it('vim.cmd', function()
exec_lua [[
vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')"
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index d5c1a78fc8..815ddbc523 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -9,6 +9,7 @@ local matches = helpers.matches
local write_file = helpers.write_file
local tmpname = helpers.tmpname
local eq = helpers.eq
+local pesc = helpers.pesc
local skip = helpers.skip
local is_ci = helpers.is_ci
@@ -189,7 +190,7 @@ describe(':Man', function()
write_file(actual_file, '')
local args = {nvim_prog, '--headless', '+:Man ' .. actual_file, '+q'}
matches(('Error detected while processing command line:\r\n' ..
- 'man.lua: "no manual entry for %s"'):format(actual_file),
+ 'man.lua: "no manual entry for %s"'):format(pesc(actual_file)),
funcs.system(args, {''}))
os.remove(actual_file)
end)
diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua
index db5dba7374..29361bc0fa 100644
--- a/test/functional/terminal/edit_spec.lua
+++ b/test/functional/terminal/edit_spec.lua
@@ -36,7 +36,6 @@ describe(':edit term://*', function()
end)
it("runs TermOpen early enough to set buffer-local 'scrollback'", function()
- if helpers.skip(helpers.is_os('win')) then return end
local columns, lines = 20, 4
local scr = get_screen(columns, lines)
local rep = 97
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 57dcb14cf8..2d4613dda4 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -1663,21 +1663,18 @@ describe('TUI', function()
it('draws correctly when cursor_address overflows #21643', function()
helpers.skip(helpers.is_os('mac'), 'FIXME: crashes/errors on macOS')
- screen:try_resize(77, 834)
+ screen:try_resize(77, 855)
retry(nil, nil, function()
- eq({true, 831}, {child_session:request('nvim_win_get_height', 0)})
+ eq({true, 852}, {child_session:request('nvim_win_get_height', 0)})
end)
-- Use full screen message so that redrawing afterwards is more deterministic.
child_session:notify('nvim_command', 'intro')
screen:expect({any = 'Nvim'})
-- Going to top-left corner needs 3 bytes.
-- Setting underline attribute needs 9 bytes.
- -- With screen width 77, 63857 characters need 829 full screen lines.
- -- Drawing each full screen line needs 77 + 2 = 79 bytes (2 bytes for CR LF).
- -- The incomplete screen line needs 24 + 3 = 27 bytes.
- -- The whole line needs 3 + 9 + 79 * 829 + 27 = 65530 bytes.
+ -- The whole line needs 3 + 9 + 65515 + 3 = 65530 bytes.
-- The cursor_address that comes after will overflow the 65535-byte buffer.
- local line = ('a'):rep(63857) .. '℃'
+ local line = ('a'):rep(65515) .. '℃'
child_session:notify('nvim_exec_lua', [[
vim.api.nvim_buf_set_lines(0, 0, -1, true, {...})
vim.o.cursorline = true
@@ -1686,8 +1683,8 @@ describe('TUI', function()
feed_data('\n')
screen:expect(
'{13:a}{12:' .. ('a'):rep(76) .. '}|\n'
- .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(828)
- .. '{12:' .. ('a'):rep(24) .. '℃' .. (' '):rep(52) .. '}|\n' .. dedent([[
+ .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(849)
+ .. '{12:' .. ('a'):rep(65) .. '℃' .. (' '):rep(11) .. '}|\n' .. dedent([[
b |
{5:[No Name] [+] }|
|
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 0528370e2a..0aa0cdd6d6 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -85,6 +85,56 @@ void ui_refresh(void)
}
}]]
+local injection_text_c = [[
+int x = INT_MAX;
+#define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
+#define foo void main() { \
+ return 42; \
+ }
+]]
+
+local injection_grid_c = [[
+ int x = INT_MAX; |
+ #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) |
+ #define foo void main() { \ |
+ return 42; \ |
+ } |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+]]
+
+local injection_grid_expected_c = [[
+ {3:int} x = {5:INT_MAX}; |
+ #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) |
+ #define foo {3:void} main() { \ |
+ {4:return} {5:42}; \ |
+ } |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+]]
+
describe('treesitter highlighting (C)', function()
local screen
@@ -411,34 +461,9 @@ describe('treesitter highlighting (C)', function()
end)
it("supports injected languages", function()
- insert([[
- int x = INT_MAX;
- #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
- #define foo void main() { \
- return 42; \
- }
- ]])
+ insert(injection_text_c)
- screen:expect{grid=[[
- int x = INT_MAX; |
- #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) |
- #define foo void main() { \ |
- return 42; \ |
- } |
- ^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
- ]]}
+ screen:expect{grid=injection_grid_c}
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c", {
@@ -448,26 +473,24 @@ describe('treesitter highlighting (C)', function()
test_hl = highlighter.new(parser, {queries = {c = hl_query}})
]]
- screen:expect{grid=[[
- {3:int} x = {5:INT_MAX}; |
- #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) |
- #define foo {3:void} main() { \ |
- {4:return} {5:42}; \ |
- } |
- ^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
- ]]}
+ screen:expect{grid=injection_grid_expected_c}
+ end)
+
+ it("supports injecting by ft name in metadata['injection.language']", function()
+ insert(injection_text_c)
+
+ screen:expect{grid=injection_grid_c}
+
+ exec_lua [[
+ vim.treesitter.language.register("c", "foo")
+ local parser = vim.treesitter.get_parser(0, "c", {
+ injections = {c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "fOO")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "fOO"))'}
+ })
+ local highlighter = vim.treesitter.highlighter
+ test_hl = highlighter.new(parser, {queries = {c = hl_query}})
+ ]]
+
+ screen:expect{grid=injection_grid_expected_c}
end)
it("supports overriding queries, like ", function()
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 4262f7ce77..daa4b4bdb3 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -692,6 +692,11 @@ describe('extmark decorations', function()
[34] = {background = Screen.colors.Yellow};
[35] = {background = Screen.colors.Yellow, bold = true, foreground = Screen.colors.Blue};
[36] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.Red};
+ [37] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
+ [38] = {background = Screen.colors.LightBlue};
+ [39] = {foreground = Screen.colors.Blue1, background = Screen.colors.LightCyan1, bold = true};
+ [40] = {reverse = true};
+ [41] = {bold = true, reverse = true};
}
ns = meths.create_namespace 'test'
@@ -859,6 +864,104 @@ describe('extmark decorations', function()
]]}
end)
+ it('overlay virtual text works with wrapped lines #25158', function()
+ screen:try_resize(50, 6)
+ insert(('ab'):rep(100))
+ for i = 0, 9 do
+ meths.buf_set_extmark(0, ns, 0, 42 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay'})
+ meths.buf_set_extmark(0, ns, 0, 91 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true})
+ end
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {4:89}abababababababababababababababababababa{4:012345678}|
+ {4:9}babababababababababababababababababababababababab|
+ ababababababababababababababababababababababababa^b|
+ {1:~ }|
+ |
+ ]]}
+
+ command('set showbreak=++')
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {1:++}{4:89}abababababababababababababababababababa{4:0123456}|
+ {1:++}{4:789}babababababababababababababababababababababab|
+ {1:++}abababababababababababababababababababababababab|
+ {1:++}ababa^b |
+ |
+ ]]}
+
+ feed('2gkvg0')
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {1:++}{4:89}abababababababababababababababababababa{4:0123456}|
+ {1:++}^a{18:babab}ababababababababababababababababababababab|
+ {1:++}abababababababababababababababababababababababab|
+ {1:++}ababab |
+ {24:-- VISUAL --} |
+ ]]}
+
+ feed('o')
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {1:++}{4:89}abababababababababababababababababababa{4:0123456}|
+ {1:++}{18:ababa}^bababababababababababababababababababababab|
+ {1:++}abababababababababababababababababababababababab|
+ {1:++}ababab |
+ {24:-- VISUAL --} |
+ ]]}
+
+ feed('gk')
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {1:++}{4:89}aba^b{18:ababababababababababababababababababababab}|
+ {1:++}{18:a}{4:89}babababababababababababababababababababababab|
+ {1:++}abababababababababababababababababababababababab|
+ {1:++}ababab |
+ {24:-- VISUAL --} |
+ ]]}
+
+ feed('o')
+ screen:expect{grid=[[
+ ababababababababababababababababababababab{4:01234567}|
+ {1:++}{4:89}aba{18:bababababababababababababababababababababab}|
+ {1:++}^a{4:89}babababababababababababababababababababababab|
+ {1:++}abababababababababababababababababababababababab|
+ {1:++}ababab |
+ {24:-- VISUAL --} |
+ ]]}
+
+ feed('<Esc>$')
+ command('set number showbreak=')
+ screen:expect{grid=[[
+ {2: 1 }ababababababababababababababababababababab{4:0123}|
+ {2: }{4:456789}abababababababababababababababababababa{4:0}|
+ {2: }{4:123456789}babababababababababababababababababab|
+ {2: }ababababababababababababababababababababababab|
+ {2: }abababababababa^b |
+ |
+ ]]}
+
+ command('set cpoptions+=n')
+ screen:expect{grid=[[
+ {2: 1 }ababababababababababababababababababababab{4:0123}|
+ {4:456789}abababababababababababababababababababa{4:01234}|
+ {4:56789}babababababababababababababababababababababab|
+ ababababababababababababababababababababababababab|
+ aba^b |
+ |
+ ]]}
+
+ feed('0g$hi<Tab>')
+ screen:expect{grid=[[
+ {2: 1 }ababababababababababababababababababababab{4:01} |
+ {4:^23456789}abababababababababababababababababababa{4:0}|
+ {4:123456789}babababababababababababababababababababab|
+ ababababababababababababababababababababababababab|
+ abababab |
+ {24:-- INSERT --} |
+ ]]}
+ end)
+
it('virt_text_hide hides overlay virtual text when extmark is off-screen', function()
screen:try_resize(50, 3)
command('set nowrap')
@@ -1028,12 +1131,12 @@ describe('extmark decorations', function()
it('can have virtual text of right_align and fixed win_col position', function()
insert(example_text)
feed 'gg'
- meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
+ meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'VERY', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'})
- meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
+ meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'MUCH', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'})
- meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
- meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'})
+ meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
+ meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'})
meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'})
meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_pos='right_align', hl_mode='blend'})
-- empty virt_text should not change anything
@@ -1191,40 +1294,78 @@ describe('extmark decorations', function()
|
]]}
- command 'set cpoptions-=n nonumber nowrap'
+ command 'set cpoptions-=n nowrap'
screen:expect{grid=[[
- for _,item in ipairs(items) do |
- local text, hl_id_cell, cou{4:Very} unpack(ite{4:VERY}|
- if |
- hl_id_cell ~= nil then {4:Much} {4:MUCH}|
- --^ -- -- -- -- -- -- --{4:Error}- -- -- h{4:ERROR}|
- end |
- for _ = 1, (count or 1) do |
- local cell = line[colpos] |
- {1:-} cell.text = text {1:-}|
- cell.hl_id = hl_id |
- colpos = colpos+1 |
- end |
- end |
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 2 } local text, hl_id_cell, cou{4:Very} unpack{4:VERY}|
+ {2: 3 } if |
+ {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}|
+ {2: 5 } --^ -- -- -- -- -- -- --{4:Error}- -- {4:ERROR}|
+ {2: 6 } end |
+ {2: 7 } for _ = 1, (count or 1) do |
+ {2: 8 } local cell = line[colpos] |
+ {2: 9 } {1:-} cell.text = text {1:-}|
+ {2: 10 } cell.hl_id = hl_id |
+ {2: 11 } colpos = colpos+1 |
+ {2: 12 } end |
+ {2: 13 }end |
{1:~ }|
|
]]}
- feed '8zl'
+ feed '12zl'
screen:expect{grid=[[
- em in ipairs(items) do |
- l text, hl_id_cell, count = unp{4:Very}item) {4:VERY}|
- |
- ll ~= nil then {4:Much} {4:MUCH}|
- --^ -- -- -- -- -- -- -- -- -- -{4:Error}hl_id = h{4:ERROR}|
- |
- _ = 1, (count or 1) do |
- local cell = line[colpos] |
- cell{1:-}text = text {1:-}|
- cell.hl_id = hl_id |
- colpos = colpos+1 |
- |
+ {2: 1 }n ipairs(items) do |
+ {2: 2 }xt, hl_id_cell, count = unpack({4:Very}) {4:VERY}|
+ {2: 3 } |
+ {2: 4 }= nil then {4:Much} {4:MUCH}|
+ {2: 5 }^- -- -- -- -- -- -- -- -- -- --{4:Error}d = h{4:ERROR}|
+ {2: 6 } |
+ {2: 7 }1, (count or 1) do |
+ {2: 8 }l cell = line[colpos] |
+ {2: 9 }.tex{1:-} = text {1:-}|
+ {2: 10 }.hl_id = hl_id |
+ {2: 11 }os = colpos+1 |
+ {2: 12 } |
+ {2: 13 } |
+ {1:~ }|
|
+ ]]}
+
+ feed('fhi<Tab>')
+ screen:expect{grid=[[
+ {2: 1 }n ipairs(items) do |
+ {2: 2 }xt, hl_id_cell, count = unpack({4:Very}) {4:VERY}|
+ {2: 3 } |
+ {2: 4 }= nil then {4:Much} {4:MUCH}|
+ {2: 5 }- -- -- -- -- -- -- -- -- -- --{4:Error}^hl_id{4:ERROR}|
+ {2: 6 } |
+ {2: 7 }1, (count or 1) do |
+ {2: 8 }l cell = line[colpos] |
+ {2: 9 }.tex{1:-} = text {1:-}|
+ {2: 10 }.hl_id = hl_id |
+ {2: 11 }os = colpos+1 |
+ {2: 12 } |
+ {2: 13 } |
+ {1:~ }|
+ {24:-- INSERT --} |
+ ]]}
+
+ feed('<Esc>0')
+ screen:expect{grid=[[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 2 } local text, hl_id_cell, cou{4:Very} unpack{4:VERY}|
+ {2: 3 } if |
+ {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}|
+ {2: 5 }^ -- -- -- -- -- -- -- --{4:Error}- -- {4:ERROR}|
+ {2: 6 } end |
+ {2: 7 } for _ = 1, (count or 1) do |
+ {2: 8 } local cell = line[colpos] |
+ {2: 9 } {1:-} cell.text = text {1:-}|
+ {2: 10 } cell.hl_id = hl_id |
+ {2: 11 } colpos = colpos+1 |
+ {2: 12 } end |
+ {2: 13 }end |
{1:~ }|
|
]]}
@@ -1236,20 +1377,21 @@ describe('extmark decorations', function()
22222
33333]])
command('1,2fold')
- command('set nowrap')
screen:try_resize(50, 3)
feed('zb')
-- XXX: the behavior of overlay virtual text at non-zero column is strange:
-- 1. With 'wrap' it is never shown.
-- 2. With 'nowrap' it is shown only if the extmark is hidden before leftcol.
meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'overlay' })
- meths.buf_set_extmark(0, ns, 0, 1, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 })
+ meths.buf_set_extmark(0, ns, 0, 5, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 })
meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'CC', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'right_align' })
screen:expect{grid=[[
{29:AA}{33:- 2 lin}{29:BB}{33:: 11111·····························}{29:CC}|
3333^3 |
|
]]}
+ command('set nowrap')
+ screen:expect_unchanged()
feed('zl')
screen:expect{grid=[[
{29:AA}{33:- 2 lin}{29:BB}{33:: 11111·····························}{29:CC}|
@@ -1270,6 +1412,38 @@ describe('extmark decorations', function()
]]}
end)
+ it('virtual text works below diff filler lines', function()
+ screen:try_resize(53, 8)
+ insert([[
+ aaaaa
+ bbbbb
+ ccccc
+ ddddd
+ eeeee]])
+ command('rightbelow vnew')
+ insert([[
+ bbbbb
+ ccccc
+ ddddd
+ eeeee]])
+ command('windo diffthis')
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Underlined'}}, virt_text_pos = 'overlay' })
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'BB', 'Underlined'}}, virt_text_win_col = 10 })
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'CC', 'Underlined'}}, virt_text_pos = 'right_align' })
+ screen:expect{grid=[[
+ {37: }{38:aaaaa }│{37: }{39:------------------------}|
+ {37: }bbbbb │{37: }{28:AA}bbb {28:BB} {28:CC}|
+ {37: }ccccc │{37: }ccccc |
+ {37: }ddddd │{37: }ddddd |
+ {37: }eeeee │{37: }eeee^e |
+ {1:~ }│{1:~ }|
+ {40:[No Name] [+] }{41:[No Name] [+] }|
+ |
+ ]]}
+ command('windo set wrap')
+ screen:expect_unchanged()
+ end)
+
it('can have virtual text which combines foreground and background groups', function()
screen:set_default_attr_ids {
[1] = {bold=true, foreground=Screen.colors.Blue};
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 4ede03f242..fcda0dad74 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -415,6 +415,46 @@ describe('float window', function()
eq(winids, eval('winids'))
end)
+ it("open does not trigger BufEnter #15300", function()
+ local res = exec_lua[[
+ local times = {}
+ local buf = vim.api.nvim_create_buf(fasle, true)
+ vim.api.nvim_create_autocmd('BufEnter', {
+ callback = function(opt)
+ if opt.buf == buf then
+ times[#times + 1] = 1
+ end
+ end
+ })
+ local win_id
+ local fconfig = {
+ relative = 'editor',
+ row = 10,
+ col = 10,
+ width = 10,
+ height = 10,
+ }
+ --enter is false doesn't trigger
+ win_id = vim.api.nvim_open_win(buf, false, fconfig)
+ vim.api.nvim_win_close(win_id, true)
+ times[#times + 1] = #times == 0 and true or nil
+
+ --enter is true trigger
+ win_id = vim.api.nvim_open_win(buf, true, fconfig)
+ vim.api.nvim_win_close(win_id, true)
+ times[#times + 1] = #times == 2 and true or nil
+
+ --enter is true and fconfig.noautocmd is true doesn't trigger
+ fconfig.noautocmd = true
+ win_id = vim.api.nvim_open_win(buf, true, fconfig)
+ vim.api.nvim_win_close(win_id, true)
+ times[#times + 1] = #times == 2 and true or nil
+
+ return times
+ ]]
+ eq({true, 1, true}, res)
+ end)
+
it('no crash with bufpos and non-existent window', function()
command('new')
local closed_win = meths.get_current_win().id
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index f00fba331e..c8ca5be282 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -40,12 +40,16 @@ describe("folded lines", function()
[8] = {foreground = Screen.colors.Brown },
[9] = {bold = true, foreground = Screen.colors.Brown},
[10] = {background = Screen.colors.LightGrey, underline = true},
- [11] = {bold=true},
+ [11] = {bold = true},
[12] = {foreground = Screen.colors.Red},
[13] = {foreground = Screen.colors.Red, background = Screen.colors.LightGrey},
[14] = {background = Screen.colors.Red},
[15] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Red},
[16] = {background = Screen.colors.LightGrey},
+ [17] = {background = Screen.colors.Yellow, foreground = Screen.colors.Red},
+ [18] = {background = Screen.colors.LightGrey, bold = true, foreground = Screen.colors.Blue},
+ [19] = {background = Screen.colors.Yellow, foreground = Screen.colors.DarkBlue},
+ [20] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue},
})
end)
@@ -419,6 +423,119 @@ describe("folded lines", function()
:set norightleft |
]])
end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:▸ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :set norightleft |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ screen:expect([[
+ {7:▸ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :set norightleft |
+ ]])
+ end
+
+ -- Add a winbar to avoid double-clicks
+ command('setlocal winbar=!!!!!!')
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 1, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {11:!!!!!! }|
+ {7:▾▸}{5:^+--- 5 lines: aa··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :set norightleft |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {11:!!!!!! }|
+ {7:▾▸}{5:^+--- 5 lines: aa··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :set norightleft |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 1, 1)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {11:!!!!!! }|
+ {7:▾▾}^aa |
+ {7:││}bb |
+ {7:││}cc |
+ {7:││}dd |
+ {7:││}ee |
+ {7:│ }ff |
+ ## grid 3
+ :set norightleft |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 1)
+ screen:expect([[
+ {11:!!!!!! }|
+ {7:▾▾}^aa |
+ {7:││}bb |
+ {7:││}cc |
+ {7:││}dd |
+ {7:││}ee |
+ {7:│ }ff |
+ :set norightleft |
+ ]])
+ end
end)
it("works with split", function()
@@ -2816,6 +2933,121 @@ describe("folded lines", function()
]])
end
end)
+
+ it('support foldtext with virtual text format', function()
+ screen:try_resize(30, 7)
+ insert(content1)
+ command("hi! CursorLine guibg=NONE guifg=Red gui=NONE")
+ meths.set_option_value('cursorline', true, {})
+ meths.set_option_value('foldcolumn', '4', {})
+ meths.set_option_value('foldtext',
+ '[[v:folddashes], ["\t", "Search"], [getline(v:foldstart), "NonText"]]', {})
+
+ command('3,4fold')
+ command('5,6fold')
+ command('2,6fold')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7:+ }{13:^-}{17: }{18:valid English}{13:·····}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7:+ }{13:^-}{17: }{18:valid English}{13:·····}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end
+ eq('-\tvalid English', funcs.foldtextresult(2))
+
+ feed('zo')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7:- }valid English |
+ {7:│+ }{5:--}{19: }{18:sentence composed }|
+ {7:│+ }{13:^--}{17: }{18:in his cave.}{13:······}|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7:- }valid English |
+ {7:│+ }{5:--}{19: }{18:sentence composed }|
+ {7:│+ }{13:^--}{17: }{18:in his cave.}{13:······}|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end
+ eq('--\tsentence composed by', funcs.foldtextresult(3))
+ eq('--\tin his cave.', funcs.foldtextresult(5))
+
+ command('hi! Visual guibg=Red')
+ feed('V2k')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7:- }^v{14:alid English} |
+ {7:│+ }{15:--}{19: }{20:sentence composed }|
+ {7:│+ }{15:--}{19: }{20:in his cave.}{15:······}|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {11:-- VISUAL LINE --} |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7:- }^v{14:alid English} |
+ {7:│+ }{15:--}{19: }{20:sentence composed }|
+ {7:│+ }{15:--}{19: }{20:in his cave.}{15:······}|
+ {1:~ }|
+ {1:~ }|
+ {11:-- VISUAL LINE --} |
+ ]])
+ end
+ end)
end
describe("with ext_multigrid", function()
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index b2b6ad80bb..e2b977c2b8 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -362,6 +362,7 @@ func s:GetFilenameChecks() abort
\ 'lilo': ['lilo.conf', 'lilo.conf-file'],
\ 'lilypond': ['file.ly', 'file.ily'],
\ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'],
+ \ 'liquidsoap': ['file.liq'],
\ 'liquid': ['file.liquid'],
\ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'],
\ 'lite': ['file.lite', 'file.lt'],
diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua
index 32300c167c..97b97b47bb 100644
--- a/test/unit/marktree_spec.lua
+++ b/test/unit/marktree_spec.lua
@@ -366,6 +366,68 @@ describe('marktree', function()
eq(0, tree[0].n_keys)
end)
+ itp('works with intersections and marktree_splice', function()
+ local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
+
+ for i = 1,1000 do
+ put(tree, 1, i, false, 2, 1000-i, false)
+ if i % 10 == 1 then
+ check_intersections(tree)
+ end
+ end
+
+ check_intersections(tree)
+ eq(2000, tree[0].n_keys)
+ ok(tree[0].root.level >= 2)
+
+ for _ = 1,10 do
+ lib.marktree_splice(tree, 0, 0, 0, 100, 0, 0)
+ check_intersections(tree)
+ end
+ end)
+
+ itp('marktree_move should preserve key order', function()
+ local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
+ local iter = ffi.new("MarkTreeIter[1]")
+ local ids = {}
+
+ -- new index and old index look the same, but still have to move becase
+ -- pos will get updated
+ table.insert(ids, put(tree, 1, 1, false, 1, 3, false))
+ table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
+ table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
+ table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
+
+ lib.marktree_lookup_ns(tree, ns, ids[3], false, iter)
+ lib.marktree_move(tree, iter, 1, 2)
+
+ check_intersections(tree)
+ end)
+
+ itp('works with intersections and marktree_move', function()
+ local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
+
+ local ids = {}
+
+ for i = 1,1000 do
+ table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false))
+ if i % 10 == 1 then
+ check_intersections(tree)
+ end
+ end
+
+ local iter = ffi.new("MarkTreeIter[1]")
+ for i = 1,1000 do
+ local which = i%2
+ lib.marktree_lookup_ns(tree, ns, ids[i], which, iter)
+ lib.marktree_move(tree, iter, 1+which, 500+i)
+ if i % 10 == 1 then
+ check_intersections(tree)
+ end
+ end
+
+ end)
+
itp('works with intersections with a even bigger tree', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
@@ -435,4 +497,48 @@ describe('marktree', function()
eq(0, tree[0].n_keys)
end)
+
+ itp('works with intersections with a even bigger tree and splice', function()
+ local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
+
+ -- too much overhead on ASAN
+ local size_factor = helpers.is_asan() and 3 or 10
+
+ local at_row = {}
+ for i = 1, 10 do
+ at_row[i] = {}
+ end
+
+ local size = 1000*size_factor
+ local k = 1
+ while k <= size do
+ for row1 = 1,9 do
+ for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
+ if k > size then
+ break
+ end
+ local id = put(tree, row1, k, false, row2, size-k, false)
+ for i = row1+1, row2 do
+ table.insert(at_row[i], id)
+ end
+ --if tree[0].root.level == 4 then error("kk"..k) end
+ if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then
+ check_intersections(tree)
+ end
+ k = k + 1
+ end
+ end
+ end
+
+ eq(2*size, tree[0].n_keys)
+ ok(tree[0].root.level >= 3)
+ check_intersections(tree)
+
+ for _ = 1,10 do
+ for j = 3, 8 do
+ lib.marktree_splice(tree, j, 0, 0, 200, 0, 0)
+ check_intersections(tree)
+ end
+ end
+ end)
end)