aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake.deps/deps.txt4
-rw-r--r--runtime/doc/autocmd.txt51
-rw-r--r--runtime/doc/cmdline.txt4
-rw-r--r--runtime/doc/develop.txt8
-rw-r--r--runtime/doc/editing.txt5
-rw-r--r--runtime/doc/help.txt2
-rw-r--r--runtime/doc/index.txt3
-rw-r--r--runtime/doc/insert.txt8
-rw-r--r--runtime/doc/lua-bit.txt12
-rw-r--r--runtime/doc/lua.txt2
-rw-r--r--runtime/doc/motion.txt5
-rw-r--r--runtime/doc/news.txt10
-rw-r--r--runtime/doc/options.txt21
-rw-r--r--runtime/doc/pi_gzip.txt1
-rw-r--r--runtime/doc/provider.txt3
-rw-r--r--runtime/doc/syntax.txt30
-rw-r--r--runtime/doc/terminal.txt52
-rw-r--r--runtime/doc/ui.txt2
-rw-r--r--runtime/doc/vim_diff.txt9
-rw-r--r--runtime/ftplugin/go.vim27
-rw-r--r--runtime/ftplugin/tera.vim30
-rw-r--r--runtime/ftplugin/vim.vim4
-rw-r--r--runtime/lua/tohtml.lua4
-rw-r--r--runtime/lua/vim/_defaults.lua74
-rw-r--r--runtime/lua/vim/_editor.lua27
-rw-r--r--runtime/lua/vim/_meta/api.lua18
-rw-r--r--runtime/lua/vim/_meta/options.lua27
-rw-r--r--runtime/lua/vim/diagnostic.lua3
-rw-r--r--runtime/lua/vim/filetype.lua2
-rw-r--r--runtime/lua/vim/lsp/buf.lua14
-rw-r--r--runtime/lua/vim/lsp/protocol.lua2
-rw-r--r--runtime/lua/vim/lsp/util.lua4
-rw-r--r--runtime/lua/vim/termcap.lua7
-rw-r--r--runtime/lua/vim/ui/clipboard/osc52.lua2
-rw-r--r--runtime/makemenu.vim3
-rw-r--r--runtime/plugin/matchparen.vim13
-rw-r--r--runtime/synmenu.vim41
-rw-r--r--runtime/syntax/abnf.vim33
-rw-r--r--runtime/syntax/tera.vim96
-rw-r--r--runtime/syntax/vim.vim24
-rwxr-xr-xscripts/lintdoc.lua6
-rwxr-xr-xsrc/gen/gen_eval_files.lua4
-rw-r--r--src/gen/gen_events.lua39
-rw-r--r--src/gen/gen_help_html.lua8
-rw-r--r--src/gen/gen_keycodes.lua86
-rw-r--r--src/gen/gen_vimvim.lua13
-rw-r--r--src/gen/hashy.lua26
-rw-r--r--src/nvim/CMakeLists.txt53
-rw-r--r--src/nvim/api/ui.c6
-rw-r--r--src/nvim/auevents.lua283
-rw-r--r--src/nvim/autocmd.c7
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/change.c8
-rw-r--r--src/nvim/channel.c2
-rw-r--r--src/nvim/cmdexpand.c4
-rw-r--r--src/nvim/cmdexpand.h1
-rw-r--r--src/nvim/drawscreen.c19
-rw-r--r--src/nvim/edit.c40
-rw-r--r--src/nvim/eval/funcs.c2
-rw-r--r--src/nvim/eval/userfunc.c31
-rw-r--r--src/nvim/event/loop.c9
-rw-r--r--src/nvim/event/multiqueue.c28
-rw-r--r--src/nvim/ex_getln.c15
-rw-r--r--src/nvim/getchar.c15
-rw-r--r--src/nvim/getchar.h1
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/keycodes.c250
-rw-r--r--src/nvim/keycodes.lua208
-rw-r--r--src/nvim/lua/executor.c46
-rw-r--r--src/nvim/normal.c24
-rw-r--r--src/nvim/ops.c4
-rw-r--r--src/nvim/options.lua23
-rw-r--r--src/nvim/popupmenu.c74
-rw-r--r--src/nvim/strings.c22
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c148
-rw-r--r--src/nvim/textobject.c8
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--test/benchmark/keycodes_spec.lua84
-rw-r--r--test/functional/api/extmark_spec.lua30
-rw-r--r--test/functional/api/vim_spec.lua25
-rw-r--r--test/functional/core/channels_spec.lua49
-rw-r--r--test/functional/editor/defaults_spec.lua2
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua14
-rw-r--r--test/functional/legacy/breakindent_spec.lua33
-rw-r--r--test/functional/legacy/cmdline_spec.lua29
-rw-r--r--test/functional/legacy/display_spec.lua15
-rw-r--r--test/functional/legacy/matchparen_spec.lua44
-rw-r--r--test/functional/legacy/messages_spec.lua296
-rw-r--r--test/functional/legacy/number_spec.lua281
-rw-r--r--test/functional/legacy/scroll_opt_spec.lua15
-rw-r--r--test/functional/legacy/search_spec.lua156
-rw-r--r--test/functional/legacy/search_stat_spec.lua53
-rw-r--r--test/functional/lua/commands_spec.lua11
-rw-r--r--test/functional/lua/diagnostic_spec.lua104
-rw-r--r--test/functional/lua/inspector_spec.lua12
-rw-r--r--test/functional/lua/loop_spec.lua48
-rw-r--r--test/functional/lua/ui_event_spec.lua4
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua43
-rw-r--r--test/functional/plugin/lsp_spec.lua97
-rw-r--r--test/functional/terminal/buffer_spec.lua197
-rw-r--r--test/functional/terminal/cursor_spec.lua140
-rw-r--r--test/functional/terminal/tui_spec.lua52
-rw-r--r--test/functional/ui/bufhl_spec.lua22
-rw-r--r--test/functional/ui/decorations_spec.lua81
-rw-r--r--test/functional/ui/popupmenu_spec.lua364
-rw-r--r--test/old/testdir/gen_opt_test.vim1
-rw-r--r--test/old/testdir/test_cmdline.vim83
-rw-r--r--test/old/testdir/test_filetype.vim2
-rw-r--r--test/old/testdir/test_ins_complete.vim4
-rw-r--r--test/old/testdir/test_plugin_matchparen.vim32
-rw-r--r--test/old/testdir/test_popup.vim9
-rw-r--r--test/old/testdir/test_registers.vim50
-rw-r--r--test/old/testdir/test_tagjump.vim17
-rw-r--r--test/old/testdir/test_visual.vim44
-rw-r--r--test/unit/multiqueue_spec.lua2
116 files changed, 3188 insertions, 1567 deletions
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index fc4ddef54b..fef7496e20 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -46,8 +46,8 @@ TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/a
TREESITTER_QUERY_SHA256 e33907fd334350e32e49b3875c36bcf070defe490357632fac9398e6d4540a80
TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.3.2.tar.gz
TREESITTER_MARKDOWN_SHA256 5dac48a6d971eb545aab665d59a18180d21963afc781bbf40f9077c06cb82ae5
-TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.2.tar.gz
-TREESITTER_SHA256 26791f69182192fef179cd58501c3226011158823557a86fe42682cb4a138523
+TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.3.tar.gz
+TREESITTER_SHA256 862fac52653bc7bc9d2cd0630483e6bdf3d02bcd23da956ca32663c4798a93e3
WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v29.0.1.tar.gz
WASMTIME_SHA256 b94b6c6fd6aebaf05d4c69c1b12b5dc217b0d42c1a95f435b33af63dddfa5304
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 413802eea1..3a579ab194 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -352,10 +352,14 @@ BufWritePost After writing the whole buffer to a file
*ChanInfo*
ChanInfo State of channel changed, for instance the
client of a RPC channel described itself.
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
Sets these |v:event| keys:
info as from |nvim_get_chan_info()|
*ChanOpen*
ChanOpen Just after a channel was opened.
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
Sets these |v:event| keys:
info as from |nvim_get_chan_info()|
*CmdUndefined*
@@ -364,6 +368,8 @@ CmdUndefined When a user command is used but it isn't
when it's used. The pattern is matched
against the command name. Both <amatch> and
<afile> expand to the command name.
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
NOTE: Autocompletion won't work until the
command is defined. An alternative is to
always define the user command and have it
@@ -689,6 +695,8 @@ FuncUndefined When a user function is used but it isn't
when it's used. The pattern is matched
against the function name. Both <amatch> and
<afile> are set to the name of the function.
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
NOTE: When writing Vim scripts a better
alternative is to use an autoloaded function.
See |autoload-functions|.
@@ -910,7 +918,10 @@ ShellCmdPost After executing a shell command with |:!cmd|,
*Signal*
Signal After Nvim receives a signal. The pattern is
matched against the signal name. Only
- "SIGUSR1" and "SIGWINCH" are supported. Example: >
+ "SIGUSR1" and "SIGWINCH" are supported.
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
+ Example: >vim
autocmd Signal SIGUSR1 call some#func()
< *ShellFilterPost*
ShellFilterPost After executing a shell command with
@@ -1004,22 +1015,40 @@ TermClose When a |terminal| job ends.
status
*TermRequest*
TermRequest When a |:terminal| child process emits an OSC,
- DCS or APC sequence. Sets |v:termrequest|. The
- |event-data| is the request string.
+ DCS, or APC sequence. Sets |v:termrequest|. The
+ |event-data| is a table with the following
+ fields:
+
+ - sequence: the received sequence
+ - cursor: (1,0)-indexed, buffer-relative
+ position of the cursor when the sequence was
+ received
+
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
+
*TermResponse*
TermResponse When Nvim receives an OSC or DCS response from
the host terminal. Sets |v:termresponse|. The
- |event-data| is the response string. May be
- triggered during another event (file I/O,
- a shell command, or anything else that takes
- time). Example: >lua
+ |event-data| is a table with the following fields:
+
+ - sequence: the received sequence
+
+ This is triggered even when inside an
+ autocommand defined without |autocmd-nested|.
+
+ May be triggered during another event (file
+ I/O, a shell command, or anything else that
+ takes time).
+
+ Example: >lua
-- Query the terminal palette for the RGB value of color 1
-- (red) using OSC 4
vim.api.nvim_create_autocmd('TermResponse', {
once = true,
callback = function(args)
- local resp = args.data
+ local resp = args.data.sequence
local r, g, b = resp:match("\027%]4;1;rgb:(%w+)/(%w+)/(%w+)")
end,
})
@@ -1391,8 +1420,8 @@ vimrc file again).
*FileExplorer*
There is one group that is recognized by Vim: FileExplorer. If this group
exists Vim assumes that editing a directory is possible and will trigger a
-plugin that lists the files in that directory. This is used by the |netrw|
-plugin. This allows you to do: >
+plugin that lists the files in that directory. This is used by directory
+browser plugins. This allows you to do: >
browse edit
==============================================================================
@@ -1666,7 +1695,7 @@ and "++ff=" argument that are effective. These should be used for the command
that reads/writes the file. The |v:cmdbang| variable is one when "!" was
used, zero otherwise.
-See the $VIMRUNTIME/plugin/netrwPlugin.vim for examples.
+See the $VIMRUNTIME/pack/dist/opt/netrw/plugin/netrwPlugin.vim for examples.
==============================================================================
11. Disabling autocommands *autocmd-disable*
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 3fc17af185..45a95e439a 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -83,6 +83,10 @@ CTRL-Q Same as CTRL-V. But with some terminals it is used for
CTRL-SHIFT-V *c_CTRL-SHIFT-V* *c_CTRL-SHIFT-Q*
CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
modifier into the key.
+ Note: When CTRL-SHIFT-V is intercepted by your system (e.g.,
+ to paste text) you can often use CTRL-SHIFT-Q instead.
+ However, in some terminals (e.g. GNOME Terminal), CTRL-SHIFT-Q
+ quits the terminal without confirmation.
*c_<Left>* *c_Left*
<Left> cursor left. See 'wildmenu' for behavior during wildmenu
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index d3170f114f..8d60d2c868 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -376,10 +376,10 @@ Where possible, these patterns apply to _both_ Lua and the API:
- See |vim.lsp.inlay_hint.enable()| and |vim.lsp.inlay_hint.is_enabled()|
for a reference implementation of these "best practices".
- NOTE: open questions: https://github.com/neovim/neovim/issues/28603
-- Transformation functions should also have a filter functionality when
- appropriate. That is, when the function returns a nil value it "filters" its
- input, otherwise the transformed value is used.
- - Example: |vim.diagnostic.config.format()|
+- Transformation functions should also have "filter" functionality (when
+ appropriate): when the function returns a nil value it excludes (filters
+ out) its input, else the transformed value is used.
+ - Example: See the format() field of |vim.diagnostic.Opts.Float|.
API DESIGN GUIDELINES *dev-api*
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 4224709a39..c275bf863d 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1496,9 +1496,8 @@ Or, when starting gvim from a shell: >
Note that if a FileChangedShell autocommand is defined you will not get a
warning message or prompt. The autocommand is expected to handle this.
-There is no warning for a directory (e.g., with |netrw-browse|). But you do
-get warned if you started editing a new file and it was created as a directory
-later.
+There is no warning for a directory. But you do get warned if you started
+editing a new file and it was created as a directory later.
When Vim notices the timestamp of a file has changed, and the file is being
edited in a buffer but has not changed, Vim checks if the contents of the file
diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt
index 914dc64c0a..991b3d02c8 100644
--- a/runtime/doc/help.txt
+++ b/runtime/doc/help.txt
@@ -180,11 +180,11 @@ Standard plugins ~
*standard-plugin-list*
|pi_gzip.txt| Reading and writing compressed files
|pi_msgpack.txt| msgpack utilities
-|pi_netrw.txt| Reading and writing files over a network
|pi_paren.txt| Highlight matching parens
|pi_spec.txt| Filetype plugin to work with rpm spec files
|pi_tar.txt| Tar file explorer
|pi_zip.txt| Zip archive explorer
+|netrw| Reading and writing files over a network
Local additions ~
*local-additions*
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 0256707420..b814b3be78 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -786,8 +786,7 @@ tag char note action in Normal mode ~
|gu| gu{motion} 2 make Nmove text lowercase
|gv| gv reselect the previous Visual area
|gw| gw{motion} 2 format Nmove text and keep cursor
-|netrw-gx| gx execute application for file name under the
- cursor (only with |netrw| plugin)
+|gx| gx execute application for filepath at cursor
|g@| g@{motion} call 'operatorfunc'
|g~| g~{motion} 2 swap case for Nmove text
|g<Down>| g<Down> 1 same as "gj"
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 06f1a4e73d..b77339a325 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -212,6 +212,10 @@ CTRL-Q Same as CTRL-V.
CTRL-SHIFT-V *i_CTRL-SHIFT-V* *i_CTRL-SHIFT-Q*
CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
modifier into the key.
+ Note: When CTRL-SHIFT-V is intercepted by your system (e.g.,
+ to paste text) you can often use CTRL-SHIFT-Q instead.
+ However, in some terminals (e.g. GNOME Terminal), CTRL-SHIFT-Q
+ quits the terminal without confirmation.
CTRL-X Enter CTRL-X mode. This is a sub-mode where commands can
be given to complete words or scroll the window. See
@@ -639,8 +643,8 @@ Also see the 'infercase' option if you want to adjust the case of the match.
When inserting a selected candidate word from the |popup-menu|, the part of
the candidate word that does not match the query is highlighted using
-|hl-ComplMatchIns|. If fuzzy is enabled in 'completopt', highlighting will not
-be applied.
+|hl-ComplMatchIns|. If fuzzy is enabled in 'completeopt', highlighting will
+not be applied.
*complete_CTRL-E*
When completion is active you can use CTRL-E to stop it and go back to the
diff --git a/runtime/doc/lua-bit.txt b/runtime/doc/lua-bit.txt
index 4c47010113..dde235d141 100644
--- a/runtime/doc/lua-bit.txt
+++ b/runtime/doc/lua-bit.txt
@@ -86,19 +86,17 @@ Bit operations
y = bit.tobit(x) *bit.tobit()*
Normalizes a number to the numeric range for bit operations and returns
it. This function is usually not needed since all bit operations already
- normalize all of their input arguments. Check the |luabit-semantics| for
- details.
+ normalize all of their input arguments. See |lua-bit-semantics|.
Example: >lua
- print(0xffffffff) --> 4294967295 (*)
+ print(0xffffffff) --> 4294967295 (see Note)
print(bit.tobit(0xffffffff)) --> -1
printx(bit.tobit(0xffffffff)) --> 0xffffffff
print(bit.tobit(0xffffffff + 1)) --> 0
print(bit.tobit(2^40 + 1234)) --> 1234
<
- (*) See the treatment of |lua-bit-hex-literals| for an explanation why the
- printed numbers in the first two lines differ (if your Lua installation
- uses a double number type).
+ Note: |lua-bit-hex-literals| explains why the numbers printed in the first
+ two lines differ (if your Lua installation uses a double number type).
y = bit.tohex(x [,n]) *bit.tohex()*
Converts its first argument to a hex string. The number of hex digits is
@@ -369,7 +367,7 @@ strongly advised not to rely on undefined or implementation-defined behavior.
- Non-integral numbers may be rounded or truncated in an
implementation-defined way. This means the result could differ between
different BitOp versions, different Lua VMs, on different platforms or
- even between interpreted vs. compiled code (as in |LuaJIT|). Avoid
+ even between interpreted vs. compiled code (as in LuaJIT). Avoid
passing fractional numbers to bitwise functions. Use `math.floor()` or
`math.ceil()` to get defined behavior.
- Lua provides auto-coercion of string arguments to numbers by default. This
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index c184e4792d..fa3367602d 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -289,7 +289,7 @@ arguments separated by " " (space) instead of "\t" (tab).
:lua {chunk}
Executes Lua chunk {chunk}. If {chunk} starts with "=" the rest of the
chunk is evaluated as an expression and printed. `:lua =expr` and `:=expr`
- are equivalent to `:lua vim.print(expr)`.
+ are equivalent to `:lua print(vim.inspect(expr))`.
Examples: >vim
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 284be09121..48fa723800 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -455,6 +455,8 @@ between Vi and Vim.
first column. When used after an operator, then also
stops below a "}" in the first column. |exclusive|
Note that |exclusive-linewise| often applies.
+ In a :terminal buffer each shell prompt is treated as
+ a section. |terminal_]]|
*][*
][ [count] |section|s forward or to the next '}' in the
@@ -465,6 +467,8 @@ between Vi and Vim.
[[ [count] |section|s backward or to the previous "{" in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
+ In a :terminal buffer each shell prompt is treated as
+ a section. |terminal_]]|
*[]*
[] [count] |section|s backward or to the previous "}" in
@@ -498,6 +502,7 @@ A section begins after a form-feed (<C-L>) in the first column and at each of
a set of section macros, specified by the pairs of characters in the
'sections' option. The default is "SHNHH HUnhsh", which defines a section to
start at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
+In a :terminal buffer each shell prompt is treated as a section. |terminal_]]|
The "]]" and "[[" commands stop at the '{' in the first column. This is
useful to find the start of a function in a C program. To search for a '}' in
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index a131934a8e..5a3e35a3dd 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -111,6 +111,9 @@ EVENTS
• `history` argument indicating if the message was added to the history.
• new message kinds: "bufwrite", "completion", "list_cmd", "lua_print",
"search_cmd", "shell_out/err/ret", "undo", "verbose", wildlist".
+• |TermRequest| and |TermResponse| |event-data| is now a table. The "sequence"
+ field contains the received sequence. |TermRequest| also contains a "cursor"
+ field indicating the cursor's position when the sequence was received.
HIGHLIGHTS
@@ -241,6 +244,8 @@ DEFAULTS
• |[a|, |]a|, |[A|, |]A| navigate through the |argument-list|
• |[b|, |]b|, |[B|, |]B| navigate through the |buffer-list|
• |[<Space>|, |]<Space>| add an empty line above and below the cursor
+ • |[[| and |]]| in Normal mode jump between shell prompts for shells which emit
+ OSC 133 sequences ("shell integration" or "semantic prompts").
• Snippet:
• `<Tab>` in Insert and Select mode maps to `vim.snippet.jump({ direction = 1 })`
@@ -306,6 +311,8 @@ LSP
• |vim.lsp.config()| has been added to define default configurations for
servers. In addition, configurations can be specified in `lsp/<name>.lua`.
• |vim.lsp.enable()| has been added to enable servers.
+• |vim.lsp.buf.code_action()| resolves the `command` property during the
+ `codeAction/resolve` request.
LUA
@@ -329,6 +336,7 @@ OPTIONS
• 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|.
• 'completeopt' flag "preinsert" highlights text to be inserted.
+• 'wildmode' flag "noselect" shows 'wildmenu' without selecting an entry.
• 'messagesopt' configures |:messages| and |hit-enter| prompt.
• 'tabclose' controls which tab page to focus when closing a tab page.
• 'eventignorewin' to persistently ignore events in a window.
@@ -395,6 +403,8 @@ TERMINAL
codes" mode is currently supported.
• The |terminal| emits a |TermRequest| autocommand event when the child process
emits an APC control sequence.
+• |TermRequest| has a "cursor" field in its |event-data| indicating the
+ cursor position when the sequence was received.
TREESITTER
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index a6f5f85d37..e42fb4f9e4 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1556,15 +1556,15 @@ A jump table for the options with a short description can be found at |Q_op|.
match, e.g., what file it comes from.
noinsert Do not insert any text for a match until the user selects
- a match from the menu. Only works in combination with
+ a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
noselect Same as "noinsert", except that no menu item is
- pre-selected. If both "noinsert" and "noselect" are
+ pre-selected. If both "noinsert" and "noselect" are
present, "noselect" has precedence.
nosort Disable sorting of completion candidates based on fuzzy
- scores when "fuzzy" is enabled. Candidates will appear
+ scores when "fuzzy" is enabled. Candidates will appear
in their original order.
popup Show extra information about the currently selected
@@ -1575,7 +1575,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Preinsert the portion of the first candidate word that is
not part of the current completion leader and using the
|hl-ComplMatchIns| highlight group. In order for it to
- work, "fuzzy" must not bet set and "menuone" must be set.
+ work, "fuzzy" must not be set and "menuone" must be set.
preview Show extra information about the currently selected
completion in the preview window. Only works in
@@ -4505,7 +4505,7 @@ A jump table for the options with a short description can be found at |Q_op|.
set path=,,
< - A directory name may end in a ':' or '/'.
- Environment variables are expanded |:set_env|.
- - When using |netrw.vim| URLs can be used. For example, adding
+ - When using |netrw| URLs can be used. For example, adding
"https://www.vim.org" will make ":find index.html" work.
- Search upwards and downwards in a directory tree using "*", "**" and
";". See |file-searching| for info and syntax.
@@ -7050,7 +7050,10 @@ A jump table for the options with a short description can be found at |Q_op|.
"lastused" When completing buffer names and more than one buffer
matches, sort buffers by time last used (other than
the current buffer).
- When there is only a single match, it is fully completed in all cases.
+ "noselect" Do not pre-select first menu item and start 'wildmenu'
+ if it is enabled.
+ When there is only a single match, it is fully completed in all cases
+ except when "noselect" is present.
Examples of useful colon-separated values:
"longest:full" Like "longest", but also start 'wildmenu' if it is
@@ -7073,7 +7076,11 @@ A jump table for the options with a short description can be found at |Q_op|.
set wildmode=list,full
< List all matches without completing, then each full match >vim
set wildmode=longest,list
-< Complete longest common string, then list alternatives.
+< Complete longest common string, then list alternatives >vim
+ set wildmode=noselect:full
+< Display 'wildmenu' without completing, then each full match >vim
+ set wildmode=noselect:lastused,full
+< Same as above, but sort buffers by time last used.
More info here: |cmdline-completion|.
*'wildoptions'* *'wop'*
diff --git a/runtime/doc/pi_gzip.txt b/runtime/doc/pi_gzip.txt
index e8a1de9cef..65e7942562 100644
--- a/runtime/doc/pi_gzip.txt
+++ b/runtime/doc/pi_gzip.txt
@@ -29,6 +29,7 @@ with these extensions:
extension compression >
*.bz2 bzip2
+ *.bz3 bzip3
*.gz gzip
*.lz lzip
*.lz4 lz4
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index 69ae0f20d1..24ec170319 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -264,7 +264,8 @@ causes the terminal emulator to write to or read from the system clipboard.
When Nvim is running in the |TUI|, it will automatically attempt to determine if
the host terminal emulator supports OSC 52. If it does, then Nvim will use OSC
52 for copying and pasting if no other |clipboard-tool| is found and when
-'clipboard' is unset.
+'clipboard' is unset. NOTE: Using a terminal multiplexer (e.g. tmux) may
+inhibit automatic OSC 52 support detection.
*g:termfeatures*
To disable the automatic detection, set the "osc52" key of |g:termfeatures| to
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 3a19ee55c3..00486a49db 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -246,12 +246,6 @@ Added added line in a diff
Changed changed line in a diff
Removed removed line in a diff
-The names marked with * are the preferred groups; the others are minor groups.
-For the preferred groups, the "syntax.vim" file contains default highlighting.
-The minor groups are linked to the preferred groups, so they get the same
-highlighting. You can override these defaults by using ":highlight" commands
-after sourcing the "syntax.vim" file.
-
Note that highlight group names are not case sensitive. "String" and "string"
can be used for the same group.
@@ -4816,16 +4810,20 @@ is mostly used, because it looks better.
==============================================================================
13. Highlight command *:highlight* *:hi* *E28* *E411* *E415*
-There are two types of highlight groups:
-- The built-in |highlight-groups|.
-- The ones used for specific languages. For these the name starts with the
- name of the language. Many of these don't have any attributes, but are
- linked to a group of the second type.
- *hitest.vim*
-You can see all the groups currently active with this command: >
- :so $VIMRUNTIME/syntax/hitest.vim
-This will open a new window containing all highlight group names, displayed
-in their own color.
+Nvim uses a range of highlight groups which fall into two categories: Editor
+interface and syntax highlighting. In rough order of importance, these are
+- basic editor |highlight-groups|
+- standard syntax |group-name|s (in addition, syntax files can define
+ language-specific groups, which are prefixed with the language name)
+- |diagnostic-highlights|
+- |treesitter-highlight-groups|
+- |lsp-semantic-highlight| groups
+- |lsp-highlight| of symbols and references
+
+Where appropriate, highlight groups are linked by default to one of the more basic
+groups, but colorschemes are expected to cover all of them. Under each tag,
+the corresponding highlight groups are highlighted using the current
+colorscheme.
*:colo* *:colorscheme* *E185*
:colo[rscheme] Output the name of the currently active color scheme.
diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt
index 0ab7151728..ea8a1c2565 100644
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -144,8 +144,8 @@ directory indicated in the request. >lua
vim.api.nvim_create_autocmd({ 'TermRequest' }, {
desc = 'Handles OSC 7 dir change requests',
callback = function(ev)
- if string.sub(vim.v.termrequest, 1, 4) == '\x1b]7;' then
- local dir = string.gsub(vim.v.termrequest, '\x1b]7;file://[^/]*', '')
+ if string.sub(ev.data.sequence, 1, 4) == '\x1b]7;' then
+ local dir = string.gsub(ev.data.sequence, '\x1b]7;file://[^/]*', '')
if vim.fn.isdirectory(dir) == 0 then
vim.notify('invalid dir: '..dir)
return
@@ -185,6 +185,54 @@ clipboard.
OSC 52 sequences sent from the :terminal buffer do not emit a |TermRequest|
event. The event is handled directly by Nvim and is not forwarded to plugins.
+OSC 133: shell integration *terminal-osc133*
+
+Shells can emit semantic escape sequences (OSC 133) to mark where each prompt
+starts and ends. The start of a prompt is marked by sequence `OSC 133 ; A ST`,
+and the end by `OSC 133 ; B ST`.
+
+ *shell-prompt-config*
+You can configure your shell "rc" (e.g. ~/.bashrc) to emit OSC 133 sequences,
+or your terminal may attempt to do it for you (assuming your shell config
+doesn't interfere).
+
+- fish: https://fishshell.com/docs/current/relnotes.html#improved-terminal-support
+- kitty: https://sw.kovidgoyal.net/kitty/shell-integration/
+- vscode: https://code.visualstudio.com/docs/terminal/shell-integration
+
+To configure bash to mark the start/end of each prompt, set $PROMPT_COMMAND
+and $PS1 as follows: >bash
+
+ # Prompt start:
+ PROMPT_COMMAND='printf "\033]133;A\007"'
+ # Prompt end:
+ PS1="$PS1"'\033]133;B\007'
+<
+ *terminal_]]* *terminal_[[*
+The |]]| and |[[| motions jump to the next/previous prompts, if your shell
+emits OSC 133 as described above.
+
+ *terminal-shell-prompt-signs*
+To annotate each terminal prompt with a sign, call |nvim_buf_set_extmark()|
+from a |TermRequest| handler: >lua
+
+ local ns = vim.api.nvim_create_namespace('my.terminal.prompt')
+ vim.api.nvim_create_autocmd('TermRequest', {
+ callback = function(args)
+ if string.match(args.data.sequence, '^\027]133;A') then
+ local lnum = args.data.cursor[1]
+ vim.api.nvim_buf_set_extmark(args.buf, ns, lnum - 1, 0, {
+ sign_text = '▶',
+ sign_hl_group = 'SpecialChar',
+ })
+ end
+ end,
+ })
+ -- Enable signcolumn in terminal buffers.
+ vim.api.nvim_create_autocmd('TermOpen', {
+ command = 'setlocal signcolumn=auto',
+ })
+<
==============================================================================
Status Variables *terminal-status*
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index f0172c36e5..31335b4fbb 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -789,7 +789,7 @@ must handle.
"" (empty) Unknown (consider a |feature-request|)
"bufwrite" |:write| message
"confirm" Message preceding a prompt (|:confirm|,
- |confirm()|, |inputlist()|, |z=,|, …)
+ |confirm()|, |inputlist()|, |z=|, …)
"emsg" Error (|errors|, internal error, |:throw|, …)
"echo" |:echo| message
"echomsg" |:echomsg| message
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 1e1f6c4db7..8d8b97fe31 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -34,7 +34,8 @@ Defaults *defaults* *nvim-defaults*
looking differently due to them relying on how highlight groups are defined
by default. Add ":colorscheme vim" to |init.vim| or
":source $VIMRUNTIME/colors/vim.lua" to your color scheme file to restore
- the old default links and colors.
+ the old default links and colors. See |:highlight| for a list of highlight
+ groups colorschemes should set.
- 'autoindent' is enabled
- 'autoread' is enabled (works in all UIs, including terminal)
@@ -181,7 +182,10 @@ nvim.terminal:
when 'background' is "light". While this may not reflect the actual
foreground/background color, it permits 'background' to be retained for a
nested Nvim instance running in the terminal emulator.
-- TermOpen: Sets default options for |terminal| buffers:
+- TermRequest: Nvim will create extmarks for shells which
+ annotate their prompts with OSC 133 escape sequences, enabling users to
+ quickly navigate between prompts using |[[| and |]]|.
+- TermOpen: Sets default options and mappings for |terminal| buffers:
- 'nomodifiable'
- 'undolevels' set to -1
- 'textwidth' set to 0
@@ -193,6 +197,7 @@ nvim.terminal:
- 'foldcolumn' set to "0"
- 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in
place of |hl-StatusLine| and |hl-StatusLineNC|
+ - |[[| and |]]| to navigate between shell prompts
nvim.cmdwin:
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|.
diff --git a/runtime/ftplugin/go.vim b/runtime/ftplugin/go.vim
index 75f78cfa4b..b83db424bd 100644
--- a/runtime/ftplugin/go.vim
+++ b/runtime/ftplugin/go.vim
@@ -3,6 +3,7 @@
" Maintainer: David Barnett (https://github.com/google/vim-ft-go)
" Last Change: 2014 Aug 16
" 2024 Jul 16 by Vim Project (add recommended indent style)
+" 2025 Mar 07 by Vim Project (add formatprg and keywordprg option #16804)
if exists('b:did_ftplugin')
finish
@@ -10,15 +11,39 @@ endif
let b:did_ftplugin = 1
setlocal formatoptions-=t
+setlocal formatprg=gofmt
setlocal comments=s1:/*,mb:*,ex:*/,://
setlocal commentstring=//\ %s
+setlocal keywordprg=:GoKeywordPrg
-let b:undo_ftplugin = 'setl fo< com< cms<'
+command! -buffer -nargs=* GoKeywordPrg call s:GoKeywordPrg()
+
+let b:undo_ftplugin = 'setl fo< com< cms< fp< kp<'
+ \ . '| delcommand -buffer GoKeywordPrg'
if get(g:, 'go_recommended_style', 1)
setlocal noexpandtab softtabstop=0 shiftwidth=0
let b:undo_ftplugin ..= ' | setl et< sts< sw<'
endif
+if !exists('*' .. expand('<SID>') .. 'GoKeywordPrg')
+ func! s:GoKeywordPrg()
+ let temp_isk = &l:iskeyword
+ setl iskeyword+=.
+ try
+ let cmd = 'go doc -C ' . shellescape(expand('%:h')) . ' ' . shellescape(expand('<cword>'))
+ if has('nvim')
+ exe "term" cmd
+ startinsert
+ tmap <buffer> <Esc> <Cmd>call jobstop(&channel) <Bar> bdelete<CR>
+ else
+ exe '!' . cmd
+ endif
+ finally
+ let &l:iskeyword = temp_isk
+ endtry
+ endfunc
+endif
+
" vim: sw=2 sts=2 et
diff --git a/runtime/ftplugin/tera.vim b/runtime/ftplugin/tera.vim
new file mode 100644
index 0000000000..65bae70048
--- /dev/null
+++ b/runtime/ftplugin/tera.vim
@@ -0,0 +1,30 @@
+" Vim filetype plugin file
+" Language: Tera
+" Maintainer: Muntasir Mahmud <muntasir.joypurhat@gmail.com>
+" Last Change: 2025 Mar 08
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal autoindent
+
+setlocal commentstring={#\ %s\ #}
+setlocal comments=s:{#,e:#}
+
+if exists("loaded_matchit")
+ let b:match_ignorecase = 0
+ let b:match_words = '{#:##\|#},{% *if:{% *else\>:{% *elif\>:{% *endif %},{% *for\>:{% *endfor %},{% *macro\>:{% *endmacro %},{% *block\>:{% *endblock %},{% *filter\>:{% *endfilter %},{% *set\>:{% *endset %},{% *raw\>:{% *endraw %},{% *with\>:{% *endwith %}'
+endif
+
+setlocal includeexpr=substitute(v:fname,'\\([^.]*\\)$','\\1','g')
+setlocal suffixesadd=.tera
+
+setlocal expandtab
+setlocal shiftwidth=2
+setlocal softtabstop=2
+
+let b:undo_ftplugin = "setlocal autoindent< commentstring< comments< " ..
+ \ "includeexpr< suffixesadd< expandtab< shiftwidth< softtabstop<"
+let b:undo_ftplugin .= "|unlet! b:match_ignorecase b:match_words"
diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim
index 16730185c7..4772683ee8 100644
--- a/runtime/ftplugin/vim.vim
+++ b/runtime/ftplugin/vim.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Vim
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2025 Feb 25
+" Last Change: 2025 Mar 05
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" Contributors: Riley Bruins <ribru17@gmail.com> ('commentstring'),
" @Konfekt
@@ -85,6 +85,8 @@ if !exists("*" .. expand("<SID>") .. "Help")
return ':'.topic
elseif pre =~# '\<v:$'
return 'v:'.topic
+ elseif pre =~# '<$'
+ return '<'.topic.'>'
elseif pre =~# '\\$'
return '/\'.topic
elseif topic ==# 'v' && post =~# ':\w\+'
diff --git a/runtime/lua/tohtml.lua b/runtime/lua/tohtml.lua
index 4415a8cdca..6b8daab2c5 100644
--- a/runtime/lua/tohtml.lua
+++ b/runtime/lua/tohtml.lua
@@ -205,7 +205,9 @@ local function try_query_terminal_color(color)
once = true,
callback = function(args)
hex = '#'
- .. table.concat({ args.data:match('\027%]%d+;%d*;?rgb:(%w%w)%w%w/(%w%w)%w%w/(%w%w)%w%w') })
+ .. table.concat({
+ args.data.sequence:match('\027%]%d+;%d*;?rgb:(%w%w)%w%w/(%w%w)%w%w/(%w%w)%w%w'),
+ })
end,
})
if type(color) == 'number' then
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index c2e4e76dd6..c709f0a308 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -515,8 +515,8 @@ do
if channel == 0 then
return
end
- local fg_request = args.data == '\027]10;?'
- local bg_request = args.data == '\027]11;?'
+ local fg_request = args.data.sequence == '\027]10;?'
+ local bg_request = args.data.sequence == '\027]11;?'
if fg_request or bg_request then
-- WARN: This does not return the actual foreground/background color,
-- but rather returns:
@@ -534,14 +534,59 @@ do
end,
})
+ local nvim_terminal_prompt_ns = vim.api.nvim_create_namespace('nvim.terminal.prompt')
+ vim.api.nvim_create_autocmd('TermRequest', {
+ group = nvim_terminal_augroup,
+ desc = 'Mark shell prompts indicated by OSC 133 sequences for navigation',
+ callback = function(args)
+ if string.match(args.data.sequence, '^\027]133;A') then
+ local lnum = args.data.cursor[1] ---@type integer
+ vim.api.nvim_buf_set_extmark(args.buf, nvim_terminal_prompt_ns, lnum - 1, 0, {})
+ end
+ end,
+ })
+
+ ---@param ns integer
+ ---@param buf integer
+ ---@param count integer
+ local function jump_to_prompt(ns, win, buf, count)
+ local row, col = unpack(vim.api.nvim_win_get_cursor(win))
+ local start = -1
+ local end_ ---@type 0|-1
+ if count > 0 then
+ start = row
+ end_ = -1
+ elseif count < 0 then
+ -- Subtract 2 because row is 1-based, but extmarks are 0-based
+ start = row - 2
+ end_ = 0
+ end
+
+ if start < 0 then
+ return
+ end
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(
+ buf,
+ ns,
+ { start, col },
+ end_,
+ { limit = math.abs(count) }
+ )
+ if #extmarks > 0 then
+ local extmark = extmarks[math.min(#extmarks, math.abs(count))]
+ vim.api.nvim_win_set_cursor(win, { extmark[2] + 1, extmark[3] })
+ end
+ end
+
vim.api.nvim_create_autocmd('TermOpen', {
group = nvim_terminal_augroup,
desc = 'Default settings for :terminal buffers',
- callback = function()
- vim.bo.modifiable = false
- vim.bo.undolevels = -1
- vim.bo.scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback)
- vim.bo.textwidth = 0
+ callback = function(args)
+ vim.bo[args.buf].modifiable = false
+ vim.bo[args.buf].undolevels = -1
+ vim.bo[args.buf].scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback)
+ vim.bo[args.buf].textwidth = 0
vim.wo[0][0].wrap = false
vim.wo[0][0].list = false
vim.wo[0][0].number = false
@@ -555,6 +600,13 @@ do
winhl = winhl .. ','
end
vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC'
+
+ vim.keymap.set('n', '[[', function()
+ jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, -vim.v.count1)
+ end, { buffer = args.buf, desc = 'Jump [count] shell prompts backward' })
+ vim.keymap.set('n', ']]', function()
+ jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, vim.v.count1)
+ end, { buffer = args.buf, desc = 'Jump [count] shell prompts forward' })
end,
})
@@ -712,7 +764,7 @@ do
nested = true,
desc = "Update the value of 'background' automatically based on the terminal emulator's background color",
callback = function(args)
- local resp = args.data ---@type string
+ local resp = args.data.sequence ---@type string
local r, g, b = parseosc11(resp)
if r and g and b then
local rr = parsecolor(r)
@@ -788,7 +840,7 @@ do
group = group,
nested = true,
callback = function(args)
- local resp = args.data ---@type string
+ local resp = args.data.sequence ---@type string
local decrqss = resp:match('^\027P1%$r([%d;:]+)m$')
if decrqss then
@@ -834,9 +886,7 @@ do
-- terminal responds to the DECRQSS with the same SGR sequence that we
-- sent then the terminal supports truecolor.
local decrqss = '\027P$qm\027\\'
- if os.getenv('TMUX') then
- decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027'))
- end
+
-- Reset attributes first, as other code may have set attributes.
io.stdout:write(string.format('\027[0m\027[48;2;%d;%d;%dm%s', r, g, b, decrqss))
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 975f3fea4a..e29d8f1c30 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1142,6 +1142,21 @@ do
end
end
+--- @param inspect_strings boolean use vim.inspect() for strings
+function vim._print(inspect_strings, ...)
+ local msg = {}
+ for i = 1, select('#', ...) do
+ local o = select(i, ...)
+ if not inspect_strings and type(o) == 'string' then
+ table.insert(msg, o)
+ else
+ table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' }))
+ end
+ end
+ print(table.concat(msg, '\n'))
+ return ...
+end
+
--- "Pretty prints" the given arguments and returns them unmodified.
---
--- Example:
@@ -1155,17 +1170,7 @@ end
--- @param ... any
--- @return any # given arguments.
function vim.print(...)
- local msg = {}
- for i = 1, select('#', ...) do
- local o = select(i, ...)
- if type(o) == 'string' then
- table.insert(msg, o)
- else
- table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' }))
- end
- end
- print(table.concat(msg, '\n'))
- return ...
+ return vim._print(false, ...)
end
--- Translates keycodes.
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 15cdd8fc79..b890b64174 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -9,19 +9,16 @@ error('Cannot require a meta file')
vim.api = {}
---- @private
--- @param buffer integer
--- @param keys boolean
--- @param dot boolean
--- @return string
function vim.api.nvim__buf_debug_extmarks(buffer, keys, dot) end
---- @private
--- @param buffer integer
--- @return table<string,any>
function vim.api.nvim__buf_stats(buffer) end
---- @private
--- EXPERIMENTAL: this API may change in the future.
---
--- Sets info for the completion item at the given index. If the info text was shown in a window,
@@ -35,11 +32,9 @@ function vim.api.nvim__buf_stats(buffer) end
--- - bufnr: (number) buffer id in floating window
function vim.api.nvim__complete_set(index, opts) end
---- @private
--- @return string
function vim.api.nvim__get_lib_dir() end
---- @private
--- Find files in runtime directories
---
--- @param pat any[] pattern of files to search for
@@ -48,7 +43,6 @@ function vim.api.nvim__get_lib_dir() end
--- @return string[] # list of absolute paths to the found files
function vim.api.nvim__get_runtime(pat, all, opts) end
---- @private
--- Returns object given as argument.
---
--- This API function is used for testing. One should not rely on its presence
@@ -58,7 +52,6 @@ function vim.api.nvim__get_runtime(pat, all, opts) end
--- @return any # its argument.
function vim.api.nvim__id(obj) end
---- @private
--- Returns array given as argument.
---
--- This API function is used for testing. One should not rely on its presence
@@ -68,7 +61,6 @@ function vim.api.nvim__id(obj) end
--- @return any[] # its argument.
function vim.api.nvim__id_array(arr) end
---- @private
--- Returns dict given as argument.
---
--- This API function is used for testing. One should not rely on its presence
@@ -78,7 +70,6 @@ function vim.api.nvim__id_array(arr) end
--- @return table<string,any> # its argument.
function vim.api.nvim__id_dict(dct) end
---- @private
--- Returns floating-point value given as argument.
---
--- This API function is used for testing. One should not rely on its presence
@@ -88,7 +79,6 @@ function vim.api.nvim__id_dict(dct) end
--- @return number # its argument.
function vim.api.nvim__id_float(flt) end
---- @private
--- NB: if your UI doesn't use hlstate, this will not return hlstate first time.
--- @param grid integer
--- @param row integer
@@ -96,12 +86,10 @@ function vim.api.nvim__id_float(flt) end
--- @return any[]
function vim.api.nvim__inspect_cell(grid, row, col) end
---- @private
--- For testing. The condition in schar_cache_clear_if_full is hard to
--- reach, so this function can be used to force a cache clear in a test.
function vim.api.nvim__invalidate_glyph_cache() end
---- @private
--- EXPERIMENTAL: this API will change in the future.
---
--- Get the properties for namespace
@@ -110,7 +98,6 @@ function vim.api.nvim__invalidate_glyph_cache() end
--- @return vim.api.keyset.ns_opts # Map defining the namespace properties, see |nvim__ns_set()|
function vim.api.nvim__ns_get(ns_id) end
---- @private
--- EXPERIMENTAL: this API will change in the future.
---
--- Set some properties for namespace
@@ -120,7 +107,6 @@ function vim.api.nvim__ns_get(ns_id) end
--- - wins: a list of windows to be scoped in
function vim.api.nvim__ns_set(ns_id, opts) end
---- @private
--- EXPERIMENTAL: this API may change in the future.
---
--- Instruct Nvim to redraw various components.
@@ -148,21 +134,17 @@ function vim.api.nvim__ns_set(ns_id, opts) end
--- - tabline: Redraw the 'tabline'.
function vim.api.nvim__redraw(opts) end
---- @private
--- @return any[]
function vim.api.nvim__runtime_inspect() end
---- @private
--- @param path string
function vim.api.nvim__screenshot(path) end
---- @private
--- Gets internal stats.
---
--- @return table<string,any> # Map of various internal stats.
function vim.api.nvim__stats() end
---- @private
--- @param str string
--- @return any
function vim.api.nvim__unpack(str) end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 8df174a838..59e65b0585 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -1083,15 +1083,15 @@ vim.go.cia = vim.go.completeitemalign
--- match, e.g., what file it comes from.
---
--- noinsert Do not insert any text for a match until the user selects
---- a match from the menu. Only works in combination with
+--- a match from the menu. Only works in combination with
--- "menu" or "menuone". No effect if "longest" is present.
---
--- noselect Same as "noinsert", except that no menu item is
---- pre-selected. If both "noinsert" and "noselect" are
+--- pre-selected. If both "noinsert" and "noselect" are
--- present, "noselect" has precedence.
---
--- nosort Disable sorting of completion candidates based on fuzzy
---- scores when "fuzzy" is enabled. Candidates will appear
+--- scores when "fuzzy" is enabled. Candidates will appear
--- in their original order.
---
--- popup Show extra information about the currently selected
@@ -1102,7 +1102,7 @@ vim.go.cia = vim.go.completeitemalign
--- Preinsert the portion of the first candidate word that is
--- not part of the current completion leader and using the
--- `hl-ComplMatchIns` highlight group. In order for it to
---- work, "fuzzy" must not bet set and "menuone" must be set.
+--- work, "fuzzy" must not be set and "menuone" must be set.
---
--- preview Show extra information about the currently selected
--- completion in the preview window. Only works in
@@ -4640,7 +4640,7 @@ vim.go.pm = vim.go.patchmode
--- ```
--- - A directory name may end in a ':' or '/'.
--- - Environment variables are expanded `:set_env`.
---- - When using `netrw.vim` URLs can be used. For example, adding
+--- - When using `netrw` URLs can be used. For example, adding
--- "https://www.vim.org" will make ":find index.html" work.
--- - Search upwards and downwards in a directory tree using "*", "**" and
--- ";". See `file-searching` for info and syntax.
@@ -7691,7 +7691,10 @@ vim.go.wmnu = vim.go.wildmenu
--- "lastused" When completing buffer names and more than one buffer
--- matches, sort buffers by time last used (other than
--- the current buffer).
---- When there is only a single match, it is fully completed in all cases.
+--- "noselect" Do not pre-select first menu item and start 'wildmenu'
+--- if it is enabled.
+--- When there is only a single match, it is fully completed in all cases
+--- except when "noselect" is present.
---
--- Examples of useful colon-separated values:
--- "longest:full" Like "longest", but also start 'wildmenu' if it is
@@ -7729,7 +7732,17 @@ vim.go.wmnu = vim.go.wildmenu
--- ```vim
--- set wildmode=longest,list
--- ```
---- Complete longest common string, then list alternatives.
+--- Complete longest common string, then list alternatives
+---
+--- ```vim
+--- set wildmode=noselect:full
+--- ```
+--- Display 'wildmenu' without completing, then each full match
+---
+--- ```vim
+--- set wildmode=noselect:lastused,full
+--- ```
+--- Same as above, but sort buffers by time last used.
--- More info here: `cmdline-completion`.
---
--- @type string
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 26f0011e82..4749f94b1b 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1757,8 +1757,7 @@ local function render_virtual_lines(namespace, bufnr, diagnostics)
string.rep(
' ',
-- +1 because indexing starts at 0 in one API but at 1 in the other.
- -- -1 for non-first lines, since the previous column was already drawn.
- distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col) - 1
+ distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col)
),
})
else
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index c47ce5e761..2c058bc6bf 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -198,6 +198,7 @@ local extension = {
abap = 'abap',
abc = 'abc',
abl = 'abel',
+ abnf = 'abnf',
wrm = 'acedb',
ads = 'ada',
ada = 'ada',
@@ -1206,6 +1207,7 @@ local extension = {
tl = 'teal',
templ = 'templ',
tmpl = 'template',
+ tera = 'tera',
ti = 'terminfo',
dtx = 'tex',
ltx = 'tex',
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 47f41b43aa..2da591e9bb 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -1129,6 +1129,7 @@ local function on_code_action_results(results, opts)
if not choice then
return
end
+
-- textDocument/codeAction can return either Command[] or CodeAction[]
--
-- CodeAction
@@ -1140,15 +1141,22 @@ local function on_code_action_results(results, opts)
-- title: string
-- command: string
-- arguments?: any[]
- --
+
local client = assert(lsp.get_client_by_id(choice.ctx.client_id))
local action = choice.action
local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number')
- if not action.edit and client:supports_method(ms.codeAction_resolve) then
+ -- Only code actions are resolved, so if we have a command, just apply it.
+ if type(action.title) == 'string' and type(action.command) == 'string' then
+ apply_action(action, client, choice.ctx)
+ return
+ end
+
+ if not action.edit or not action.command and client:supports_method(ms.codeAction_resolve) then
client:request(ms.codeAction_resolve, action, function(err, resolved_action)
if err then
- if action.command then
+ -- If resolve fails, try to apply the edit/command from the original code action.
+ if action.edit or action.command then
apply_action(action, client, choice.ctx)
else
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 7975006f9d..feff9adbd0 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -424,7 +424,7 @@ function protocol.make_client_capabilities()
isPreferredSupport = true,
dataSupport = true,
resolveSupport = {
- properties = { 'edit' },
+ properties = { 'edit', 'command' },
},
},
codeLens = {
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 1219f71427..ef177e903f 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1656,7 +1656,9 @@ function M.open_floating_preview(contents, syntax, opts)
if not opts.height then
-- Reduce window height if TS highlighter conceals code block backticks.
local conceal_height = api.nvim_win_text_height(floating_winnr, {}).all
- api.nvim_win_set_height(floating_winnr, conceal_height)
+ if conceal_height < api.nvim_win_get_height(floating_winnr) then
+ api.nvim_win_set_height(floating_winnr, conceal_height)
+ end
end
end
diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua
index 4aa41bba9b..2789aacb90 100644
--- a/runtime/lua/vim/termcap.lua
+++ b/runtime/lua/vim/termcap.lua
@@ -34,7 +34,7 @@ function M.query(caps, cb)
local id = vim.api.nvim_create_autocmd('TermResponse', {
nested = true,
callback = function(args)
- local resp = args.data ---@type string
+ local resp = args.data.sequence ---@type string
local k, rest = resp:match('^\027P1%+r(%x+)(.*)$')
if k and rest then
local cap = vim.text.hexdecode(k)
@@ -71,11 +71,6 @@ function M.query(caps, cb)
local query = string.format('\027P+q%s\027\\', table.concat(encoded, ';'))
- -- If running in tmux, wrap with the passthrough sequence
- if os.getenv('TMUX') then
- query = string.format('\027Ptmux;%s\027\\', query:gsub('\027', '\027\027'))
- end
-
io.stdout:write(query)
timer:start(1000, 0, function()
diff --git a/runtime/lua/vim/ui/clipboard/osc52.lua b/runtime/lua/vim/ui/clipboard/osc52.lua
index 50afbe63a5..73f64c9743 100644
--- a/runtime/lua/vim/ui/clipboard/osc52.lua
+++ b/runtime/lua/vim/ui/clipboard/osc52.lua
@@ -25,7 +25,7 @@ function M.paste(reg)
local contents = nil
local id = vim.api.nvim_create_autocmd('TermResponse', {
callback = function(args)
- local resp = args.data ---@type string
+ local resp = args.data.sequence ---@type string
local encoded = resp:match('\027%]52;%w?;([A-Za-z0-9+/=]*)')
if encoded then
contents = vim.base64.decode(encoded)
diff --git a/runtime/makemenu.vim b/runtime/makemenu.vim
index 01f214cfa0..3d27bfc551 100644
--- a/runtime/makemenu.vim
+++ b/runtime/makemenu.vim
@@ -1,6 +1,6 @@
" Script to define the syntax menu in synmenu.vim
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2023 Aug 10
+" Last Change: 2025 Mar 09
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" This is used by "make menu" in the src directory.
@@ -602,6 +602,7 @@ SynMenu T.Tcl/Tk:tcl
SynMenu T.TealInfo:tli
SynMenu T.Telix\ Salt:tsalt
SynMenu T.Termcap/Printcap:ptcap
+SynMenu T.Tera:tera
SynMenu T.Terminfo:terminfo
SynMenu T.Tera\ Term:teraterm
SynMenu T.TeX.TeX/LaTeX:tex
diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim
index 13d1b6824f..079cc668f0 100644
--- a/runtime/plugin/matchparen.vim
+++ b/runtime/plugin/matchparen.vim
@@ -1,6 +1,6 @@
" Vim plugin for showing matching parens
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2024 May 18
+" Last Change: 2025 Mar 08
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" Exit quickly when:
@@ -114,6 +114,17 @@ func s:Highlight_Matching_Pair()
\ .. 'string\|character\|singlequote\|escape\|symbol\|comment'
\ .. "') != -1"
else
+ " do not attempt to match when the syntax item where the cursor is
+ " indicates there does not exist a matching parenthesis, e.g. for shells
+ " case statement: "case $var in foobar)"
+ "
+ " add the check behind a filetype check, so it only needs to be
+ " evaluated for certain filetypes
+ if ['sh']->index(&filetype) >= 0 &&
+ \ synstack(".", col("."))->indexof({_, id -> synIDattr(id, "name")
+ \ =~? "shSnglCase"}) >= 0
+ return
+ endif
" Build an expression that detects whether the current cursor position is
" in certain syntax types (string, comment, etc.), for use as
" searchpairpos()'s skip argument.
diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim
index edc6eabd1d..6a3bb8eaf4 100644
--- a/runtime/synmenu.vim
+++ b/runtime/synmenu.vim
@@ -582,26 +582,27 @@ an 50.150.190 &Syntax.T.TealInfo :cal SetSyn("tli")<CR>
an 50.150.200 &Syntax.T.Telix\ Salt :cal SetSyn("tsalt")<CR>
an 50.150.210 &Syntax.T.Termcap/Printcap :cal SetSyn("ptcap")<CR>
an 50.150.220 &Syntax.T.Terminfo :cal SetSyn("terminfo")<CR>
-an 50.150.230 &Syntax.T.Tera\ Term :cal SetSyn("teraterm")<CR>
-an 50.150.240 &Syntax.T.TeX.TeX/LaTeX :cal SetSyn("tex")<CR>
-an 50.150.250 &Syntax.T.TeX.plain\ TeX :cal SetSyn("plaintex")<CR>
-an 50.150.260 &Syntax.T.TeX.Initex :cal SetSyn("initex")<CR>
-an 50.150.270 &Syntax.T.TeX.ConTeXt :cal SetSyn("context")<CR>
-an 50.150.280 &Syntax.T.TeX.TeX\ configuration :cal SetSyn("texmf")<CR>
-an 50.150.290 &Syntax.T.TeX.Texinfo :cal SetSyn("texinfo")<CR>
-an 50.150.300 &Syntax.T.TF\ mud\ client :cal SetSyn("tf")<CR>
-an 50.150.310 &Syntax.T.Tidy\ configuration :cal SetSyn("tidy")<CR>
-an 50.150.320 &Syntax.T.Tilde :cal SetSyn("tilde")<CR>
-an 50.150.330 &Syntax.T.Tmux\ configuration :cal SetSyn("tmux")<CR>
-an 50.150.340 &Syntax.T.TPP :cal SetSyn("tpp")<CR>
-an 50.150.350 &Syntax.T.Trasys\ input :cal SetSyn("trasys")<CR>
-an 50.150.360 &Syntax.T.Treetop :cal SetSyn("treetop")<CR>
-an 50.150.370 &Syntax.T.Trustees :cal SetSyn("trustees")<CR>
-an 50.150.380 &Syntax.T.TSS.Command\ Line :cal SetSyn("tsscl")<CR>
-an 50.150.390 &Syntax.T.TSS.Geometry :cal SetSyn("tssgm")<CR>
-an 50.150.400 &Syntax.T.TSS.Optics :cal SetSyn("tssop")<CR>
-an 50.150.410 &Syntax.T.Typescript :cal SetSyn("typescript")<CR>
-an 50.150.420 &Syntax.T.TypescriptReact :cal SetSyn("typescriptreact")<CR>
+an 50.150.230 &Syntax.T.Tera :cal SetSyn("tera")<CR>
+an 50.150.240 &Syntax.T.Tera\ Term :cal SetSyn("teraterm")<CR>
+an 50.150.250 &Syntax.T.TeX.TeX/LaTeX :cal SetSyn("tex")<CR>
+an 50.150.260 &Syntax.T.TeX.plain\ TeX :cal SetSyn("plaintex")<CR>
+an 50.150.270 &Syntax.T.TeX.Initex :cal SetSyn("initex")<CR>
+an 50.150.280 &Syntax.T.TeX.ConTeXt :cal SetSyn("context")<CR>
+an 50.150.290 &Syntax.T.TeX.TeX\ configuration :cal SetSyn("texmf")<CR>
+an 50.150.300 &Syntax.T.TeX.Texinfo :cal SetSyn("texinfo")<CR>
+an 50.150.310 &Syntax.T.TF\ mud\ client :cal SetSyn("tf")<CR>
+an 50.150.320 &Syntax.T.Tidy\ configuration :cal SetSyn("tidy")<CR>
+an 50.150.330 &Syntax.T.Tilde :cal SetSyn("tilde")<CR>
+an 50.150.340 &Syntax.T.Tmux\ configuration :cal SetSyn("tmux")<CR>
+an 50.150.350 &Syntax.T.TPP :cal SetSyn("tpp")<CR>
+an 50.150.360 &Syntax.T.Trasys\ input :cal SetSyn("trasys")<CR>
+an 50.150.370 &Syntax.T.Treetop :cal SetSyn("treetop")<CR>
+an 50.150.380 &Syntax.T.Trustees :cal SetSyn("trustees")<CR>
+an 50.150.390 &Syntax.T.TSS.Command\ Line :cal SetSyn("tsscl")<CR>
+an 50.150.400 &Syntax.T.TSS.Geometry :cal SetSyn("tssgm")<CR>
+an 50.150.410 &Syntax.T.TSS.Optics :cal SetSyn("tssop")<CR>
+an 50.150.420 &Syntax.T.Typescript :cal SetSyn("typescript")<CR>
+an 50.150.430 &Syntax.T.TypescriptReact :cal SetSyn("typescriptreact")<CR>
an 50.160.100 &Syntax.UV.Udev\ config :cal SetSyn("udevconf")<CR>
an 50.160.110 &Syntax.UV.Udev\ permissions :cal SetSyn("udevperm")<CR>
an 50.160.120 &Syntax.UV.Udev\ rules :cal SetSyn("udevrules")<CR>
diff --git a/runtime/syntax/abnf.vim b/runtime/syntax/abnf.vim
new file mode 100644
index 0000000000..13459eb9f8
--- /dev/null
+++ b/runtime/syntax/abnf.vim
@@ -0,0 +1,33 @@
+" Vim compiler file
+" Language: abnf
+" Maintainer: A4-Tacks <wdsjxhno1001@163.com>
+" Last Change: 2025 Mar 05
+" Upstream: https://github.com/A4-Tacks/abnf.vim
+
+" Implementing RFC-5234, RFC-7405
+
+if exists('b:current_syntax')
+ finish
+endif
+
+syn case ignore
+
+syn match abnfError /[<>"]/
+syn match abnfComment /;.*/
+syn match abnfOption /[[/\]]/
+syn region abnfString start=/\(%[si]\)\="/ end=/"/ oneline
+syn region abnfProse start=/</ end=/>/ oneline
+syn match abnfNumVal /\v\%b[01]+%(%(\.[01]+)+|-[01]+)=>/
+syn match abnfNumVal /\v\%d\d+%(%(\.\d+)+|-\d+)=>/
+syn match abnfNumVal /\v\%x[0-9a-f]+%(%(\.[0-9a-f]+)+|-[0-9a-f]+)=>/
+syn match abnfRepeat /\v%(%(<\d+)=\*\d*|<\d+ =)\ze[^ \t\r\n0-9*/)\]]/
+
+hi def link abnfError Error
+hi def link abnfComment Comment
+hi def link abnfOption PreProc
+hi def link abnfString String
+hi def link abnfProse String
+hi def link abnfNumVal Number
+hi def link abnfRepeat Repeat
+
+" vim:noet:ts=8:sts=8:nowrap
diff --git a/runtime/syntax/tera.vim b/runtime/syntax/tera.vim
new file mode 100644
index 0000000000..922b9c9752
--- /dev/null
+++ b/runtime/syntax/tera.vim
@@ -0,0 +1,96 @@
+" Vim syntax file
+" Language: Tera
+" Maintainer: Muntasir Mahmud <muntasir.joypurhat@gmail.com>
+" Last Change: 2025 Mar 09
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" Detect the underlying language based on filename pattern
+" For files like file.html.tera, we want to load html syntax
+let s:filename = expand("%:t")
+let s:dotpos = strridx(s:filename, '.', strridx(s:filename, '.tera') - 1)
+let s:underlying_filetype = ""
+
+if s:dotpos != -1
+ let s:underlying_ext = s:filename[s:dotpos+1:strridx(s:filename, '.tera')-1]
+ if s:underlying_ext != "" && s:underlying_ext != "tera"
+ let s:underlying_filetype = s:underlying_ext
+ endif
+endif
+
+" Load the underlying language syntax if detected
+if s:underlying_filetype != ""
+ execute "runtime! syntax/" . s:underlying_filetype . ".vim"
+ unlet! b:current_syntax
+else
+ " Default to HTML if no specific language detected
+ runtime! syntax/html.vim
+ unlet! b:current_syntax
+endif
+
+" Tera comment blocks: {# comment #}
+syn region teraCommentBlock start="{#" end="#}" contains=@Spell containedin=cssDefinition,cssStyle,htmlHead,htmlTitle
+
+" Tera statements: {% if condition %}
+syn region teraStatement start="{%" end="%}" contains=teraKeyword,teraString,teraNumber,teraFunction,teraBoolean,teraFilter,teraOperator containedin=cssDefinition,cssStyle,htmlHead,htmlTitle
+
+" Tera expressions: {{ variable }}
+syn region teraExpression start="{{" end="}}" contains=teraString,teraNumber,teraFunction,teraBoolean,teraFilter,teraOperator,teraIdentifier containedin=cssDefinition,cssStyle,htmlHead,htmlTitle
+
+" Special handling for raw blocks - content inside shouldn't be processed
+syn region teraRawBlock start="{% raw %}" end="{% endraw %}" contains=TOP,teraCommentBlock,teraStatement,teraExpression
+
+" Control structure keywords
+syn keyword teraKeyword contained if else elif endif for endfor in macro endmacro
+syn keyword teraKeyword contained block endblock extends include import set endset
+syn keyword teraKeyword contained break continue filter endfilter raw endraw with endwith
+
+" Identifiers - define before operators for correct priority
+syn match teraIdentifier contained "\<\w\+\>"
+
+" Operators used in expressions and statements
+syn match teraOperator contained "==\|!=\|>=\|<=\|>\|<\|+\|-\|*\|/"
+syn match teraOperator contained "{\@<!%}\@!" " Match % but not when part of {% or %}
+syn keyword teraOperator contained and or not is as
+
+" Functions and filters
+syn match teraFunction contained "\<\w\+\ze("
+syn match teraFilter contained "|\_s*\w\+"
+
+" String literals - both single and double quoted
+syn region teraString contained start=+"+ skip=+\\"+ end=+"+ contains=@Spell
+syn region teraString contained start=+'+ skip=+\\'+ end=+'+ contains=@Spell
+
+" Numeric literals - both integer and float
+syn match teraNumber contained "\<\d\+\>"
+syn match teraNumber contained "\<\d\+\.\d\+\>"
+
+" Boolean values
+syn keyword teraBoolean contained true false
+
+" Highlighting links
+hi def link teraCommentBlock Comment
+hi def link teraKeyword Statement
+hi def link teraOperator Operator
+hi def link teraFunction Function
+hi def link teraIdentifier Identifier
+hi def link teraString String
+hi def link teraNumber Number
+hi def link teraBoolean Boolean
+hi def link teraFilter PreProc
+
+" Special highlighting for blocks and expressions
+hi def link teraStatement PreProc
+hi def link teraExpression PreProc
+
+" Clean up script-local variables
+unlet s:filename
+unlet s:dotpos
+if exists("s:underlying_ext")
+ unlet s:underlying_ext
+endif
+unlet s:underlying_filetype
+
+let b:current_syntax = "tera"
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index de0a1228e2..7fe22190b7 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -711,9 +711,9 @@ syn match vimCmplxRepeat '[^a-zA-Z_/\\()]q[0-9a-zA-Z"]\>'lc=1
syn match vimCmplxRepeat '@[0-9a-z".=@:]\ze\($\|[^a-zA-Z]\>\)'
" Set command and associated set-options (vimOptions) with comment {{{2
-syn match vimSet "\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skipwhite nextgroup=vimSetBang,vimSetRegion
-syn region vimSetRegion contained start="\S" skip=+\\\\\|\\|\|\n\s*\\\|\n\s*["#]\\ + matchgroup=vimCmdSep end="|" end="$" matchgroup=vimNotation end="<[cC][rR]>" keepend contains=@vimComment,@vimContinue,vimErrSetting,vimOption,vimSetAll,vimSetTermcap
-syn region vimSetEqual contained matchgroup=vimOper start="[=:]\|[-+^]=" skip=+\\\\\|\\|\|\\\s\|\n\s*\\\|\n\s*["#]\\ \|^\s*\\\|^\s*["#]\\ + matchgroup=vimCmdSep end="|" end="\ze\s" end="$" contains=@vimContinue,vimCtrlChar,vimEnvvar,vimNotation,vimSetSep
+syn match vimSet "\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skipwhite nextgroup=vimSetBang,vimSetArgs
+syn region vimSetArgs contained start="\S" skip=+\\|\|\n\s*\\\|\n\s*["#]\\ + matchgroup=vimCmdSep end="|" end="$" matchgroup=vimNotation end="<[cC][rR]>" keepend contains=@vimComment,@vimContinue,vimErrSetting,vimOption,vimSetAll,vimSetTermcap
+syn region vimSetEqual contained matchgroup=vimOper start="[=:]\|[-+^]=" skip=+\\|\|\\\s\|\n\s*\\\|\n\s*["#]\\ \|^\s*\\\|^\s*["#]\\ + matchgroup=vimCmdSep end="|" end="\ze\s" end="$" contains=@vimContinue,vimCtrlChar,vimEnvvar,vimNotation,vimSetSep
syn match vimSetBang contained "\a\@1<=!" skipwhite nextgroup=vimSetAll,vimSetTermcap
syn keyword vimSetAll contained all nextgroup=vimSetMod
syn keyword vimSetTermcap contained termcap
@@ -835,15 +835,17 @@ syn keyword vimMap mapc[lear] skipwhite nextgroup=vimMapBang,vimMapMod
syn keyword vimUnmap cu[nmap] iu[nmap] lu[nmap] nun[map] ou[nmap] sunm[ap] tunma[p] vu[nmap] xu[nmap] skipwhite nextgroup=vimMapMod,vimMapLhs
syn keyword vimUnmap unm[ap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs
-syn match vimMapLhs contained "\%(.\|\S\)\+" contains=vimCtrlChar,vimNotation skipwhite nextgroup=vimMapRhs
-syn match vimMapLhs contained "\%(.\|\S\)\+\ze\s*$" contains=vimCtrlChar,vimNotation skipwhite skipnl nextgroup=vimMapRhsContinue
+syn match vimMapLhs contained "\%(.\|\S\)\+" contains=vimCtrlChar,vimNotation,vimMapLeader skipwhite nextgroup=vimMapRhs
+syn match vimMapLhs contained "\%(.\|\S\)\+\ze\s*$" contains=vimCtrlChar,vimNotation,vimMapLeader skipwhite skipnl nextgroup=vimMapRhsContinue
syn match vimMapBang contained "\a\@1<=!" skipwhite nextgroup=vimMapMod,vimMapLhs
-syn match vimMapMod contained "\%#=1\c<\(buffer\|expr\|\(local\)\=leader\|nowait\|plug\|script\|sid\|unique\|silent\)\+>" contains=vimMapModKey,vimMapModErr skipwhite nextgroup=vimMapMod,vimMapLhs
-syn region vimMapRhs contained start="\S" skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation skipnl nextgroup=vimMapRhsContinue
+syn match vimMapMod contained "\%#=1<\%(buffer\|expr\|nowait\|script\|silent\|special\|unique\)\+>" contains=vimMapModKey,vimMapModErr skipwhite nextgroup=vimMapMod,vimMapLhs
+syn region vimMapRhs contained start="\S" skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation,vimMapLeader skipnl nextgroup=vimMapRhsContinue
" assume a continuation comment introduces the RHS
-syn region vimMapRhsContinue contained start=+^\s*\%(\\\|"\\ \)+ skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation
+syn region vimMapRhsContinue contained start=+^\s*\%(\\\|"\\ \)+ skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation,vimMapLeader
+syn match vimMapLeader contained "\%#=1\c<\%(local\)\=leader>" contains=vimMapLeaderKey
+syn keyword vimMapModKey contained buffer expr nowait script silent special unique
syn case ignore
-syn keyword vimMapModKey contained buffer expr leader localleader nowait plug script sid silent unique
+syn keyword vimMapLeaderKey contained leader localleader
syn case match
" Menus: {{{2
@@ -909,7 +911,7 @@ syn match vimUsrCmd '^\s*\zs\u\%(\w*\)\@>\%([(#[]\|\s\+\%([-+*/%]\=\|\.\.\)=\)\@
" Vim user commands
" Compiler plugins
-syn match vimCompilerSet "\<CompilerSet\>" skipwhite nextgroup=vimSetRegion
+syn match vimCompilerSet "\<CompilerSet\>" skipwhite nextgroup=vimSetArgs
" runtime/makemenu.vim
syn match vimSynMenu "\<SynMenu\>" skipwhite nextgroup=vimSynMenuPath
@@ -1494,6 +1496,8 @@ if !exists("skip_vim_syntax_inits")
hi def link vimLetRegister Special
hi def link vimLineComment vimComment
hi def link vimMapBang vimBang
+ hi def link vimMapLeader vimBracket
+ hi def link vimMapLeaderKey vimNotation
hi def link vimMapModKey vimFuncSID
hi def link vimMapMod vimBracket
hi def link vimMap vimCommand
diff --git a/scripts/lintdoc.lua b/scripts/lintdoc.lua
index 78cf9fed72..d246c51176 100755
--- a/scripts/lintdoc.lua
+++ b/scripts/lintdoc.lua
@@ -10,8 +10,10 @@
print('Running lintdoc ...')
--- gen_help_html requires :helptags to be generated on $VIMRUNTIME/doc
--- :helptags checks for duplicate tags.
+-- gen_help_html.lua requires helptags to be generated in $VIMRUNTIME/doc.
+-- :helptags also checks for duplicate tags.
+-- 🤢 Load netrw so its tags are generated by :helptags.
+vim.cmd [[ packadd netrw ]]
vim.cmd [[ helptags ALL ]]
require('src.gen.gen_help_html').run_validate()
diff --git a/src/gen/gen_eval_files.lua b/src/gen/gen_eval_files.lua
index f99f6bafbd..b5351cb3a4 100755
--- a/src/gen/gen_eval_files.lua
+++ b/src/gen/gen_eval_files.lua
@@ -371,10 +371,6 @@ end
local function render_api_meta(_f, fun, write)
write('')
- if vim.startswith(fun.name, 'nvim__') then
- write('--- @private')
- end
-
if fun.deprecated then
write('--- @deprecated')
end
diff --git a/src/gen/gen_events.lua b/src/gen/gen_events.lua
index 77f766bb28..020599301c 100644
--- a/src/gen/gen_events.lua
+++ b/src/gen/gen_events.lua
@@ -3,9 +3,14 @@ local names_file = arg[2]
local auevents = require('nvim.auevents')
local events = auevents.events
+local aliases = auevents.aliases
-local enum_tgt = io.open(fileio_enum_file, 'w')
-local names_tgt = io.open(names_file, 'w')
+--- @type string[]
+local names = vim.tbl_keys(vim.tbl_extend('error', events, aliases))
+table.sort(names)
+
+local enum_tgt = assert(io.open(fileio_enum_file, 'w'))
+local names_tgt = assert(io.open(names_file, 'w'))
enum_tgt:write([[
// IWYU pragma: private, include "nvim/autocmd_defs.h"
@@ -18,23 +23,25 @@ static const struct event_name {
int event;
} event_names[] = {]])
-local aliases = 0
-for i, event in ipairs(events) do
- enum_tgt:write(('\n EVENT_%s = %u,'):format(event[1]:upper(), i + aliases - 1))
+for i, name in ipairs(names) do
+ enum_tgt:write(('\n EVENT_%s = %u,'):format(name:upper(), i - 1))
+ local pref_name = aliases[name] ~= nil and aliases[name] or name
+ local win_local = events[pref_name]
+ assert(win_local ~= nil)
-- Events with positive keys aren't allowed in 'eventignorewin'.
- local event_int = ('%sEVENT_%s'):format(event[3] and '-' or '', event[1]:upper())
- names_tgt:write(('\n {%u, "%s", %s},'):format(#event[1], event[1], event_int))
- for _, alias in ipairs(event[2]) do
- aliases = aliases + 1
- names_tgt:write(('\n {%u, "%s", %s},'):format(#alias, alias, event_int))
- enum_tgt:write(('\n EVENT_%s = %u,'):format(alias:upper(), i + aliases - 1))
- end
- if i == #events then -- Last item.
- enum_tgt:write(('\n NUM_EVENTS = %u,'):format(i + aliases))
- end
+ names_tgt:write(
+ ('\n [EVENT_%s] = {%u, "%s", %sEVENT_%s},'):format(
+ name:upper(),
+ #name,
+ name,
+ win_local and '-' or '',
+ pref_name:upper()
+ )
+ )
end
-names_tgt:write('\n {0, NULL, (event_T)0},\n};\n')
+enum_tgt:write(('\n NUM_EVENTS = %u,'):format(#names))
+names_tgt:write('\n [NUM_EVENTS] = {0, NULL, (event_T)0},\n};\n')
names_tgt:write('\nstatic AutoCmdVec autocmds[NUM_EVENTS] = { 0 };\n')
names_tgt:close()
diff --git a/src/gen/gen_help_html.lua b/src/gen/gen_help_html.lua
index d4766d84a1..0d98d9e1b1 100644
--- a/src/gen/gen_help_html.lua
+++ b/src/gen/gen_help_html.lua
@@ -778,6 +778,7 @@ local function parse_buf(fname, text, parser_path)
vim.treesitter.language.add('vimdoc', { path = parser_path })
end
local lang_tree = assert(vim.treesitter.get_parser(buf, nil, { error = false }))
+ lang_tree:parse()
return lang_tree, buf
end
@@ -1397,6 +1398,9 @@ function M.validate(help_dir, include, parser_path)
local files_to_errors = {} ---@type table<string, string[]>
ensure_runtimepath()
tagmap = get_helptags(vim.fs.normalize(help_dir))
+ --- XXX: Append tags from netrw, until we remove it...
+ local netrwtags = get_helptags(vim.fs.normalize('$VIMRUNTIME/pack/dist/opt/netrw/doc/'))
+ tagmap = vim.tbl_extend('keep', tagmap, netrwtags)
helpfiles = get_helpfiles(help_dir, include)
parser_path = parser_path and vim.fs.normalize(parser_path) or nil
@@ -1424,7 +1428,7 @@ function M.validate(help_dir, include, parser_path)
}
end
---- Validates vimdoc files on $VIMRUNTIME. and print human-readable error messages if fails.
+--- Validates vimdoc files in $VIMRUNTIME, and prints error messages on failure.
---
--- If this fails, try these steps (in order):
--- 1. Fix/cleanup the :help docs.
@@ -1464,7 +1468,7 @@ function M.test_gen(help_dir)
print('doc path = ' .. vim.uv.fs_realpath(help_dir))
-- Because gen() is slow (~30s), this test is limited to a few files.
- local input = { 'help.txt', 'index.txt', 'nvim.txt' }
+ local input = { 'api.txt', 'index.txt', 'nvim.txt' }
local rv = M.gen(help_dir, tmpdir, input)
eq(#input, #rv.helpfiles)
eq(0, rv.err_count, 'parse errors in :help docs')
diff --git a/src/gen/gen_keycodes.lua b/src/gen/gen_keycodes.lua
new file mode 100644
index 0000000000..53f0c24e58
--- /dev/null
+++ b/src/gen/gen_keycodes.lua
@@ -0,0 +1,86 @@
+local names_file = arg[1]
+
+local hashy = require('gen.hashy')
+local keycodes = require('nvim.keycodes')
+
+local keycode_names = keycodes.names
+
+--- @type table<string,integer>
+--- Maps lower-case key names to their original indexes.
+local name_orig_idx = {}
+
+--- @type table<string,integer>
+--- Maps keys to the original indexes of their preferred names.
+local key_orig_idx = {}
+
+--- @type [string, string][]
+--- When multiple keys have the same name (e.g. TAB and K_TAB), only the first one
+--- is added to the two tables above, and the other keys are added here.
+local extra_keys = {}
+
+for i, keycode in ipairs(keycode_names) do
+ local key = keycode[1]
+ local name = keycode[2]
+ local name_lower = name:lower()
+ if name_orig_idx[name_lower] == nil then
+ name_orig_idx[name_lower] = i
+ if key_orig_idx[key] == nil then
+ key_orig_idx[key] = i
+ end
+ else
+ table.insert(extra_keys, keycode)
+ end
+end
+
+local hashorder = vim.tbl_keys(name_orig_idx)
+table.sort(hashorder)
+local hashfun
+hashorder, hashfun = hashy.hashy_hash('get_special_key_code', hashorder, function(idx)
+ return 'key_names_table[' .. idx .. '].name.data'
+end, true)
+
+--- @type table<string,integer>
+--- Maps keys to the (after hash) indexes of the entries with preferred names.
+local key_hash_idx = {}
+
+for i, lower_name in ipairs(hashorder) do
+ local orig_idx = name_orig_idx[lower_name]
+ local key = keycode_names[orig_idx][1]
+ if key_orig_idx[key] == orig_idx then
+ key_hash_idx[key] = i
+ end
+end
+
+local names_tgt = assert(io.open(names_file, 'w'))
+names_tgt:write([[
+static const struct key_name_entry {
+ int key; ///< Special key code or ascii value
+ String name; ///< Name of key
+ const String *pref_name; ///< Pointer to preferred key name
+ ///< (may be NULL or point to the name in another entry)
+} key_names_table[] = {]])
+
+for i, lower_name in ipairs(hashorder) do
+ local keycode = keycode_names[name_orig_idx[lower_name]]
+ local key = keycode[1]
+ local name = keycode[2]
+ local pref_idx = key_hash_idx[key]
+ names_tgt:write(
+ ('\n {%s, {"%s", %u}, %s},'):format(
+ key,
+ name,
+ #name,
+ pref_idx == i and 'NULL' or ('&key_names_table[%u].name'):format(pref_idx - 1)
+ )
+ )
+end
+
+for _, keycode in ipairs(extra_keys) do
+ local key = keycode[1]
+ local name = keycode[2]
+ names_tgt:write(('\n {%s, {"%s", %u}, NULL},'):format(key, name, #name))
+end
+
+names_tgt:write('\n};\n\n')
+names_tgt:write('static ' .. hashfun)
+names_tgt:close()
diff --git a/src/gen/gen_vimvim.lua b/src/gen/gen_vimvim.lua
index d2b1f48a4c..6545483bf3 100644
--- a/src/gen/gen_vimvim.lua
+++ b/src/gen/gen_vimvim.lua
@@ -113,19 +113,12 @@ w('\nsyn case ignore')
local vimau_start = 'syn keyword vimAutoEvent contained '
w('\n\n' .. vimau_start)
-for _, au in ipairs(auevents.events) do
- if not auevents.nvim_specific[au[1]] then
+for au, _ in vim.spairs(vim.tbl_extend('error', auevents.events, auevents.aliases)) do
+ if not auevents.nvim_specific[au] then
if lld.line_length > 850 then
w('\n' .. vimau_start)
end
- w(' ' .. au[1])
- for _, alias in ipairs(au[2]) do
- if lld.line_length > 850 then
- w('\n' .. vimau_start)
- end
- -- au[1] is aliased to alias
- w(' ' .. alias)
- end
+ w(' ' .. au)
end
end
diff --git a/src/gen/hashy.lua b/src/gen/hashy.lua
index 74b7655324..cd0e90830d 100644
--- a/src/gen/hashy.lua
+++ b/src/gen/hashy.lua
@@ -54,7 +54,7 @@ function M.build_pos_hash(strings)
return len_pos_buckets, maxlen, worst_buck_size
end
-function M.switcher(put, tab, maxlen, worst_buck_size)
+function M.switcher(put, tab, maxlen, worst_buck_size, icase)
local neworder = {} --- @type string[]
put ' switch (len) {\n'
local bucky = worst_buck_size > 1
@@ -72,7 +72,11 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
local startidx = #neworder
vim.list_extend(neworder, buck)
local endidx = #neworder
- put(" case '" .. c .. "': ")
+ if icase and c:upper() ~= c:lower() then
+ put((" case '%s': case '%s': "):format(c:upper(), c:lower()))
+ else
+ put((" case '%s': "):format(c))
+ end
if len == 1 then
put('return ' .. startidx .. ';\n')
else
@@ -102,7 +106,9 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
return neworder
end
-function M.hashy_hash(name, strings, access)
+--- @param icase? boolean generate a case-insensitive hash function.
+--- `strings` must not have mixed case when using this.
+function M.hashy_hash(name, strings, access, icase)
local stats = {}
local put = function(str)
table.insert(stats, str)
@@ -116,27 +122,27 @@ function M.hashy_hash(name, strings, access)
else
put(' int low = -1;\n')
end
- local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
+ local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size, icase)
if maxlen == 1 then
put([[
return -1;
]])
elseif worst_buck_size > 1 then
- put([[
+ put(([[
for (int i = low; i < high; i++) {
- if (!memcmp(str, ]] .. access('i') .. [[, len)) {
+ if (!%s(str, %s, len)) {
return i;
}
}
return -1;
-]])
+]]):format(icase and 'vim_strnicmp_asc' or 'memcmp', access('i')))
else
- put([[
- if (low < 0 || memcmp(str, ]] .. access('low') .. [[, len)) {
+ put(([[
+ if (low < 0 || %s(str, %s, len)) {
return -1;
}
return low;
-]])
+]]):format(icase and 'vim_strnicmp_asc' or 'memcmp', access('low')))
end
put '}\n\n'
return neworder, table.concat(stats)
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 36bcd5fbce..355c3c2e6b 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -304,6 +304,7 @@ set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua)
set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua)
set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua)
set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua)
+set(KEYCODES_GENERATOR ${GENERATOR_DIR}/gen_keycodes.lua)
set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua)
set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload_nlua.lua)
@@ -318,6 +319,7 @@ set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h)
set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h)
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h)
+set(GENERATED_KEYCODE_NAMES ${GENERATED_DIR}/keycode_names.generated.h)
set(GENERATED_API_METADATA ${GENERATED_DIR}/api/private/api_metadata.generated.h)
set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h)
set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h)
@@ -601,6 +603,7 @@ add_custom_command(
${MSGPACK_RPC_HEADERS}
${API_DISPATCH_GENERATOR}
${GENERATOR_C_GRAMMAR}
+ ${GENERATOR_HASHY}
${UI_METADATA}
${NVIM_VERSION_LUA}
${NVIM_VERSION_GIT_H}
@@ -658,27 +661,10 @@ add_custom_command(
${LUA_GEN_DEPS}
${API_UI_EVENTS_GENERATOR}
${GENERATOR_C_GRAMMAR}
+ ${GENERATOR_HASHY}
${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
)
-list(APPEND NVIM_GENERATED_FOR_HEADERS
- "${GENERATED_EX_CMDS_ENUM}"
- "${GENERATED_EVENTS_ENUM}"
- "${GENERATED_KEYSETS_DEFS}"
- "${GENERATED_OPTIONS_ENUM}"
- "${GENERATED_OPTION_VARS}"
-)
-
-list(APPEND NVIM_GENERATED_FOR_SOURCES
- "${GENERATED_API_DISPATCH}"
- "${GENERATED_EX_CMDS_DEFS}"
- "${GENERATED_EVENTS_NAMES_MAP}"
- "${GENERATED_OPTIONS}"
- "${GENERATED_OPTIONS_MAP}"
- "${VIM_MODULE_FILE}"
- "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.h"
-)
-
add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
COMMAND ${LUA_GEN} ${EX_CMDS_GENERATOR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR}
DEPENDS ${LUA_GEN_DEPS} ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua
@@ -686,19 +672,42 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA}
COMMAND ${LUA_GEN} ${FUNCS_GENERATOR} ${GENERATED_DIR} ${FUNCS_METADATA} ${FUNCS_DATA}
- DEPENDS ${LUA_GEN_DEPS} ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${FUNCS_METADATA}
+ DEPENDS ${LUA_GEN_DEPS} ${FUNCS_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${FUNCS_METADATA}
)
-list(APPEND NVIM_GENERATED_FOR_SOURCES
- "${GENERATED_FUNCS}")
add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
COMMAND ${LUA_GEN} ${EVENTS_GENERATOR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
+add_custom_command(OUTPUT ${GENERATED_KEYCODE_NAMES}
+ COMMAND ${LUA_GEN} ${KEYCODES_GENERATOR} ${GENERATED_KEYCODE_NAMES}
+ DEPENDS ${LUA_GEN_DEPS} ${KEYCODES_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/keycodes.lua
+)
+
add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
- DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
+ DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${GENERATOR_HASHY} ${CMAKE_CURRENT_LIST_DIR}/options.lua
+)
+
+list(APPEND NVIM_GENERATED_FOR_HEADERS
+ "${GENERATED_EX_CMDS_ENUM}"
+ "${GENERATED_EVENTS_ENUM}"
+ "${GENERATED_KEYSETS_DEFS}"
+ "${GENERATED_OPTIONS_ENUM}"
+ "${GENERATED_OPTION_VARS}"
+)
+
+list(APPEND NVIM_GENERATED_FOR_SOURCES
+ "${GENERATED_API_DISPATCH}"
+ "${GENERATED_EX_CMDS_DEFS}"
+ "${GENERATED_EVENTS_NAMES_MAP}"
+ "${GENERATED_FUNCS}"
+ "${GENERATED_KEYCODE_NAMES}"
+ "${GENERATED_OPTIONS}"
+ "${GENERATED_OPTIONS_MAP}"
+ "${VIM_MODULE_FILE}"
+ "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.h"
)
# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive.
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 41a09999d0..aa9dc1098e 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -507,7 +507,11 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *
const String termresponse = value.data.string;
set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
- apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, false, AUGROUP_ALL, NULL, NULL, &value);
+
+ MAXSIZE_TEMP_DICT(data, 1);
+ PUT_C(data, "sequence", value);
+ apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
+ &DICT_OBJ(data));
}
}
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 9655fef0a4..c5e7186713 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -1,142 +1,153 @@
return {
- --- @type [string, string[], boolean][] List of [eventname, aliases, window-local event] tuples.
+ --- @type table<string,boolean>
+ --- Keys are events names.
+ --- Values are boolean indicating whether the event is window-local.
events = {
- { 'BufAdd', { 'BufCreate' }, true }, -- after adding a buffer to the buffer list
- { 'BufDelete', {}, true }, -- deleting a buffer from the buffer list
- { 'BufEnter', {}, true }, -- after entering a buffer
- { 'BufFilePost', {}, true }, -- after renaming a buffer
- { 'BufFilePre', {}, true }, -- before renaming a buffer
- { 'BufHidden', {}, true }, -- just after buffer becomes hidden
- { 'BufLeave', {}, true }, -- before leaving a buffer
- { 'BufModifiedSet', {}, true }, -- after the 'modified' state of a buffer changes
- { 'BufNew', {}, true }, -- after creating any buffer
- { 'BufNewFile', {}, true }, -- when creating a buffer for a new file
- { 'BufReadCmd', {}, true }, -- read buffer using command
- { 'BufReadPost', { 'BufRead' }, true }, -- after reading a buffer
- { 'BufReadPre', {}, true }, -- before reading a buffer
- { 'BufUnload', {}, true }, -- just before unloading a buffer
- { 'BufWinEnter', {}, true }, -- after showing a buffer in a window
- { 'BufWinLeave', {}, true }, -- just after buffer removed from window
- { 'BufWipeout', {}, true }, -- just before really deleting a buffer
- { 'BufWriteCmd', {}, true }, -- write buffer using command
- { 'BufWritePost', {}, true }, -- after writing a buffer
- { 'BufWritePre', { 'BufWrite' }, true }, -- before writing a buffer
- { 'ChanInfo', {}, false }, -- info was received about channel
- { 'ChanOpen', {}, false }, -- channel was opened
- { 'CmdUndefined', {}, false }, -- command undefined
- { 'CmdWinEnter', {}, false }, -- after entering the cmdline window
- { 'CmdWinLeave', {}, false }, -- before leaving the cmdline window
- { 'CmdlineChanged', {}, false }, -- command line was modified
- { 'CmdlineEnter', {}, false }, -- after entering cmdline mode
- { 'CmdlineLeave', {}, false }, -- before leaving cmdline mode
- { 'ColorScheme', {}, false }, -- after loading a colorscheme
- { 'ColorSchemePre', {}, false }, -- before loading a colorscheme
- { 'CompleteChanged', {}, false }, -- after popup menu changed
- { 'CompleteDone', {}, false }, -- after finishing insert complete
- { 'CompleteDonePre', {}, false }, -- idem, before clearing info
- { 'CursorHold', {}, true }, -- cursor in same position for a while
- { 'CursorHoldI', {}, true }, -- idem, in Insert mode
- { 'CursorMoved', {}, true }, -- cursor was moved
- { 'CursorMovedC', {}, true }, -- cursor was moved in Cmdline mode
- { 'CursorMovedI', {}, true }, -- cursor was moved in Insert mode
- { 'DiagnosticChanged', {}, false }, -- diagnostics in a buffer were modified
- { 'DiffUpdated', {}, false }, -- diffs have been updated
- { 'DirChanged', {}, false }, -- directory changed
- { 'DirChangedPre', {}, false }, -- directory is going to change
- { 'EncodingChanged', { 'FileEncoding' }, false }, -- after changing the 'encoding' option
- { 'ExitPre', {}, false }, -- before exiting
- { 'FileAppendCmd', {}, true }, -- append to a file using command
- { 'FileAppendPost', {}, true }, -- after appending to a file
- { 'FileAppendPre', {}, true }, -- before appending to a file
- { 'FileChangedRO', {}, true }, -- before first change to read-only file
- { 'FileChangedShell', {}, true }, -- after shell command that changed file
- { 'FileChangedShellPost', {}, true }, -- after (not) reloading changed file
- { 'FileReadCmd', {}, true }, -- read from a file using command
- { 'FileReadPost', {}, true }, -- after reading a file
- { 'FileReadPre', {}, true }, -- before reading a file
- { 'FileType', {}, true }, -- new file type detected (user defined)
- { 'FileWriteCmd', {}, true }, -- write to a file using command
- { 'FileWritePost', {}, true }, -- after writing a file
- { 'FileWritePre', {}, true }, -- before writing a file
- { 'FilterReadPost', {}, true }, -- after reading from a filter
- { 'FilterReadPre', {}, true }, -- before reading from a filter
- { 'FilterWritePost', {}, true }, -- after writing to a filter
- { 'FilterWritePre', {}, true }, -- before writing to a filter
- { 'FocusGained', {}, false }, -- got the focus
- { 'FocusLost', {}, false }, -- lost the focus to another app
- { 'FuncUndefined', {}, false }, -- if calling a function which doesn't exist
- { 'GUIEnter', {}, false }, -- after starting the GUI
- { 'GUIFailed', {}, false }, -- after starting the GUI failed
- { 'InsertChange', {}, true }, -- when changing Insert/Replace mode
- { 'InsertCharPre', {}, true }, -- before inserting a char
- { 'InsertEnter', {}, true }, -- when entering Insert mode
- { 'InsertLeave', {}, true }, -- just after leaving Insert mode
- { 'InsertLeavePre', {}, true }, -- just before leaving Insert mode
- { 'LspAttach', {}, false }, -- after an LSP client attaches to a buffer
- { 'LspDetach', {}, false }, -- after an LSP client detaches from a buffer
- { 'LspNotify', {}, false }, -- after an LSP notice has been sent to the server
- { 'LspProgress', {}, false }, -- after a LSP progress update
- { 'LspRequest', {}, false }, -- after an LSP request is started, canceled, or completed
- { 'LspTokenUpdate', {}, false }, -- after a visible LSP token is updated
- { 'MenuPopup', {}, false }, -- just before popup menu is displayed
- { 'ModeChanged', {}, false }, -- after changing the mode
- { 'OptionSet', {}, false }, -- after setting any option
- { 'QuickFixCmdPost', {}, false }, -- after :make, :grep etc.
- { 'QuickFixCmdPre', {}, false }, -- before :make, :grep etc.
- { 'QuitPre', {}, false }, -- before :quit
- { 'RecordingEnter', {}, true }, -- when starting to record a macro
- { 'RecordingLeave', {}, true }, -- just before a macro stops recording
- { 'RemoteReply', {}, false }, -- upon string reception from a remote vim
- { 'SafeState', {}, false }, -- going to wait for a character
- { 'SearchWrapped', {}, true }, -- after the search wrapped around
- { 'SessionLoadPost', {}, false }, -- after loading a session file
- { 'SessionWritePost', {}, false }, -- after writing a session file
- { 'ShellCmdPost', {}, false }, -- after ":!cmd"
- { 'ShellFilterPost', {}, true }, -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
- { 'Signal', {}, false }, -- after nvim process received a signal
- { 'SourceCmd', {}, false }, -- sourcing a Vim script using command
- { 'SourcePost', {}, false }, -- after sourcing a Vim script
- { 'SourcePre', {}, false }, -- before sourcing a Vim script
- { 'SpellFileMissing', {}, false }, -- spell file missing
- { 'StdinReadPost', {}, false }, -- after reading from stdin
- { 'StdinReadPre', {}, false }, -- before reading from stdin
- { 'SwapExists', {}, false }, -- found existing swap file
- { 'Syntax', {}, false }, -- syntax selected
- { 'TabClosed', {}, false }, -- a tab has closed
- { 'TabEnter', {}, false }, -- after entering a tab page
- { 'TabLeave', {}, false }, -- before leaving a tab page
- { 'TabNew', {}, false }, -- when creating a new tab
- { 'TabNewEntered', {}, false }, -- after entering a new tab
- { 'TermChanged', {}, false }, -- after changing 'term'
- { 'TermClose', {}, false }, -- after the process exits
- { 'TermEnter', {}, false }, -- after entering Terminal mode
- { 'TermLeave', {}, false }, -- after leaving Terminal mode
- { 'TermOpen', {}, false }, -- after opening a terminal buffer
- { 'TermRequest', {}, false }, -- after an unhandled OSC sequence is emitted
- { 'TermResponse', {}, false }, -- after setting "v:termresponse"
- { 'TextChanged', {}, true }, -- text was modified
- { 'TextChangedI', {}, true }, -- text was modified in Insert mode(no popup)
- { 'TextChangedP', {}, true }, -- text was modified in Insert mode(popup)
- { 'TextChangedT', {}, true }, -- text was modified in Terminal mode
- { 'TextYankPost', {}, true }, -- after a yank or delete was done (y, d, c)
- { 'UIEnter', {}, false }, -- after UI attaches
- { 'UILeave', {}, false }, -- after UI detaches
- { 'User', {}, false }, -- user defined autocommand
- { 'VimEnter', {}, false }, -- after starting Vim
- { 'VimLeave', {}, false }, -- before exiting Vim
- { 'VimLeavePre', {}, false }, -- before exiting Vim and writing ShaDa file
- { 'VimResized', {}, false }, -- after Vim window was resized
- { 'VimResume', {}, false }, -- after Nvim is resumed
- { 'VimSuspend', {}, false }, -- before Nvim is suspended
- { 'WinClosed', {}, true }, -- after closing a window
- { 'WinEnter', {}, true }, -- after entering a window
- { 'WinLeave', {}, true }, -- before leaving a window
- { 'WinNew', {}, false }, -- when entering a new window
- { 'WinResized', {}, true }, -- after a window was resized
- { 'WinScrolled', {}, true }, -- after a window was scrolled or resized
+ BufAdd = true, -- after adding a buffer to the buffer list
+ BufDelete = true, -- deleting a buffer from the buffer list
+ BufEnter = true, -- after entering a buffer
+ BufFilePost = true, -- after renaming a buffer
+ BufFilePre = true, -- before renaming a buffer
+ BufHidden = true, -- just after buffer becomes hidden
+ BufLeave = true, -- before leaving a buffer
+ BufModifiedSet = true, -- after the 'modified' state of a buffer changes
+ BufNew = true, -- after creating any buffer
+ BufNewFile = true, -- when creating a buffer for a new file
+ BufReadCmd = true, -- read buffer using command
+ BufReadPost = true, -- after reading a buffer
+ BufReadPre = true, -- before reading a buffer
+ BufUnload = true, -- just before unloading a buffer
+ BufWinEnter = true, -- after showing a buffer in a window
+ BufWinLeave = true, -- just after buffer removed from window
+ BufWipeout = true, -- just before really deleting a buffer
+ BufWriteCmd = true, -- write buffer using command
+ BufWritePost = true, -- after writing a buffer
+ BufWritePre = true, -- before writing a buffer
+ ChanInfo = false, -- info was received about channel
+ ChanOpen = false, -- channel was opened
+ CmdUndefined = false, -- command undefined
+ CmdWinEnter = false, -- after entering the cmdline window
+ CmdWinLeave = false, -- before leaving the cmdline window
+ CmdlineChanged = false, -- command line was modified
+ CmdlineEnter = false, -- after entering cmdline mode
+ CmdlineLeave = false, -- before leaving cmdline mode
+ ColorScheme = false, -- after loading a colorscheme
+ ColorSchemePre = false, -- before loading a colorscheme
+ CompleteChanged = false, -- after popup menu changed
+ CompleteDone = false, -- after finishing insert complete
+ CompleteDonePre = false, -- idem, before clearing info
+ CursorHold = true, -- cursor in same position for a while
+ CursorHoldI = true, -- idem, in Insert mode
+ CursorMoved = true, -- cursor was moved
+ CursorMovedC = true, -- cursor was moved in Cmdline mode
+ CursorMovedI = true, -- cursor was moved in Insert mode
+ DiagnosticChanged = false, -- diagnostics in a buffer were modified
+ DiffUpdated = false, -- diffs have been updated
+ DirChanged = false, -- directory changed
+ DirChangedPre = false, -- directory is going to change
+ EncodingChanged = false, -- after changing the 'encoding' option
+ ExitPre = false, -- before exiting
+ FileAppendCmd = true, -- append to a file using command
+ FileAppendPost = true, -- after appending to a file
+ FileAppendPre = true, -- before appending to a file
+ FileChangedRO = true, -- before first change to read-only file
+ FileChangedShell = true, -- after shell command that changed file
+ FileChangedShellPost = true, -- after (not) reloading changed file
+ FileReadCmd = true, -- read from a file using command
+ FileReadPost = true, -- after reading a file
+ FileReadPre = true, -- before reading a file
+ FileType = true, -- new file type detected (user defined)
+ FileWriteCmd = true, -- write to a file using command
+ FileWritePost = true, -- after writing a file
+ FileWritePre = true, -- before writing a file
+ FilterReadPost = true, -- after reading from a filter
+ FilterReadPre = true, -- before reading from a filter
+ FilterWritePost = true, -- after writing to a filter
+ FilterWritePre = true, -- before writing to a filter
+ FocusGained = false, -- got the focus
+ FocusLost = false, -- lost the focus to another app
+ FuncUndefined = false, -- if calling a function which doesn't exist
+ GUIEnter = false, -- after starting the GUI
+ GUIFailed = false, -- after starting the GUI failed
+ InsertChange = true, -- when changing Insert/Replace mode
+ InsertCharPre = true, -- before inserting a char
+ InsertEnter = true, -- when entering Insert mode
+ InsertLeave = true, -- just after leaving Insert mode
+ InsertLeavePre = true, -- just before leaving Insert mode
+ LspAttach = false, -- after an LSP client attaches to a buffer
+ LspDetach = false, -- after an LSP client detaches from a buffer
+ LspNotify = false, -- after an LSP notice has been sent to the server
+ LspProgress = false, -- after a LSP progress update
+ LspRequest = false, -- after an LSP request is started, canceled, or completed
+ LspTokenUpdate = false, -- after a visible LSP token is updated
+ MenuPopup = false, -- just before popup menu is displayed
+ ModeChanged = false, -- after changing the mode
+ OptionSet = false, -- after setting any option
+ QuickFixCmdPost = false, -- after :make, :grep etc.
+ QuickFixCmdPre = false, -- before :make, :grep etc.
+ QuitPre = false, -- before :quit
+ RecordingEnter = true, -- when starting to record a macro
+ RecordingLeave = true, -- just before a macro stops recording
+ RemoteReply = false, -- upon string reception from a remote vim
+ SafeState = false, -- going to wait for a character
+ SearchWrapped = true, -- after the search wrapped around
+ SessionLoadPost = false, -- after loading a session file
+ SessionWritePost = false, -- after writing a session file
+ ShellCmdPost = false, -- after ":!cmd"
+ ShellFilterPost = true, -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
+ Signal = false, -- after nvim process received a signal
+ SourceCmd = false, -- sourcing a Vim script using command
+ SourcePost = false, -- after sourcing a Vim script
+ SourcePre = false, -- before sourcing a Vim script
+ SpellFileMissing = false, -- spell file missing
+ StdinReadPost = false, -- after reading from stdin
+ StdinReadPre = false, -- before reading from stdin
+ SwapExists = false, -- found existing swap file
+ Syntax = false, -- syntax selected
+ TabClosed = false, -- a tab has closed
+ TabEnter = false, -- after entering a tab page
+ TabLeave = false, -- before leaving a tab page
+ TabNew = false, -- when creating a new tab
+ TabNewEntered = false, -- after entering a new tab
+ TermChanged = false, -- after changing 'term'
+ TermClose = false, -- after the process exits
+ TermEnter = false, -- after entering Terminal mode
+ TermLeave = false, -- after leaving Terminal mode
+ TermOpen = false, -- after opening a terminal buffer
+ TermRequest = false, -- after an unhandled OSC sequence is emitted
+ TermResponse = false, -- after setting "v:termresponse"
+ TextChanged = true, -- text was modified
+ TextChangedI = true, -- text was modified in Insert mode(no popup)
+ TextChangedP = true, -- text was modified in Insert mode(popup)
+ TextChangedT = true, -- text was modified in Terminal mode
+ TextYankPost = true, -- after a yank or delete was done (y, d, c)
+ UIEnter = false, -- after UI attaches
+ UILeave = false, -- after UI detaches
+ User = false, -- user defined autocommand
+ VimEnter = false, -- after starting Vim
+ VimLeave = false, -- before exiting Vim
+ VimLeavePre = false, -- before exiting Vim and writing ShaDa file
+ VimResized = false, -- after Vim window was resized
+ VimResume = false, -- after Nvim is resumed
+ VimSuspend = false, -- before Nvim is suspended
+ WinClosed = true, -- after closing a window
+ WinEnter = true, -- after entering a window
+ WinLeave = true, -- before leaving a window
+ WinNew = false, -- when entering a new window
+ WinResized = true, -- after a window was resized
+ WinScrolled = true, -- after a window was scrolled or resized
},
- -- List of nvim-specific events or aliases for the purpose of generating
- -- syntax file
+ --- @type table<string,string>
+ --- Keys are event aliases.
+ --- Values are the names in the `events` table above.
+ aliases = {
+ BufCreate = 'BufAdd',
+ BufRead = 'BufReadPost',
+ BufWrite = 'BufWritePre',
+ FileEncoding = 'EncodingChanged',
+ },
+ --- @type table<string,true>
+ --- List of Nvim-specific events or aliases for generating syntax file.
nvim_specific = {
BufModifiedSet = true,
DiagnosticChanged = true,
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 34ad734528..51d952ed3e 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -658,12 +658,7 @@ event_T event_name2nr_str(String str)
const char *event_nr2name(event_T event)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
{
- for (int i = 0; event_names[i].name != NULL; i++) {
- if ((event_T)abs(event_names[i].event) == event) {
- return event_names[i].name;
- }
- }
- return "Unknown";
+ return event >= 0 && event < NUM_EVENTS ? event_names[event].name : "Unknown";
}
/// Return true if "event" is included in 'eventignore(win)'.
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 95cee3c442..87d548bfab 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -810,8 +810,8 @@ struct tabpage_S {
typedef struct {
linenr_T wl_lnum; // buffer line number for logical line
uint16_t wl_size; // height in screen lines
- char wl_valid; // true values are valid for text in buffer
- char wl_folded; // true when this is a range of folded lines
+ bool wl_valid; // true values are valid for text in buffer
+ bool wl_folded; // true when this is a range of folded lines
linenr_T wl_foldend; // last buffer line number for folded line
linenr_T wl_lastlnum; // last buffer line number for logical line
} wline_T;
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 84053619d7..28854ae8aa 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -208,8 +208,9 @@ static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col,
wp->w_lines[i].wl_foldend += xtra;
wp->w_lines[i].wl_lastlnum += xtra;
}
- } else if (wp->w_lines[i].wl_foldend >= lnum) {
- // change somewhere inside this range of folded lines,
+ } else if (wp->w_lines[i].wl_foldend >= lnum
+ || wp->w_lines[i].wl_lastlnum >= lnum) {
+ // change somewhere inside this range of folded or concealed lines,
// may need to be redrawn
wp->w_lines[i].wl_valid = false;
}
@@ -1315,7 +1316,8 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// May do indenting after opening a new line.
bool do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL)
&& in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK,
- ' ', linewhite(curwin->w_cursor.lnum));
+ ' ', linewhite(curwin->w_cursor.lnum))
+ && !(flags & OPENLINE_FORCE_INDENT);
// Find out if the current line starts with a comment leader.
// This may then be inserted in front of the new line.
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index c964748a20..9fc0a82ca3 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -897,7 +897,7 @@ static void set_info_event(void **argv)
tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict);
tv_dict_set_keys_readonly(dict);
- apply_autocmds(event, NULL, NULL, false, curbuf);
+ apply_autocmds(event, NULL, NULL, true, curbuf);
restore_v_event(dict, &save_v_event);
arena_mem_free(arena_finish(&arena));
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 33ccc834d2..fcfeda5482 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -288,7 +288,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
}
// Translate string into pattern and expand it.
- const int use_options = (options
+ const int use_options = ((options & ~WILD_KEEP_SOLE_ITEM)
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
@@ -339,7 +339,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
if (xp->xp_numfiles <= 0 && p2 == NULL) {
beep_flush();
- } else if (xp->xp_numfiles == 1) {
+ } else if (xp->xp_numfiles == 1 && !(options & WILD_KEEP_SOLE_ITEM)) {
// free expanded pattern
ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
}
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 33ff787589..59c734e9a3 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -40,6 +40,7 @@ enum {
WILD_NOERROR = 0x800, ///< sets EW_NOERROR
WILD_BUFLASTUSED = 0x1000,
BUF_DIFF_FILTER = 0x2000,
+ WILD_KEEP_SOLE_ITEM = 0x4000,
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index a645dced40..e958610f20 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -2118,13 +2118,13 @@ static void win_update(win_T *wp)
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
conceal_cursor_used = conceal_cursor_line(curwin);
}
- if (idx > 0) {
- wp->w_lines[idx - 1].wl_lastlnum = lnum + foldinfo.fi_lines - (foldinfo.fi_lines != 0);
- }
- if (lnum == mod_top && lnum < mod_bot) {
- mod_top += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
- }
if (win_get_fill(wp, lnum) == 0) {
+ if (idx > 0) {
+ wp->w_lines[idx - 1].wl_lastlnum = lnum + foldinfo.fi_lines - (foldinfo.fi_lines != 0);
+ }
+ if (lnum == mod_top && lnum < mod_bot) {
+ mod_top += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
+ }
lnum += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
spv.spv_capcol_lnum = 0;
continue;
@@ -2319,6 +2319,13 @@ static void win_update(win_T *wp)
wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
did_update = DID_FOLD;
}
+
+ // Adjust "wl_lastlnum" for concealed lines below the last line in the window.
+ while (row == wp->w_grid.rows
+ && decor_conceal_line(wp, wp->w_lines[idx].wl_lastlnum, false)) {
+ wp->w_lines[idx].wl_lastlnum++;
+ hasFolding(wp, wp->w_lines[idx].wl_lastlnum, NULL, &wp->w_lines[idx].wl_lastlnum);
+ }
}
wp->w_lines[idx].wl_lnum = lnum;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index f5ba9c1c05..c3f204045a 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -9,6 +9,7 @@
#include <uv.h>
#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -99,7 +100,6 @@ typedef struct {
int did_restart_edit; // remember if insert mode was restarted
// after a ctrl+o
bool nomove;
- char *ptr;
} InsertState;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -163,15 +163,8 @@ static void insert_enter(InsertState *s)
if (s->cmdchar != 'r' && s->cmdchar != 'v') {
pos_T save_cursor = curwin->w_cursor;
- if (s->cmdchar == 'R') {
- s->ptr = "r";
- } else if (s->cmdchar == 'V') {
- s->ptr = "v";
- } else {
- s->ptr = "i";
- }
-
- set_vim_var_string(VV_INSERTMODE, s->ptr, 1);
+ const char *const ptr = s->cmdchar == 'R' ? "r" : s->cmdchar == 'V' ? "v" : "i";
+ set_vim_var_string(VV_INSERTMODE, ptr, 1);
set_vim_var_string(VV_CHAR, NULL, -1);
ins_apply_autocmds(EVENT_INSERTENTER);
@@ -288,14 +281,15 @@ static void insert_enter(InsertState *s)
// column. Eg after "^O$" or "^O80|".
validate_virtcol(curwin);
update_curswant();
+ const char *ptr;
if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum)
|| curwin->w_curswant > curwin->w_virtcol)
- && *(s->ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) {
- if (s->ptr[1] == NUL) {
+ && *(ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) {
+ if (ptr[1] == NUL) {
curwin->w_cursor.col++;
} else {
- s->i = utfc_ptr2len(s->ptr);
- if (s->ptr[s->i] == NUL) {
+ s->i = utfc_ptr2len(ptr);
+ if (ptr[s->i] == NUL) {
curwin->w_cursor.col += s->i;
}
}
@@ -335,12 +329,10 @@ static void insert_enter(InsertState *s)
// Get the current length of the redo buffer, those characters have to be
// skipped if we want to get to the inserted characters.
- s->ptr = get_inserted();
- if (s->ptr == NULL) {
- new_insert_skip = 0;
- } else {
- new_insert_skip = (int)get_inserted_len();
- xfree(s->ptr);
+ String inserted = get_inserted();
+ new_insert_skip = (int)inserted.size;
+ if (inserted.data != NULL) {
+ xfree(inserted.data);
}
old_indent = 0;
@@ -2337,14 +2329,14 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
// Save the inserted text for later redo with ^@ and CTRL-A.
// Don't do it when "restart_edit" was set and nothing was inserted,
// otherwise CTRL-O w and then <Left> will clear "last_insert".
- char *ptr = get_inserted();
- int added = ptr == NULL ? 0 : (int)get_inserted_len() - new_insert_skip;
+ String inserted = get_inserted();
+ int added = inserted.data == NULL ? 0 : (int)inserted.size - new_insert_skip;
if (did_restart_edit == 0 || added > 0) {
xfree(last_insert);
- last_insert = ptr;
+ last_insert = inserted.data;
last_insert_skip = added < 0 ? 0 : new_insert_skip;
} else {
- xfree(ptr);
+ xfree(inserted.data);
}
if (!arrow_used && end_insert_pos != NULL) {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7cec2aaa06..c8ad61f885 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -4191,7 +4191,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
list_T *args = argvars[0].vval.v_list;
Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs));
- MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
+ MultiQueue *waiting_jobs = multiqueue_new(loop_on_put, &main_loop);
// Validate, prepare jobs for waiting.
int i = 0;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f386dd28b9..c5357d507c 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -265,29 +266,16 @@ static void register_closure(ufunc_T *fp)
}
static char lambda_name[8 + NUMBUFLEN];
-static size_t lambda_namelen = 0;
/// @return a name for a lambda. Returned in static memory.
-char *get_lambda_name(void)
+static String get_lambda_name(void)
{
static int lambda_no = 0;
int n = snprintf(lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
- if (n < 1) {
- lambda_namelen = 0;
- } else if (n >= (int)sizeof(lambda_name)) {
- lambda_namelen = sizeof(lambda_name) - 1;
- } else {
- lambda_namelen = (size_t)n;
- }
- return lambda_name;
-}
-
-/// Get the length of the last lambda name.
-size_t get_lambda_name_len(void)
-{
- return lambda_namelen;
+ return cbuf_as_string(lambda_name,
+ n < 1 ? 0 : (size_t)MIN(n, (int)sizeof(lambda_name) - 1));
}
/// Allocate a "ufunc_T" for a function called "name".
@@ -371,10 +359,8 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
int flags = 0;
garray_T newlines;
- char *name = get_lambda_name();
- size_t namelen = get_lambda_name_len();
-
- fp = alloc_ufunc(name, namelen);
+ String name = get_lambda_name();
+ fp = alloc_ufunc(name.data, name.size);
pt = xcalloc(1, sizeof(partial_T));
ga_init(&newlines, (int)sizeof(char *), 1);
@@ -4142,9 +4128,8 @@ bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
/// Registers a luaref as a lambda.
char *register_luafunc(LuaRef ref)
{
- char *name = get_lambda_name();
- size_t namelen = get_lambda_name_len();
- ufunc_T *fp = alloc_ufunc(name, namelen);
+ String name = get_lambda_name();
+ ufunc_T *fp = alloc_ufunc(name.data, name.size);
fp->uf_refcount = 1;
fp->uf_varargs = true;
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 15d993cc62..050419b8e9 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -21,9 +21,9 @@ void loop_init(Loop *loop, void *data)
loop->closing = false;
loop->uv.data = loop;
kv_init(loop->children);
- loop->events = multiqueue_new_parent(loop_on_put, loop);
+ loop->events = multiqueue_new(loop_on_put, loop);
loop->fast_events = multiqueue_new_child(loop->events);
- loop->thread_events = multiqueue_new_parent(NULL, NULL);
+ loop->thread_events = multiqueue_new(NULL, NULL);
uv_mutex_init(&loop->mutex);
uv_async_init(&loop->uv, &loop->async, async_cb);
uv_signal_init(&loop->uv, &loop->children_watcher);
@@ -212,10 +212,7 @@ static void async_cb(uv_async_t *handle)
Loop *l = handle->loop->data;
uv_mutex_lock(&l->mutex);
// Flush thread_events to fast_events for processing on main loop.
- while (!multiqueue_empty(l->thread_events)) {
- Event ev = multiqueue_get(l->thread_events);
- multiqueue_put_event(l->fast_events, ev);
- }
+ multiqueue_move_events(l->fast_events, l->thread_events);
uv_mutex_unlock(&l->mutex);
}
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 8646173776..859051e594 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -67,7 +67,7 @@ struct multiqueue_item {
struct multiqueue {
MultiQueue *parent;
QUEUE headtail; // circularly-linked
- PutCallback put_cb;
+ PutCallback on_put; // Called on the parent (if any) when an item is enqueued in a child.
void *data;
size_t size;
};
@@ -84,26 +84,28 @@ typedef struct {
static Event NILEVENT = { .handler = NULL, .argv = { NULL } };
-MultiQueue *multiqueue_new_parent(PutCallback put_cb, void *data)
+/// Creates a new root (parentless) queue, which may gain child queues via `multiqueue_new_child`.
+MultiQueue *multiqueue_new(PutCallback on_put, void *data)
{
- return multiqueue_new(NULL, put_cb, data);
+ return _multiqueue_new(NULL, on_put, data);
}
+/// Creates a new queue as a child of a `parent` queue.
MultiQueue *multiqueue_new_child(MultiQueue *parent)
FUNC_ATTR_NONNULL_ALL
{
assert(!parent->parent); // parent cannot have a parent, more like a "root"
parent->size++;
- return multiqueue_new(parent, NULL, NULL);
+ return _multiqueue_new(parent, NULL, NULL);
}
-static MultiQueue *multiqueue_new(MultiQueue *parent, PutCallback put_cb, void *data)
+static MultiQueue *_multiqueue_new(MultiQueue *parent, PutCallback on_put, void *data)
{
MultiQueue *rv = xmalloc(sizeof(MultiQueue));
QUEUE_INIT(&rv->headtail);
rv->size = 0;
rv->parent = parent;
- rv->put_cb = put_cb;
+ rv->on_put = on_put;
rv->data = data;
return rv;
}
@@ -135,8 +137,18 @@ void multiqueue_put_event(MultiQueue *self, Event event)
{
assert(self);
multiqueue_push(self, event);
- if (self->parent && self->parent->put_cb) {
- self->parent->put_cb(self->parent, self->parent->data);
+ if (self->parent && self->parent->on_put) {
+ self->parent->on_put(self->parent, self->parent->data);
+ }
+}
+
+/// Move events from src to dest.
+void multiqueue_move_events(MultiQueue *dest, MultiQueue *src)
+ FUNC_ATTR_NONNULL_ALL
+{
+ while (!multiqueue_empty(src)) {
+ Event event = multiqueue_get(src);
+ multiqueue_put_event(dest, event);
}
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index fc20748309..240bdae6cb 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1082,6 +1082,9 @@ static int command_line_wildchar_complete(CommandLineState *s)
if (wim_flags[s->wim_index] & kOptWimFlagLastused) {
options |= WILD_BUFLASTUSED;
}
+ if (wim_flags[0] & kOptWimFlagNoselect) {
+ options |= WILD_KEEP_SOLE_ITEM;
+ }
if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
// if 'wildmode' contains "list" may still need to list
if (s->xpc.xp_numfiles > 1
@@ -1124,19 +1127,20 @@ static int command_line_wildchar_complete(CommandLineState *s)
// when more than one match, and 'wildmode' first contains
// "list", or no change and 'wildmode' contains "longest,list",
// list all matches
- if (res == OK && s->xpc.xp_numfiles > 1) {
+ if (res == OK
+ && s->xpc.xp_numfiles > ((wim_flags[s->wim_index] & kOptWimFlagNoselect) ? 0 : 1)) {
// a "longest" that didn't do anything is skipped (but not
// "list:longest")
if (wim_flags[0] == kOptWimFlagLongest && ccline.cmdpos == j) {
s->wim_index = 1;
}
if ((wim_flags[s->wim_index] & kOptWimFlagList)
- || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0)) {
+ || (p_wmnu && (wim_flags[s->wim_index] & (kOptWimFlagFull|kOptWimFlagNoselect)))) {
if (!(wim_flags[0] & kOptWimFlagLongest)) {
int p_wmnu_save = p_wmnu;
p_wmnu = 0;
// remove match
- nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
+ nextwild(&s->xpc, WILD_PREV, options, s->firstc != '@');
p_wmnu = p_wmnu_save;
}
@@ -1146,7 +1150,8 @@ static int command_line_wildchar_complete(CommandLineState *s)
if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
+ } else if ((wim_flags[s->wim_index] & kOptWimFlagFull)
+ && !(wim_flags[s->wim_index] & kOptWimFlagNoselect)) {
nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
}
} else {
@@ -2875,6 +2880,8 @@ int check_opt_wim(void)
new_wim_flags[idx] |= kOptWimFlagList;
} else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
new_wim_flags[idx] |= kOptWimFlagLastused;
+ } else if (i == 8 && strncmp(p, "noselect", 8) == 0) {
+ new_wim_flags[idx] |= kOptWimFlagNoselect;
} else {
return FAIL;
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index ac817ab9e6..899293e9f6 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -155,9 +155,6 @@ static uint8_t noremapbuf_init[TYPELEN_INIT]; ///< initial typebuf.tb_noremap
static size_t last_recorded_len = 0; ///< number of last recorded chars
-static size_t last_get_inserted_len = 0; ///< length of the string returned from the
- ///< last call to get_inserted()
-
enum {
KEYLEN_PART_KEY = -1, ///< keylen value for incomplete key-code
KEYLEN_PART_MAP = -2, ///< keylen value for incomplete mapping
@@ -254,15 +251,11 @@ char *get_recorded(void)
/// Return the contents of the redo buffer as a single string.
/// K_SPECIAL in the returned string is escaped.
-char *get_inserted(void)
-{
- return get_buffcont(&redobuff, false, &last_get_inserted_len);
-}
-
-/// Return the length of string returned from the last call of get_inserted().
-size_t get_inserted_len(void)
+String get_inserted(void)
{
- return last_get_inserted_len;
+ size_t len = 0;
+ char *str = get_buffcont(&redobuff, false, &len);
+ return cbuf_as_string(str, len);
}
/// Add string after the current block of the given buffer
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 766c5eb2a4..aa5110655c 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -3,6 +3,7 @@
#include <stddef.h> // IWYU pragma: keep
#include <stdint.h> // IWYU pragma: keep
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/getchar_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index e0bbba3d68..15152bc1a4 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -464,6 +464,8 @@ EXTERN bool VIsual_active INIT( = false);
EXTERN bool VIsual_select INIT( = false);
/// Register name for Select mode
EXTERN int VIsual_select_reg INIT( = 0);
+/// Whether incremented cursor during exclusive selection
+EXTERN bool VIsual_select_exclu_adj INIT( = false);
/// Restart Select mode when next cmd finished
EXTERN int restart_VIsual_select INIT( = 0);
/// Whether to restart the selection after a Select-mode mapping or menu.
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index b6ba73f7c1..9cb2321eee 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <uv.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
@@ -24,6 +25,7 @@
#include "nvim/strings.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keycode_names.generated.h"
# include "keycodes.c.generated.h"
#endif
@@ -142,214 +144,6 @@ static uint8_t modifier_keys_table[] = {
NUL
};
-static const struct key_name_entry {
- int key; // Special key code or ascii value
- const char *name; // Name of key
-} key_names_table[] = {
- { ' ', "Space" },
- { TAB, "Tab" },
- { K_TAB, "Tab" },
- { NL, "NL" },
- { NL, "NewLine" }, // Alternative name
- { NL, "LineFeed" }, // Alternative name
- { NL, "LF" }, // Alternative name
- { CAR, "CR" },
- { CAR, "Return" }, // Alternative name
- { CAR, "Enter" }, // Alternative name
- { K_BS, "BS" },
- { K_BS, "BackSpace" }, // Alternative name
- { ESC, "Esc" },
- { ESC, "Escape" }, // Alternative name
- { CSI, "CSI" },
- { '|', "Bar" },
- { '\\', "Bslash" },
- { K_DEL, "Del" },
- { K_DEL, "Delete" }, // Alternative name
- { K_KDEL, "kDel" },
- { K_KDEL, "KPPeriod" }, // libtermkey name
- { K_UP, "Up" },
- { K_DOWN, "Down" },
- { K_LEFT, "Left" },
- { K_RIGHT, "Right" },
- { K_XUP, "xUp" },
- { K_XDOWN, "xDown" },
- { K_XLEFT, "xLeft" },
- { K_XRIGHT, "xRight" },
- { K_KUP, "kUp" },
- { K_KUP, "KP8" },
- { K_KDOWN, "kDown" },
- { K_KDOWN, "KP2" },
- { K_KLEFT, "kLeft" },
- { K_KLEFT, "KP4" },
- { K_KRIGHT, "kRight" },
- { K_KRIGHT, "KP6" },
-
- { K_F1, "F1" },
- { K_F2, "F2" },
- { K_F3, "F3" },
- { K_F4, "F4" },
- { K_F5, "F5" },
- { K_F6, "F6" },
- { K_F7, "F7" },
- { K_F8, "F8" },
- { K_F9, "F9" },
- { K_F10, "F10" },
-
- { K_F11, "F11" },
- { K_F12, "F12" },
- { K_F13, "F13" },
- { K_F14, "F14" },
- { K_F15, "F15" },
- { K_F16, "F16" },
- { K_F17, "F17" },
- { K_F18, "F18" },
- { K_F19, "F19" },
- { K_F20, "F20" },
-
- { K_F21, "F21" },
- { K_F22, "F22" },
- { K_F23, "F23" },
- { K_F24, "F24" },
- { K_F25, "F25" },
- { K_F26, "F26" },
- { K_F27, "F27" },
- { K_F28, "F28" },
- { K_F29, "F29" },
- { K_F30, "F30" },
-
- { K_F31, "F31" },
- { K_F32, "F32" },
- { K_F33, "F33" },
- { K_F34, "F34" },
- { K_F35, "F35" },
- { K_F36, "F36" },
- { K_F37, "F37" },
- { K_F38, "F38" },
- { K_F39, "F39" },
- { K_F40, "F40" },
-
- { K_F41, "F41" },
- { K_F42, "F42" },
- { K_F43, "F43" },
- { K_F44, "F44" },
- { K_F45, "F45" },
- { K_F46, "F46" },
- { K_F47, "F47" },
- { K_F48, "F48" },
- { K_F49, "F49" },
- { K_F50, "F50" },
-
- { K_F51, "F51" },
- { K_F52, "F52" },
- { K_F53, "F53" },
- { K_F54, "F54" },
- { K_F55, "F55" },
- { K_F56, "F56" },
- { K_F57, "F57" },
- { K_F58, "F58" },
- { K_F59, "F59" },
- { K_F60, "F60" },
-
- { K_F61, "F61" },
- { K_F62, "F62" },
- { K_F63, "F63" },
-
- { K_XF1, "xF1" },
- { K_XF2, "xF2" },
- { K_XF3, "xF3" },
- { K_XF4, "xF4" },
-
- { K_HELP, "Help" },
- { K_UNDO, "Undo" },
- { K_FIND, "Find" }, // DEC key, often used as 'Home'
- { K_KSELECT, "Select" }, // DEC key, often used as 'End'
- { K_INS, "Insert" },
- { K_INS, "Ins" }, // Alternative name
- { K_KINS, "kInsert" },
- { K_KINS, "KP0" },
- { K_HOME, "Home" },
- { K_KHOME, "kHome" },
- { K_KHOME, "KP7" },
- { K_XHOME, "xHome" },
- { K_ZHOME, "zHome" },
- { K_END, "End" },
- { K_KEND, "kEnd" },
- { K_KEND, "KP1" },
- { K_XEND, "xEnd" },
- { K_ZEND, "zEnd" },
- { K_PAGEUP, "PageUp" },
- { K_PAGEDOWN, "PageDown" },
- { K_KPAGEUP, "kPageUp" },
- { K_KPAGEUP, "KP9" },
- { K_KPAGEDOWN, "kPageDown" },
- { K_KPAGEDOWN, "KP3" },
- { K_KORIGIN, "kOrigin" },
- { K_KORIGIN, "KP5" },
-
- { K_KPLUS, "kPlus" },
- { K_KPLUS, "KPPlus" },
- { K_KMINUS, "kMinus" },
- { K_KMINUS, "KPMinus" },
- { K_KDIVIDE, "kDivide" },
- { K_KDIVIDE, "KPDiv" },
- { K_KMULTIPLY, "kMultiply" },
- { K_KMULTIPLY, "KPMult" },
- { K_KENTER, "kEnter" },
- { K_KENTER, "KPEnter" },
- { K_KPOINT, "kPoint" },
- { K_KCOMMA, "kComma" },
- { K_KCOMMA, "KPComma" },
- { K_KEQUAL, "kEqual" },
- { K_KEQUAL, "KPEquals" },
-
- { K_K0, "k0" },
- { K_K1, "k1" },
- { K_K2, "k2" },
- { K_K3, "k3" },
- { K_K4, "k4" },
- { K_K5, "k5" },
- { K_K6, "k6" },
- { K_K7, "k7" },
- { K_K8, "k8" },
- { K_K9, "k9" },
-
- { '<', "lt" },
-
- { K_MOUSE, "Mouse" },
- { K_LEFTMOUSE, "LeftMouse" },
- { K_LEFTMOUSE_NM, "LeftMouseNM" },
- { K_LEFTDRAG, "LeftDrag" },
- { K_LEFTRELEASE, "LeftRelease" },
- { K_LEFTRELEASE_NM, "LeftReleaseNM" },
- { K_MOUSEMOVE, "MouseMove" },
- { K_MIDDLEMOUSE, "MiddleMouse" },
- { K_MIDDLEDRAG, "MiddleDrag" },
- { K_MIDDLERELEASE, "MiddleRelease" },
- { K_RIGHTMOUSE, "RightMouse" },
- { K_RIGHTDRAG, "RightDrag" },
- { K_RIGHTRELEASE, "RightRelease" },
- { K_MOUSEDOWN, "ScrollWheelUp" },
- { K_MOUSEUP, "ScrollWheelDown" },
- { K_MOUSELEFT, "ScrollWheelRight" },
- { K_MOUSERIGHT, "ScrollWheelLeft" },
- { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use
- { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead
- { K_X1MOUSE, "X1Mouse" },
- { K_X1DRAG, "X1Drag" },
- { K_X1RELEASE, "X1Release" },
- { K_X2MOUSE, "X2Mouse" },
- { K_X2DRAG, "X2Drag" },
- { K_X2RELEASE, "X2Release" },
- { K_DROP, "Drop" },
- { K_ZERO, "Nul" },
- { K_SNR, "SNR" },
- { K_PLUG, "Plug" },
- { K_IGNORE, "Ignore" },
- { K_COMMAND, "Cmd" },
- { 0, NULL }
- // NOTE: When adding a long name update MAX_KEY_NAME_LEN.
-};
-
static struct mousetable {
int pseudo_code; // Code for pseudo mouse event
int button; // Which mouse button is it?
@@ -544,15 +338,18 @@ char *get_special_key_name(int c, int modifiers)
}
}
} else { // use name of special key
- size_t len = strlen(key_names_table[table_idx].name);
+ const String *s = key_names_table[table_idx].pref_name != NULL
+ ? key_names_table[table_idx].pref_name
+ : &key_names_table[table_idx].name;
- if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) {
- STRCPY(string + idx, key_names_table[table_idx].name);
- idx += (int)len;
+ if ((int)s->size + idx + 2 <= MAX_KEY_NAME_LEN) {
+ STRCPY(string + idx, s->data);
+ idx += (int)s->size;
}
}
string[idx++] = '>';
string[idx] = NUL;
+
return string;
}
@@ -795,17 +592,13 @@ static int extract_modifiers(int key, int *modp, const bool simplify, bool *cons
/// @return the index when found, -1 when not found.
int find_special_key_in_table(int c)
{
- int i;
-
- for (i = 0; key_names_table[i].name != NULL; i++) {
+ for (int i = 0; i < (int)ARRAY_SIZE(key_names_table); i++) {
if (c == key_names_table[i].key) {
- break;
+ return i;
}
}
- if (key_names_table[i].name == NULL) {
- i = -1;
- }
- return i;
+
+ return -1;
}
/// Find the special key with the given name
@@ -823,20 +616,13 @@ int get_special_key_code(const char *name)
return TERMCAP2KEY((uint8_t)name[2], (uint8_t)name[3]);
}
- for (int i = 0; key_names_table[i].name != NULL; i++) {
- const char *const table_name = key_names_table[i].name;
- int j;
- for (j = 0; ascii_isident((uint8_t)name[j]) && table_name[j] != NUL; j++) {
- if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC((uint8_t)name[j])) {
- break;
- }
- }
- if (!ascii_isident((uint8_t)name[j]) && table_name[j] == NUL) {
- return key_names_table[i].key;
- }
+ const char *name_end = name;
+ while (ascii_isident(*name_end)) {
+ name_end++;
}
- return 0;
+ int idx = get_special_key_code_hash(name, (size_t)(name_end - name));
+ return idx >= 0 ? key_names_table[idx].key : 0;
}
/// Look up the given mouse code to return the relevant information in the other arguments.
diff --git a/src/nvim/keycodes.lua b/src/nvim/keycodes.lua
new file mode 100644
index 0000000000..04f09eb005
--- /dev/null
+++ b/src/nvim/keycodes.lua
@@ -0,0 +1,208 @@
+return {
+ --- @type [string, string][] List of [key, name] tuples.
+ --- For keys with multiple names, put the preferred name first.
+ --- For multiple keys with the same name, put the preferred key first.
+ names = {
+ { [[' ']], 'Space' },
+ { [[TAB]], 'Tab' },
+ { [[K_TAB]], 'Tab' },
+ { [[NL]], 'NL' },
+ { [[NL]], 'NewLine' }, -- Alternative name
+ { [[NL]], 'LineFeed' }, -- Alternative name
+ { [[NL]], 'LF' }, -- Alternative name
+ { [[CAR]], 'CR' },
+ { [[CAR]], 'Return' }, -- Alternative name
+ { [[CAR]], 'Enter' }, -- Alternative name
+ { [[K_BS]], 'BS' },
+ { [[K_BS]], 'BackSpace' }, -- Alternative name
+ { [[ESC]], 'Esc' },
+ { [[ESC]], 'Escape' }, -- Alternative name
+ { [[CSI]], 'CSI' },
+ { [['|']], 'Bar' },
+ { [['\\']], 'Bslash' },
+ { [[K_DEL]], 'Del' },
+ { [[K_DEL]], 'Delete' }, -- Alternative name
+ { [[K_KDEL]], 'kDel' },
+ { [[K_KDEL]], 'KPPeriod' }, -- libtermkey name
+ { [[K_UP]], 'Up' },
+ { [[K_DOWN]], 'Down' },
+ { [[K_LEFT]], 'Left' },
+ { [[K_RIGHT]], 'Right' },
+ { [[K_XUP]], 'xUp' },
+ { [[K_XDOWN]], 'xDown' },
+ { [[K_XLEFT]], 'xLeft' },
+ { [[K_XRIGHT]], 'xRight' },
+ { [[K_KUP]], 'kUp' },
+ { [[K_KUP]], 'KP8' },
+ { [[K_KDOWN]], 'kDown' },
+ { [[K_KDOWN]], 'KP2' },
+ { [[K_KLEFT]], 'kLeft' },
+ { [[K_KLEFT]], 'KP4' },
+ { [[K_KRIGHT]], 'kRight' },
+ { [[K_KRIGHT]], 'KP6' },
+
+ { [[K_F1]], 'F1' },
+ { [[K_F2]], 'F2' },
+ { [[K_F3]], 'F3' },
+ { [[K_F4]], 'F4' },
+ { [[K_F5]], 'F5' },
+ { [[K_F6]], 'F6' },
+ { [[K_F7]], 'F7' },
+ { [[K_F8]], 'F8' },
+ { [[K_F9]], 'F9' },
+ { [[K_F10]], 'F10' },
+
+ { [[K_F11]], 'F11' },
+ { [[K_F12]], 'F12' },
+ { [[K_F13]], 'F13' },
+ { [[K_F14]], 'F14' },
+ { [[K_F15]], 'F15' },
+ { [[K_F16]], 'F16' },
+ { [[K_F17]], 'F17' },
+ { [[K_F18]], 'F18' },
+ { [[K_F19]], 'F19' },
+ { [[K_F20]], 'F20' },
+
+ { [[K_F21]], 'F21' },
+ { [[K_F22]], 'F22' },
+ { [[K_F23]], 'F23' },
+ { [[K_F24]], 'F24' },
+ { [[K_F25]], 'F25' },
+ { [[K_F26]], 'F26' },
+ { [[K_F27]], 'F27' },
+ { [[K_F28]], 'F28' },
+ { [[K_F29]], 'F29' },
+ { [[K_F30]], 'F30' },
+
+ { [[K_F31]], 'F31' },
+ { [[K_F32]], 'F32' },
+ { [[K_F33]], 'F33' },
+ { [[K_F34]], 'F34' },
+ { [[K_F35]], 'F35' },
+ { [[K_F36]], 'F36' },
+ { [[K_F37]], 'F37' },
+ { [[K_F38]], 'F38' },
+ { [[K_F39]], 'F39' },
+ { [[K_F40]], 'F40' },
+
+ { [[K_F41]], 'F41' },
+ { [[K_F42]], 'F42' },
+ { [[K_F43]], 'F43' },
+ { [[K_F44]], 'F44' },
+ { [[K_F45]], 'F45' },
+ { [[K_F46]], 'F46' },
+ { [[K_F47]], 'F47' },
+ { [[K_F48]], 'F48' },
+ { [[K_F49]], 'F49' },
+ { [[K_F50]], 'F50' },
+
+ { [[K_F51]], 'F51' },
+ { [[K_F52]], 'F52' },
+ { [[K_F53]], 'F53' },
+ { [[K_F54]], 'F54' },
+ { [[K_F55]], 'F55' },
+ { [[K_F56]], 'F56' },
+ { [[K_F57]], 'F57' },
+ { [[K_F58]], 'F58' },
+ { [[K_F59]], 'F59' },
+ { [[K_F60]], 'F60' },
+
+ { [[K_F61]], 'F61' },
+ { [[K_F62]], 'F62' },
+ { [[K_F63]], 'F63' },
+
+ { [[K_XF1]], 'xF1' },
+ { [[K_XF2]], 'xF2' },
+ { [[K_XF3]], 'xF3' },
+ { [[K_XF4]], 'xF4' },
+
+ { [[K_HELP]], 'Help' },
+ { [[K_UNDO]], 'Undo' },
+ { [[K_FIND]], 'Find' }, -- DEC key, often used as 'Home'
+ { [[K_KSELECT]], 'Select' }, -- DEC key, often used as 'End'
+ { [[K_INS]], 'Insert' },
+ { [[K_INS]], 'Ins' }, -- Alternative name
+ { [[K_KINS]], 'kInsert' },
+ { [[K_KINS]], 'KP0' },
+ { [[K_HOME]], 'Home' },
+ { [[K_KHOME]], 'kHome' },
+ { [[K_KHOME]], 'KP7' },
+ { [[K_XHOME]], 'xHome' },
+ { [[K_ZHOME]], 'zHome' },
+ { [[K_END]], 'End' },
+ { [[K_KEND]], 'kEnd' },
+ { [[K_KEND]], 'KP1' },
+ { [[K_XEND]], 'xEnd' },
+ { [[K_ZEND]], 'zEnd' },
+ { [[K_PAGEUP]], 'PageUp' },
+ { [[K_PAGEDOWN]], 'PageDown' },
+ { [[K_KPAGEUP]], 'kPageUp' },
+ { [[K_KPAGEUP]], 'KP9' },
+ { [[K_KPAGEDOWN]], 'kPageDown' },
+ { [[K_KPAGEDOWN]], 'KP3' },
+ { [[K_KORIGIN]], 'kOrigin' },
+ { [[K_KORIGIN]], 'KP5' },
+
+ { [[K_KPLUS]], 'kPlus' },
+ { [[K_KPLUS]], 'KPPlus' },
+ { [[K_KMINUS]], 'kMinus' },
+ { [[K_KMINUS]], 'KPMinus' },
+ { [[K_KDIVIDE]], 'kDivide' },
+ { [[K_KDIVIDE]], 'KPDiv' },
+ { [[K_KMULTIPLY]], 'kMultiply' },
+ { [[K_KMULTIPLY]], 'KPMult' },
+ { [[K_KENTER]], 'kEnter' },
+ { [[K_KENTER]], 'KPEnter' },
+ { [[K_KPOINT]], 'kPoint' },
+ { [[K_KCOMMA]], 'kComma' },
+ { [[K_KCOMMA]], 'KPComma' },
+ { [[K_KEQUAL]], 'kEqual' },
+ { [[K_KEQUAL]], 'KPEquals' },
+
+ { [[K_K0]], 'k0' },
+ { [[K_K1]], 'k1' },
+ { [[K_K2]], 'k2' },
+ { [[K_K3]], 'k3' },
+ { [[K_K4]], 'k4' },
+ { [[K_K5]], 'k5' },
+ { [[K_K6]], 'k6' },
+ { [[K_K7]], 'k7' },
+ { [[K_K8]], 'k8' },
+ { [[K_K9]], 'k9' },
+
+ { [['<']], 'lt' },
+
+ { [[K_MOUSE]], 'Mouse' },
+ { [[K_LEFTMOUSE]], 'LeftMouse' },
+ { [[K_LEFTMOUSE_NM]], 'LeftMouseNM' },
+ { [[K_LEFTDRAG]], 'LeftDrag' },
+ { [[K_LEFTRELEASE]], 'LeftRelease' },
+ { [[K_LEFTRELEASE_NM]], 'LeftReleaseNM' },
+ { [[K_MOUSEMOVE]], 'MouseMove' },
+ { [[K_MIDDLEMOUSE]], 'MiddleMouse' },
+ { [[K_MIDDLEDRAG]], 'MiddleDrag' },
+ { [[K_MIDDLERELEASE]], 'MiddleRelease' },
+ { [[K_RIGHTMOUSE]], 'RightMouse' },
+ { [[K_RIGHTDRAG]], 'RightDrag' },
+ { [[K_RIGHTRELEASE]], 'RightRelease' },
+ { [[K_MOUSEDOWN]], 'ScrollWheelUp' },
+ { [[K_MOUSEUP]], 'ScrollWheelDown' },
+ { [[K_MOUSELEFT]], 'ScrollWheelRight' },
+ { [[K_MOUSERIGHT]], 'ScrollWheelLeft' },
+ { [[K_MOUSEDOWN]], 'MouseDown' }, -- OBSOLETE: Use ScrollWheelUp instead
+ { [[K_MOUSEUP]], 'MouseUp' }, -- OBSOLETE: Use ScrollWheelDown instead
+ { [[K_X1MOUSE]], 'X1Mouse' },
+ { [[K_X1DRAG]], 'X1Drag' },
+ { [[K_X1RELEASE]], 'X1Release' },
+ { [[K_X2MOUSE]], 'X2Mouse' },
+ { [[K_X2DRAG]], 'X2Drag' },
+ { [[K_X2RELEASE]], 'X2Release' },
+ { [[K_DROP]], 'Drop' },
+ { [[K_ZERO]], 'Nul' },
+ { [[K_SNR]], 'SNR' },
+ { [[K_PLUG]], 'Plug' },
+ { [[K_IGNORE]], 'Ignore' },
+ { [[K_COMMAND]], 'Cmd' },
+ -- NOTE: When adding a long name update MAX_KEY_NAME_LEN.
+ },
+}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index b1766db379..724a3d77e6 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -125,30 +125,36 @@ lua_State *get_global_lstate(void)
return global_lstate;
}
-/// Convert lua error into a Vim error message
+/// Gets the Lua error at top of stack as a string, possibly modifying it in-place (but doesn't
+/// change stack height).
///
-/// @param lstate Lua interpreter state.
-/// @param[in] msg Message base, must contain one `%.*s`.
-void nlua_error(lua_State *const lstate, const char *const msg)
- FUNC_ATTR_NONNULL_ALL
+/// The returned string points to memory on the Lua stack. Use or duplicate it before using
+/// `lstate` again.
+///
+/// @param[out] len length of error (can be NULL)
+static const char *nlua_get_error(lua_State *lstate, size_t *len)
{
- size_t len;
- const char *str = NULL;
-
if (luaL_getmetafield(lstate, -1, "__tostring")) {
if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) {
- // call __tostring, convert the result and pop result.
- str = lua_tolstring(lstate, -1, &len);
- lua_pop(lstate, 1);
+ // call __tostring, convert the result and replace error with it
+ lua_replace(lstate, -3);
}
// pop __tostring.
lua_pop(lstate, 1);
}
- if (!str) {
- // defer to lua default conversion, this will render tables as [NULL].
- str = lua_tolstring(lstate, -1, &len);
- }
+ return lua_tolstring(lstate, -1, len);
+}
+
+/// Converts a Lua error into a Vim error message.
+///
+/// @param lstate Lua interpreter state.
+/// @param[in] msg Message base, must contain one `%.*s`.
+void nlua_error(lua_State *const lstate, const char *const msg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t len;
+ const char *str = nlua_get_error(lstate, &len);
if (in_script) {
fprintf(stderr, msg, (int)len, str);
@@ -218,7 +224,9 @@ static int nlua_fast_cfpcall(lua_State *lstate, int nargs, int nresult, int flag
// consider out of memory errors unrecoverable, just like xmalloc()
preserve_exit(e_outofmem);
}
- const char *error = lua_tostring(lstate, -1);
+
+ size_t len;
+ const char *error = nlua_get_error(lstate, &len);
multiqueue_put(main_loop.events, nlua_luv_error_event,
error != NULL ? xstrdup(error) : NULL, (void *)(intptr_t)kCallback);
@@ -1659,13 +1667,13 @@ void ex_lua(exarg_T *const eap)
// ":lua {code}", ":={expr}" or ":lua ={expr}"
//
- // When "=expr" is used transform it to "vim.print(expr)".
+ // When "=expr" is used transform it to "vim._print(true, expr)".
if (eap->cmdidx == CMD_equal || code[0] == '=') {
size_t off = (eap->cmdidx == CMD_equal) ? 0 : 1;
- len += sizeof("vim.print()") - 1 - off;
+ len += sizeof("vim._print(true, )") - 1 - off;
// `nlua_typval_exec` doesn't expect NUL-terminated string so `len` must end before NUL byte.
char *code_buf = xmallocz(len);
- vim_snprintf(code_buf, len + 1, "vim.print(%s)", code + off);
+ vim_snprintf(code_buf, len + 1, "vim._print(true, %s)", code + off);
xfree(code);
code = code_buf;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 44e48e9e72..83ab0d5c24 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1524,6 +1524,7 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
/// do_pending_operator().
void end_visual_mode(void)
{
+ VIsual_select_exclu_adj = false;
VIsual_active = false;
setmouse();
mouse_dragging = 0;
@@ -4035,17 +4036,24 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int o
/// cap->nchar is NUL for ',' and ';' (repeat the search)
static void nv_csearch(cmdarg_T *cap)
{
- bool t_cmd;
+ bool cursor_dec = false;
- if (cap->cmdchar == 't' || cap->cmdchar == 'T') {
- t_cmd = true;
- } else {
- t_cmd = false;
+ // If adjusted cursor position previously, unadjust it.
+ if (*p_sel == 'e' && VIsual_active && VIsual_mode == 'v'
+ && VIsual_select_exclu_adj) {
+ unadjust_for_sel();
+ cursor_dec = true;
}
+ bool t_cmd = cap->cmdchar == 't' || cap->cmdchar == 'T';
+
cap->oap->motion_type = kMTCharWise;
if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) {
clearopbeep(cap->oap);
+ // Revert unadjust when failed.
+ if (cursor_dec) {
+ adjust_for_sel(cap);
+ }
return;
}
@@ -5054,6 +5062,8 @@ static void nv_visual(cmdarg_T *cap)
n_start_visual_mode(cap->cmdchar);
if (VIsual_mode != 'V' && *p_sel == 'e') {
cap->count1++; // include one more char
+ } else {
+ VIsual_select_exclu_adj = false;
}
if (cap->count0 > 0 && --cap->count1 > 0) {
// With a count select that many characters or lines.
@@ -6021,6 +6031,7 @@ static void adjust_for_sel(cmdarg_T *cap)
&& gchar_cursor() != NUL && lt(VIsual, curwin->w_cursor)) {
inc_cursor();
cap->oap->inclusive = false;
+ VIsual_select_exclu_adj = true;
}
}
@@ -6042,7 +6053,7 @@ bool unadjust_for_sel(void)
/// @return true when backed up to the previous line.
bool unadjust_for_sel_inner(pos_T *pp)
{
- colnr_T cs, ce;
+ VIsual_select_exclu_adj = false;
if (pp->coladd > 0) {
pp->coladd--;
@@ -6050,6 +6061,7 @@ bool unadjust_for_sel_inner(pos_T *pp)
pp->col--;
mark_mb_adjustpos(curbuf, pp);
if (virtual_active(curwin)) {
+ colnr_T cs, ce;
getvcol(curwin, pp, &cs, NULL, &ce);
pp->coladd = ce - cs;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 72c75534d4..0bda14402f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2806,6 +2806,10 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
curbuf->b_op_start.col = 0;
curbuf->b_op_end.col = MAXCOL;
}
+ if (yank_type != kMTLineWise && !oap->inclusive) {
+ // Exclude the end position.
+ decl(&curbuf->b_op_end);
+ }
}
}
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index d1d5a56082..49de8b92ef 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1525,15 +1525,15 @@ local options = {
match, e.g., what file it comes from.
noinsert Do not insert any text for a match until the user selects
- a match from the menu. Only works in combination with
+ a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
noselect Same as "noinsert", except that no menu item is
- pre-selected. If both "noinsert" and "noselect" are
+ pre-selected. If both "noinsert" and "noselect" are
present, "noselect" has precedence.
nosort Disable sorting of completion candidates based on fuzzy
- scores when "fuzzy" is enabled. Candidates will appear
+ scores when "fuzzy" is enabled. Candidates will appear
in their original order.
popup Show extra information about the currently selected
@@ -1544,7 +1544,7 @@ local options = {
Preinsert the portion of the first candidate word that is
not part of the current completion leader and using the
|hl-ComplMatchIns| highlight group. In order for it to
- work, "fuzzy" must not bet set and "menuone" must be set.
+ work, "fuzzy" must not be set and "menuone" must be set.
preview Show extra information about the currently selected
completion in the preview window. Only works in
@@ -6236,7 +6236,7 @@ local options = {
set path=,,
< - A directory name may end in a ':' or '/'.
- Environment variables are expanded |:set_env|.
- - When using |netrw.vim| URLs can be used. For example, adding
+ - When using |netrw| URLs can be used. For example, adding
"https://www.vim.org" will make ":find index.html" work.
- Search upwards and downwards in a directory tree using "*", "**" and
";". See |file-searching| for info and syntax.
@@ -10020,7 +10020,7 @@ local options = {
cb = 'did_set_wildmode',
defaults = 'full',
-- Keep this in sync with check_opt_wim().
- values = { 'full', 'longest', 'list', 'lastused' },
+ values = { 'full', 'longest', 'list', 'lastused', 'noselect' },
flags = true,
deny_duplicates = false,
desc = [=[
@@ -10042,7 +10042,10 @@ local options = {
"lastused" When completing buffer names and more than one buffer
matches, sort buffers by time last used (other than
the current buffer).
- When there is only a single match, it is fully completed in all cases.
+ "noselect" Do not pre-select first menu item and start 'wildmenu'
+ if it is enabled.
+ When there is only a single match, it is fully completed in all cases
+ except when "noselect" is present.
Examples of useful colon-separated values:
"longest:full" Like "longest", but also start 'wildmenu' if it is
@@ -10065,7 +10068,11 @@ local options = {
set wildmode=list,full
< List all matches without completing, then each full match >vim
set wildmode=longest,list
- < Complete longest common string, then list alternatives.
+ < Complete longest common string, then list alternatives >vim
+ set wildmode=noselect:full
+ < Display 'wildmenu' without completing, then each full match >vim
+ set wildmode=noselect:lastused,full
+ < Same as above, but sort buffers by time last used.
More info here: |cmdline-completion|.
]=],
full_name = 'wildmode',
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 2e533bea23..b057cae08d 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -430,12 +430,10 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
size_t leader_len = strlen(leader);
garray_T *ga = NULL;
- bool matched_start = false;
+ int matched_len = -1;
if (in_fuzzy) {
ga = fuzzy_match_str_with_pos(text, leader);
- } else {
- matched_start = mb_strnicmp(text, leader, leader_len) == 0;
}
const char *ptr = text;
@@ -456,10 +454,16 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
break;
}
}
- } else if (matched_start && ptr < text + leader_len) {
- new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
- new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
- new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
+ } else {
+ if (matched_len < 0 && mb_strnicmp(ptr, leader, leader_len) == 0) {
+ matched_len = (int)leader_len;
+ }
+ if (matched_len > 0) {
+ new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
+ matched_len--;
+ }
}
new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), new_attr);
@@ -799,14 +803,25 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma
Error err = ERROR_INIT;
Arena arena = ARENA_EMPTY;
Array replacement = ARRAY_DICT_INIT;
- char *token = NULL;
- char *line = os_strtok(info, "\n", &token);
buf->b_p_ma = true;
- while (line != NULL) {
- ADD(replacement, STRING_OBJ(cstr_to_string(line)));
+
+ // Iterate through the string line by line by temporarily replacing newlines with NUL
+ for (char *curr = info, *next; curr; curr = next ? next + 1 : NULL) {
+ if ((next = strchr(curr, '\n'))) {
+ *next = NUL; // Temporarily replace the newline with a string terminator
+ }
+ // Only skip if this is an empty line AND it's the last line
+ if (*curr == '\0' && !next) {
+ break;
+ }
+
+ *max_width = MAX(*max_width, (int)mb_string2cells(curr));
+ ADD(replacement, STRING_OBJ(cstr_to_string(curr)));
(*lnum)++;
- (*max_width) = MAX(*max_width, (int)mb_string2cells(line));
- line = os_strtok(NULL, "\n", &token);
+
+ if (next) {
+ *next = '\n';
+ }
}
int original_textlock = textlock;
@@ -824,8 +839,23 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma
buf->b_p_ma = false;
}
+/// Calculate the total height (in screen lines) of the first 'count' buffer lines in window 'wp'.
+/// Takes line wrapping and other display factors into account.
+///
+/// @param wp Window pointer
+/// @param count Number of buffer lines to measure (1-based)
+/// @return Total height in screen lines
+static inline int pum_preview_win_height(win_T *wp, linenr_T count)
+{
+ int height = 0;
+ for (int i = 1; i <= count; i++) {
+ height += plines_win(wp, i, false);
+ }
+ return height;
+}
+
/// adjust floating info preview window position
-static void pum_adjust_info_position(win_T *wp, int height, int width)
+static void pum_adjust_info_position(win_T *wp, int width)
{
int col = pum_col + pum_width + pum_scrollbar + 1;
// TODO(glepnir): support config align border by using completepopup
@@ -846,8 +876,10 @@ static void pum_adjust_info_position(win_T *wp, int height, int width)
}
// when pum_above is SW otherwise is NW
wp->w_config.anchor = pum_above ? kFloatAnchorSouth : 0;
- wp->w_config.row = pum_above ? pum_row + height : pum_row;
- wp->w_config.height = MIN(Rows, height);
+ linenr_T count = wp->w_buffer->b_ml.ml_line_count;
+ wp->w_width_inner = wp->w_config.width;
+ wp->w_config.height = MIN(Rows, pum_preview_win_height(wp, count));
+ wp->w_config.row = pum_above ? pum_row + wp->w_config.height : pum_row;
wp->w_config.hide = false;
win_config_float(wp, wp->w_config);
}
@@ -880,10 +912,7 @@ win_T *pum_set_info(int selected, char *info)
RedrawingDisabled--;
redraw_later(wp, UPD_NOT_VALID);
- if (wp->w_p_wrap) {
- lnum += plines_win(wp, lnum, true);
- }
- pum_adjust_info_position(wp, lnum, max_info_width);
+ pum_adjust_info_position(wp, max_info_width);
unblock_autocmds();
return wp;
}
@@ -1059,11 +1088,8 @@ static bool pum_set_selected(int n, int repeat)
curwin->w_cursor.col = 0;
if (use_float) {
- if (curwin->w_p_wrap) {
- lnum += plines_win(curwin, lnum, true);
- }
// adjust floating window by actually height and max info text width
- pum_adjust_info_position(curwin, lnum, max_info_width);
+ pum_adjust_info_position(curwin, max_info_width);
}
if ((curwin != curwin_save && win_valid(curwin_save))
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 322b97d6bf..add4c9dcb6 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -475,6 +475,28 @@ bool striequal(const char *a, const char *b)
return (a == NULL && b == NULL) || (a && b && STRICMP(a, b) == 0);
}
+/// Compare two ASCII strings, for length "len", ignoring case, ignoring locale.
+///
+/// @return 0 for match, < 0 for smaller, > 0 for bigger
+int vim_strnicmp_asc(const char *s1, const char *s2, size_t len)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ int i = 0;
+ while (len > 0) {
+ i = TOLOWER_ASC(*s1) - TOLOWER_ASC(*s2);
+ if (i != 0) {
+ break; // this character is different
+ }
+ if (*s1 == NUL) {
+ break; // strings match until NUL
+ }
+ s1++;
+ s2++;
+ len--;
+ }
+ return i;
+}
+
/// strchr() version which handles multibyte strings
///
/// @param[in] string String to search in.
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 557d41a467..baa862f57a 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2986,6 +2986,8 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
secure = 1;
sandbox++;
curwin->w_cursor.lnum = 1; // start command in line 1
+ curwin->w_cursor.col = 0;
+ curwin->w_cursor.coladd = 0;
do_cmdline_cmd(pbuf);
retval = OK;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 47630ddea9..f4acfb486c 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -106,10 +106,11 @@
typedef struct {
VimState state;
Terminal *term;
- int save_rd; // saved value of RedrawingDisabled
+ int save_rd; ///< saved value of RedrawingDisabled
bool close;
- bool got_bsl; // if the last input was <C-\>
- bool got_bsl_o; // if left terminal mode with <c-\><c-o>
+ bool got_bsl; ///< if the last input was <C-\>
+ bool got_bsl_o; ///< if left terminal mode with <c-\><c-o>
+ bool cursor_visible; ///< cursor's current visibility; ensures matched busy_start/stop UI events
} TerminalState;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -169,7 +170,8 @@ struct terminal {
struct {
int row, col;
int shape;
- bool visible;
+ bool visible; ///< Terminal wants to show cursor.
+ ///< `TerminalState.cursor_visible` indicates whether it is actually shown.
bool blink;
} cursor;
@@ -177,6 +179,7 @@ struct terminal {
bool resize; ///< pending width/height
bool cursor; ///< pending cursor shape or blink change
StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input.
+ MultiQueue *events; ///< Events waiting for refresh.
} pending;
bool theme_updates; ///< Send a theme update notification when 'bg' changes
@@ -186,7 +189,7 @@ struct terminal {
char *selection_buffer; ///< libvterm selection buffer
StringBuilder selection; ///< Growable array containing full selection data
- StringBuilder termrequest_buffer; ///< Growable array containing unfinished request payload
+ StringBuilder termrequest_buffer; ///< Growable array containing unfinished request sequence
size_t refcount; // reference count
};
@@ -213,16 +216,37 @@ static Set(ptr_t) invalidated_terminals = SET_INIT;
static void emit_termrequest(void **argv)
{
Terminal *term = argv[0];
- char *payload = argv[1];
- size_t payload_length = (size_t)argv[2];
+ char *sequence = argv[1];
+ size_t sequence_length = (size_t)argv[2];
StringBuilder *pending_send = argv[3];
+ int row = (int)(intptr_t)argv[4];
+ int col = (int)(intptr_t)argv[5];
+
+ if (term->sb_pending > 0) {
+ // Don't emit the event while there is pending scrollback because we need
+ // the buffer contents to be fully updated. If this is the case, schedule
+ // the event onto the pending queue where it will be executed after the
+ // terminal is refreshed and the pending scrollback is cleared.
+ multiqueue_put(term->pending.events, emit_termrequest, term, sequence, (void *)sequence_length,
+ pending_send, (void *)(intptr_t)row, (void *)(intptr_t)col);
+ return;
+ }
+
+ set_vim_var_string(VV_TERMREQUEST, sequence, (ptrdiff_t)sequence_length);
+
+ MAXSIZE_TEMP_ARRAY(cursor, 2);
+ ADD_C(cursor, INTEGER_OBJ(row));
+ ADD_C(cursor, INTEGER_OBJ(col));
+
+ MAXSIZE_TEMP_DICT(data, 2);
+ String termrequest = { .data = sequence, .size = sequence_length };
+ PUT_C(data, "sequence", STRING_OBJ(termrequest));
+ PUT_C(data, "cursor", ARRAY_OBJ(cursor));
buf_T *buf = handle_get_buffer(term->buf_handle);
- String termrequest = { .data = payload, .size = payload_length };
- Object data = STRING_OBJ(termrequest);
- set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length);
- apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data);
- xfree(payload);
+ apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, true, AUGROUP_ALL, buf, NULL,
+ &DICT_OBJ(data));
+ xfree(sequence);
StringBuilder *term_pending_send = term->pending.send;
term->pending.send = NULL;
@@ -236,12 +260,15 @@ static void emit_termrequest(void **argv)
xfree(pending_send);
}
-static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length)
+static void schedule_termrequest(Terminal *term, char *sequence, size_t sequence_length)
{
term->pending.send = xmalloc(sizeof(StringBuilder));
kv_init(*term->pending.send);
- multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length,
- term->pending.send);
+
+ int line = row_to_linenr(term, term->cursor.row);
+ multiqueue_put(main_loop.events, emit_termrequest, term, sequence, (void *)sequence_length,
+ term->pending.send, (void *)(intptr_t)line,
+ (void *)(intptr_t)term->cursor.col);
}
static int parse_osc8(VTermStringFragment frag, int *attr)
@@ -315,8 +342,8 @@ static int on_osc(int command, VTermStringFragment frag, void *user)
}
kv_concat_len(term->termrequest_buffer, frag.str, frag.len);
if (frag.final) {
- char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
- schedule_termrequest(user, payload, term->termrequest_buffer.size);
+ char *sequence = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
+ schedule_termrequest(user, sequence, term->termrequest_buffer.size);
}
return 1;
}
@@ -338,8 +365,8 @@ static int on_dcs(const char *command, size_t commandlen, VTermStringFragment fr
}
kv_concat_len(term->termrequest_buffer, frag.str, frag.len);
if (frag.final) {
- char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
- schedule_termrequest(user, payload, term->termrequest_buffer.size);
+ char *sequence = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
+ schedule_termrequest(user, sequence, term->termrequest_buffer.size);
}
return 1;
}
@@ -361,8 +388,8 @@ static int on_apc(VTermStringFragment frag, void *user)
}
kv_concat_len(term->termrequest_buffer, frag.str, frag.len);
if (frag.final) {
- char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
- schedule_termrequest(user, payload, term->termrequest_buffer.size);
+ char *sequence = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size);
+ schedule_termrequest(user, sequence, term->termrequest_buffer.size);
}
return 1;
}
@@ -466,6 +493,13 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
term->invalid_start = 0;
term->invalid_end = opts.height;
+ // Create a separate queue for events which need to wait for a terminal
+ // refresh. We cannot reschedule events back onto the main queue because this
+ // can create an infinite loop (#32753).
+ // This queue is never processed directly: when the terminal is refreshed, all
+ // events from this queue are copied back onto the main event queue.
+ term->pending.events = multiqueue_new(NULL, NULL);
+
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
@@ -650,6 +684,7 @@ bool terminal_enter(void)
assert(buf->terminal); // Should only be called when curbuf has a terminal.
TerminalState s[1] = { 0 };
s->term = buf->terminal;
+ s->cursor_visible = true; // Assume visible; may change via refresh_cursor later.
stop_insert_mode = false;
// Ensure the terminal is properly sized. Ideally window size management
@@ -662,10 +697,6 @@ bool terminal_enter(void)
State = MODE_TERMINAL;
mapped_ctrl_c |= MODE_TERMINAL; // Always map CTRL-C to avoid interrupt.
RedrawingDisabled = false;
- if (!s->term->cursor.visible) {
- // Hide cursor if it should be hidden. Do so right after setting State, before events.
- ui_busy_start();
- }
// Disable these options in terminal-mode. They are nonsense because cursor is
// placed at end of buffer to "follow" output. #11072
@@ -692,10 +723,7 @@ bool terminal_enter(void)
curwin->w_p_so = 0;
curwin->w_p_siso = 0;
- // Update the cursor shape table and flush changes to the UI
- s->term->pending.cursor = true;
- refresh_cursor(s->term);
-
+ s->term->pending.cursor = true; // Update the cursor shape table
adjust_topline(s->term, buf, 0); // scroll to end
showmode();
curwin->w_redr_status = true; // For mode() in statusline. #8323
@@ -716,8 +744,8 @@ bool terminal_enter(void)
}
State = save_state;
RedrawingDisabled = s->save_rd;
- if (!s->term->cursor.visible) {
- // If cursor was hidden, show it again. Do so right after restoring State, before events.
+ if (!s->cursor_visible) {
+ // If cursor was hidden, show it again. Do so right after restoring State.
ui_busy_stop();
}
@@ -782,10 +810,13 @@ static void terminal_check_cursor(void)
// 0 if the main loop must exit
static int terminal_check(VimState *state)
{
+ TerminalState *const s = (TerminalState *)state;
+
if (stop_insert_mode) {
return 0;
}
+ assert(s->term == curbuf->terminal);
terminal_check_cursor();
validate_cursor(curwin);
@@ -812,6 +843,7 @@ static int terminal_check(VimState *state)
}
setcursor();
+ refresh_cursor(s->term, &s->cursor_visible);
ui_flush();
return 1;
}
@@ -914,23 +946,9 @@ static int terminal_execute(VimState *state, int key)
}
if (s->term != curbuf->terminal) {
// Active terminal buffer changed, flush terminal's cursor state to the UI
- curbuf->terminal->pending.cursor = true;
-
- if (!s->term->cursor.visible) {
- // If cursor was hidden, show it again
- ui_busy_stop();
- }
-
- if (!curbuf->terminal->cursor.visible) {
- // Hide cursor if it should be hidden
- ui_busy_start();
- }
-
- invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
- invalidate_terminal(curbuf->terminal,
- curbuf->terminal->cursor.row,
- curbuf->terminal->cursor.row + 1);
s->term = curbuf->terminal;
+ s->term->pending.cursor = true;
+ invalidate_terminal(s->term, -1, -1);
}
return 1;
}
@@ -964,6 +982,7 @@ void terminal_destroy(Terminal **termpp)
kv_destroy(term->selection);
kv_destroy(term->termrequest_buffer);
vterm_free(term->vt);
+ multiqueue_free(term->pending.events);
xfree(term);
*termpp = NULL; // coverity[dead-store]
}
@@ -1268,17 +1287,8 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
break;
case VTERM_PROP_CURSORVISIBLE:
- if (is_focused(term)) {
- if (!val->boolean && term->cursor.visible) {
- // Hide the cursor
- ui_busy_start();
- } else if (val->boolean && !term->cursor.visible) {
- // Unhide the cursor
- ui_busy_stop();
- }
- invalidate_terminal(term, -1, -1);
- }
term->cursor.visible = val->boolean;
+ invalidate_terminal(term, -1, -1);
break;
case VTERM_PROP_TITLE: {
@@ -2019,23 +2029,38 @@ static void refresh_terminal(Terminal *term)
}
linenr_T ml_before = buf->b_ml.ml_line_count;
- // refresh_ functions assume the terminal buffer is current
+ // Some refresh_ functions assume the terminal buffer is current. Don't call refresh_cursor here,
+ // as we don't want a terminal that was possibly made temporarily current influencing the cursor.
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
refresh_size(term, buf);
refresh_scrollback(term, buf);
refresh_screen(term, buf);
- refresh_cursor(term);
aucmd_restbuf(&aco);
int ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
+
+ // Copy pending events back to the main event queue
+ multiqueue_move_events(main_loop.events, term->pending.events);
}
-static void refresh_cursor(Terminal *term)
+static void refresh_cursor(Terminal *term, bool *cursor_visible)
FUNC_ATTR_NONNULL_ALL
{
- if (!is_focused(term) || !term->pending.cursor) {
+ if (!is_focused(term)) {
+ return;
+ }
+ if (term->cursor.visible != *cursor_visible) {
+ *cursor_visible = term->cursor.visible;
+ if (*cursor_visible) {
+ ui_busy_stop();
+ } else {
+ ui_busy_start();
+ }
+ }
+
+ if (!term->pending.cursor) {
return;
}
term->pending.cursor = false;
@@ -2143,6 +2168,7 @@ static void adjust_scrollback(Terminal *term, buf_T *buf)
// Refresh the scrollback of an invalidated terminal.
static void refresh_scrollback(Terminal *term, buf_T *buf)
{
+ assert(buf == curbuf); // TODO(seandewar): remove this condition
int width, height;
vterm_get_size(term->vt, &height, &width);
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index e3b3bba7c1..fda8b45e21 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -19,6 +19,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
+#include "nvim/normal.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/search.h"
@@ -427,6 +428,13 @@ int end_word(int count, bool bigword, bool stop, bool empty)
curwin->w_cursor.coladd = 0;
cls_bigword = bigword;
+
+ // If adjusted cursor position previously, unadjust it.
+ if (*p_sel == 'e' && VIsual_active && VIsual_mode == 'v'
+ && VIsual_select_exclu_adj) {
+ unadjust_for_sel();
+ }
+
while (--count >= 0) {
// When inside a range of folded lines, move to the last char of the
// last line.
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 31f95a1006..7c93be844a 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1,4 +1,4 @@
-// Terminal UI functions. Invoked (by ui_client.c) on the UI process.
+// Terminal UI functions. Invoked by the UI process (ui_client.c), not the server.
#include <assert.h>
#include <inttypes.h>
diff --git a/test/benchmark/keycodes_spec.lua b/test/benchmark/keycodes_spec.lua
new file mode 100644
index 0000000000..443df2a6fb
--- /dev/null
+++ b/test/benchmark/keycodes_spec.lua
@@ -0,0 +1,84 @@
+local n = require('test.functional.testnvim')()
+local clear = n.clear
+local api = n.api
+local fn = n.fn
+
+local keycodes = require('src.nvim.keycodes')
+
+describe('nvim_replace_termcodes performance', function()
+ it('200 calls with a key repeated 5000 times', function()
+ clear()
+ local stats = {}
+ local sum = 0
+ local ms = 1 / 1000000
+
+ for _, keycode in ipairs(keycodes.names) do
+ local notation = ('<%s>'):format(keycode[2])
+ local str = notation:rep(5000)
+
+ local start = vim.uv.hrtime()
+ for _ = 1, 200 do
+ api.nvim_replace_termcodes(str, false, true, true)
+ end
+ local elapsed = vim.uv.hrtime() - start
+
+ table.insert(stats, elapsed)
+ sum = sum + elapsed
+ io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
+ io.stdout:flush()
+ end
+ io.stdout:write('\n')
+
+ table.sort(stats)
+ print(('%18s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
+ print(
+ (' %14.6f ms'):rep(6):format(
+ sum / #stats * ms,
+ stats[1] * ms,
+ stats[1 + math.floor(#stats * 0.25)] * ms,
+ stats[1 + math.floor(#stats * 0.5)] * ms,
+ stats[1 + math.floor(#stats * 0.75)] * ms,
+ stats[#stats] * ms
+ )
+ )
+ end)
+end)
+
+describe('keytrans() performance', function()
+ it('200 calls with a key repeated 5000 times', function()
+ clear()
+ local stats = {}
+ local sum = 0
+ local ms = 1 / 1000000
+
+ for _, keycode in ipairs(keycodes.names) do
+ local notation = ('<%s>'):format(keycode[2])
+ local str = api.nvim_replace_termcodes(notation, false, true, true):rep(5000)
+
+ local start = vim.uv.hrtime()
+ for _ = 1, 200 do
+ fn.keytrans(str)
+ end
+ local elapsed = vim.uv.hrtime() - start
+
+ table.insert(stats, elapsed)
+ sum = sum + elapsed
+ io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
+ io.stdout:flush()
+ end
+ io.stdout:write('\n')
+
+ table.sort(stats)
+ print((' %17s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
+ print(
+ (' %14.6f ms'):rep(6):format(
+ sum / #stats * ms,
+ stats[1] * ms,
+ stats[1 + math.floor(#stats * 0.25)] * ms,
+ stats[1 + math.floor(#stats * 0.5)] * ms,
+ stats[1 + math.floor(#stats * 0.75)] * ms,
+ stats[#stats] * ms
+ )
+ )
+ end)
+end)
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index 6f53076f1f..31bb9841dc 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -83,7 +83,7 @@ end
describe('API/extmarks', function()
local screen
local marks, positions, init_text, row, col
- local ns, ns2
+ local ns, ns2 ---@type integer, integer
before_each(function()
-- Initialize some namespaces and insert 12345 into a buffer
@@ -1425,14 +1425,12 @@ describe('API/extmarks', function()
end)
it('throws consistent error codes', function()
- local ns_invalid = ns2 + 1
- eq(
- "Invalid 'ns_id': 3",
- pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2])
- )
- eq("Invalid 'ns_id': 3", pcall_err(api.nvim_buf_del_extmark, 0, ns_invalid, marks[1]))
- eq("Invalid 'ns_id': 3", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
- eq("Invalid 'ns_id': 3", pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
+ local ns_invalid = ns2 + 1 ---@type integer
+ local err = string.format("Invalid 'ns_id': %d", ns_invalid)
+ eq(err, pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
+ eq(err, pcall_err(api.nvim_buf_del_extmark, 0, ns_invalid, marks[1]))
+ eq(err, pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
+ eq(err, pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
end)
it('when col = line-length, set the mark on eol', function()
@@ -1535,7 +1533,7 @@ describe('API/extmarks', function()
0,
0,
{
- ns_id = 1,
+ ns_id = ns,
end_col = 0,
end_row = 1,
right_gravity = true,
@@ -1595,7 +1593,7 @@ describe('API/extmarks', function()
hl_group = 'String',
hl_mode = 'blend',
line_hl_group = 'Statement',
- ns_id = 1,
+ ns_id = ns,
number_hl_group = 'Statement',
priority = 0,
right_gravity = false,
@@ -1626,7 +1624,7 @@ describe('API/extmarks', function()
0,
0,
{
- ns_id = 1,
+ ns_id = ns,
right_gravity = true,
priority = 0,
virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
@@ -1647,7 +1645,7 @@ describe('API/extmarks', function()
0,
0,
{
- ns_id = 1,
+ ns_id = ns,
right_gravity = true,
ui_watched = true,
priority = 0,
@@ -1663,7 +1661,7 @@ describe('API/extmarks', function()
0,
0,
{
- ns_id = 1,
+ ns_id = ns,
cursorline_hl_group = 'Statement',
priority = 4096,
right_gravity = true,
@@ -1683,7 +1681,7 @@ describe('API/extmarks', function()
end_col = 1,
end_right_gravity = false,
end_row = 0,
- ns_id = 1,
+ ns_id = ns,
right_gravity = true,
spell = true,
},
@@ -1700,7 +1698,7 @@ describe('API/extmarks', function()
end_col = 1,
end_right_gravity = false,
end_row = 0,
- ns_id = 1,
+ ns_id = ns,
right_gravity = true,
spell = false,
},
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index b5fcd88f71..18e6dbd9b6 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -3167,14 +3167,23 @@ describe('API', function()
describe('nvim_create_namespace', function()
it('works', function()
- eq({}, api.nvim_get_namespaces())
- eq(1, api.nvim_create_namespace('ns-1'))
- eq(2, api.nvim_create_namespace('ns-2'))
- eq(1, api.nvim_create_namespace('ns-1'))
- eq({ ['ns-1'] = 1, ['ns-2'] = 2 }, api.nvim_get_namespaces())
- eq(3, api.nvim_create_namespace(''))
- eq(4, api.nvim_create_namespace(''))
- eq({ ['ns-1'] = 1, ['ns-2'] = 2 }, api.nvim_get_namespaces())
+ local orig = api.nvim_get_namespaces()
+ local base = vim.iter(orig):fold(0, function(acc, _, v)
+ return math.max(acc, v)
+ end)
+ eq(base + 1, api.nvim_create_namespace('ns-1'))
+ eq(base + 2, api.nvim_create_namespace('ns-2'))
+ eq(base + 1, api.nvim_create_namespace('ns-1'))
+
+ local expected = vim.tbl_extend('error', orig, {
+ ['ns-1'] = base + 1,
+ ['ns-2'] = base + 2,
+ })
+
+ eq(expected, api.nvim_get_namespaces())
+ eq(base + 3, api.nvim_create_namespace(''))
+ eq(base + 4, api.nvim_create_namespace(''))
+ eq(expected, api.nvim_get_namespaces())
end)
end)
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index 7b10eb05ef..11617905dd 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -3,6 +3,8 @@ local n = require('test.functional.testnvim')()
local clear, eq, eval, next_msg, ok, source = n.clear, t.eq, n.eval, n.next_msg, t.ok, n.source
local command, fn, api = n.command, n.fn, n.api
+local feed = n.feed
+local exec_lua = n.exec_lua
local matches = t.matches
local sleep = vim.uv.sleep
local get_session, set_session = n.get_session, n.set_session
@@ -416,6 +418,53 @@ describe('channels', function()
-- works correctly with no output
eq({ 'notification', 'exit', { id, 1, { '' } } }, next_msg())
end)
+
+ it('ChanOpen works with vim.wait() from another autocommand #32706', function()
+ exec_lua([[
+ vim.api.nvim_create_autocmd('ChanOpen', {
+ callback = function(ev)
+ _G.chan = vim.v.event.info.id
+ end,
+ })
+ vim.api.nvim_create_autocmd('InsertEnter', {
+ buffer = 0,
+ callback = function()
+ local chan = vim.fn.jobstart({ 'cat' })
+ _G.result = vim.wait(3000, function()
+ return _G.chan == chan
+ end)
+ end,
+ })
+ ]])
+ feed('i')
+ retry(nil, 4000, function()
+ eq(true, exec_lua('return _G.result'))
+ end)
+ end)
+
+ it('ChanInfo works with vim.wait() from another autocommand #32706', function()
+ exec_lua([[
+ vim.api.nvim_create_autocmd('ChanInfo', {
+ callback = function(ev)
+ _G.foo = vim.v.event.info.client.attributes.foo
+ end,
+ })
+ vim.api.nvim_create_autocmd('InsertEnter', {
+ buffer = 0,
+ callback = function()
+ local chan = vim.fn.jobstart({ 'cat' })
+ _G.result = vim.wait(3000, function()
+ return _G.foo == 'bar'
+ end)
+ end,
+ })
+ ]])
+ feed('i')
+ api.nvim_set_client_info('test', {}, 'remote', {}, { foo = 'bar' })
+ retry(nil, 4000, function()
+ eq(true, exec_lua('return _G.result'))
+ end)
+ end)
end)
describe('loopback', function()
diff --git a/test/functional/editor/defaults_spec.lua b/test/functional/editor/defaults_spec.lua
index 876810ce6f..9843238e35 100644
--- a/test/functional/editor/defaults_spec.lua
+++ b/test/functional/editor/defaults_spec.lua
@@ -10,7 +10,7 @@ local Screen = require('test.functional.ui.screen')
describe('default', function()
describe('autocommands', function()
- it('nvim_terminal.TermClose closes terminal with default shell on success', function()
+ it('nvim.terminal.TermClose closes terminal with default shell on success', function()
n.clear()
n.api.nvim_set_option_value('shell', n.testprg('shell-test'), {})
n.command('set shellcmdflag=EXIT shellredir= shellpipe= shellquote= shellxquote=')
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 5d7ab2ad12..6798d53d2b 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -789,15 +789,19 @@ function tests.code_action_with_resolve()
end,
body = function()
notify('start')
- local cmd = {
- title = 'Command 1',
- command = 'dummy1',
- }
+ local cmd = { title = 'Action 1' }
expect_request('textDocument/codeAction', function()
return nil, { cmd }
end)
expect_request('codeAction/resolve', function()
- return nil, cmd
+ return nil,
+ {
+ title = 'Action 1',
+ command = {
+ title = 'Command 1',
+ command = 'dummy1',
+ },
+ }
end)
notify('shutdown')
end,
diff --git a/test/functional/legacy/breakindent_spec.lua b/test/functional/legacy/breakindent_spec.lua
index db27e19e50..42d28b1231 100644
--- a/test/functional/legacy/breakindent_spec.lua
+++ b/test/functional/legacy/breakindent_spec.lua
@@ -12,11 +12,6 @@ describe('breakindent', function()
-- oldtest: Test_cursor_position_with_showbreak()
it('cursor shown at correct position with showbreak', function()
local screen = Screen.new(75, 6)
- screen:set_default_attr_ids({
- [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
- [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue }, -- SignColumn
- [2] = { bold = true }, -- ModeMsg
- })
exec([[
set listchars=eol:$
let &signcolumn = 'yes'
@@ -29,10 +24,10 @@ describe('breakindent', function()
feed('AX')
screen:expect([[
- {1: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
- {1: }^second line |
- {0:~ }|*3
- {2:-- INSERT --} |
+ {7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
+ {7: }^second line |
+ {1:~ }|*3
+ {5:-- INSERT --} |
]])
-- No line wraps, so changing 'showbreak' should lead to the same screen.
command('setlocal showbreak=+')
@@ -43,19 +38,19 @@ describe('breakindent', function()
-- The first line now wraps because of "eol" in 'listchars'.
command('setlocal list')
screen:expect([[
- {1: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
- {1: } {0:+^$} |
- {1: }second line{0:$} |
- {0:~ }|*2
- {2:-- INSERT --} |
+ {7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
+ {7: } {1:+^$} |
+ {7: }second line{1:$} |
+ {1:~ }|*2
+ {5:-- INSERT --} |
]])
command('setlocal nobreakindent')
screen:expect([[
- {1: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
- {1: }{0:+^$} |
- {1: }second line{0:$} |
- {0:~ }|*2
- {2:-- INSERT --} |
+ {7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX|
+ {7: }{1:+^$} |
+ {7: }second line{1:$} |
+ {1:~ }|*2
+ {5:-- INSERT --} |
]])
end)
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index 819b40323a..62e77e9b85 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -294,41 +294,34 @@ describe('cmdwin', function()
-- oldtest: Test_cmdwin_interrupted()
it('still uses a new buffer when interrupting more prompt on open', function()
local screen = Screen.new(30, 16)
- screen:set_default_attr_ids({
- [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
- [1] = { bold = true, reverse = true }, -- StatusLine
- [2] = { reverse = true }, -- StatusLineNC
- [3] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg
- [4] = { bold = true }, -- ModeMsg
- })
command('set more')
command('autocmd WinNew * highlight')
feed('q:')
- screen:expect({ any = pesc('{3:-- More --}^') })
+ screen:expect({ any = pesc('{6:-- More --}^') })
feed('q')
screen:expect([[
|
- {0:~ }|*5
+ {1:~ }|*5
{2:[No Name] }|
- {0::}^ |
- {0:~ }|*6
- {1:[Command Line] }|
+ {1::}^ |
+ {1:~ }|*6
+ {3:[Command Line] }|
|
]])
feed([[aecho 'done']])
screen:expect([[
|
- {0:~ }|*5
+ {1:~ }|*5
{2:[No Name] }|
- {0::}echo 'done'^ |
- {0:~ }|*6
- {1:[Command Line] }|
- {4:-- INSERT --} |
+ {1::}echo 'done'^ |
+ {1:~ }|*6
+ {3:[Command Line] }|
+ {5:-- INSERT --} |
]])
feed('<CR>')
screen:expect([[
^ |
- {0:~ }|*14
+ {1:~ }|*14
done |
]])
end)
diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua
index 945cd21710..c93b2d96d8 100644
--- a/test/functional/legacy/display_spec.lua
+++ b/test/functional/legacy/display_spec.lua
@@ -45,11 +45,6 @@ describe('display', function()
local function run_test_display_lastline(euro)
local screen = Screen.new(75, 10)
- screen:set_default_attr_ids({
- [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
- [2] = { bold = true, reverse = true }, -- StatusLine
- [3] = { reverse = true }, -- StatusLineNC
- })
exec([[
call setline(1, ['aaa', 'b'->repeat(200)])
set display=truncate
@@ -68,7 +63,7 @@ describe('display', function()
b│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
b│{1:~ }|*3
{1:@}│{1:~ }|
- {2:< }{3:[No Name] [+] }|
+ {3:< }{2:[No Name] [+] }|
|
]]):gsub('@', fillchar)))
@@ -82,7 +77,7 @@ describe('display', function()
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb │b|
{1:~ }│b|*3
{1:~ }│{1:@}|
- {2:[No Name] [+] }{3:<}|
+ {3:[No Name] [+] }{2:<}|
|
]]):gsub('@', fillchar)))
@@ -95,11 +90,11 @@ describe('display', function()
^aaa |
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
{1:@@@ }|
- {2:[No Name] [+] }|
+ {3:[No Name] [+] }|
aaa |
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|*2
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
- {3:[No Name] [+] }|
+ {2:[No Name] [+] }|
|
]]):gsub('@', fillchar)))
@@ -112,7 +107,7 @@ describe('display', function()
bb│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
bb│{1:~ }|*3
{1:@@}│{1:~ }|
- {2:< }{3:[No Name] [+] }|
+ {3:< }{2:[No Name] [+] }|
|
]]):gsub('@', fillchar)))
end
diff --git a/test/functional/legacy/matchparen_spec.lua b/test/functional/legacy/matchparen_spec.lua
index d992420c30..367830b564 100644
--- a/test/functional/legacy/matchparen_spec.lua
+++ b/test/functional/legacy/matchparen_spec.lua
@@ -195,4 +195,48 @@ describe('matchparen', function()
{5:-- INSERT --} |
]])
end)
+
+ -- oldtest: Test_matchparen_ignore_sh_case()
+ it('ignores shell case statements', function()
+ local screen = Screen.new(40, 15)
+ exec([[
+ syntax on
+ source $VIMRUNTIME/plugin/matchparen.vim
+ set ft=sh
+ call setline(1, [
+ \ '#!/bin/sh',
+ \ 'SUSUWU_PRINT() (',
+ \ ' case "${LEVEL}" in',
+ \ ' "$SUSUWU_SH_NOTICE")',
+ \ ' ${SUSUWU_S} && return 1',
+ \ ' ;;',
+ \ ' "$SUSUWU_SH_DEBUG")',
+ \ ' (! ${SUSUWU_VERBOSE}) && return 1',
+ \ ' ;;',
+ \ ' esac',
+ \ ' # snip',
+ \ ')'
+ \ ])
+ call cursor(4, 26)
+ ]])
+ screen:add_extra_attr_ids({
+ [100] = { foreground = tonumber('0x6a0dad') },
+ })
+ screen:expect([[
+ {18:#!/bin/sh} |
+ {25:SUSUWU_PRINT() (} |
+ {15:case} {15:"}{100:${LEVEL}}{15:"} {15:in} |
+ {15:"}{100:$SUSUWU_SH_NOTICE}{15:"^)} |
+ {100:${SUSUWU_S}} {15:&&} {15:return} {26:1} |
+ {15:;;} |
+ {15:"}{100:$SUSUWU_SH_DEBUG}{15:")} |
+ {100:(}{15:!} {100:${SUSUWU_VERBOSE})} {15:&&} {15:return} {26:1} |
+ {15:;;} |
+ {15:esac} |
+ {18:# snip} |
+ {25:)} |
+ {1:~ }|*2
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index f7691fdacc..e73a0780aa 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -122,10 +122,6 @@ describe('messages', function()
-- oldtest: Test_message_more()
it('works', function()
screen = Screen.new(75, 6)
- screen:set_default_attr_ids({
- [1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg
- [2] = { foreground = Screen.colors.Brown }, -- LineNr
- })
command('call setline(1, range(1, 100))')
@@ -140,194 +136,194 @@ describe('messages', function()
]])
feed('\n')
screen:expect([[
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {1:-- More --}^ |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {6:-- More --}^ |
]])
feed('?')
screen:expect([[
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {1:-- More -- SPACE/d/j: screen/page/line down, b/u/k: up, q: quit }^ |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {6:-- More -- SPACE/d/j: screen/page/line down, b/u/k: up, q: quit }^ |
]])
-- Down a line with j, <CR>, <NL> or <Down>.
feed('j')
screen:expect([[
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {2: 6 }6 |
- {1:-- More --}^ |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {8: 6 }6 |
+ {6:-- More --}^ |
]])
feed('<NL>')
screen:expect([[
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {2: 6 }6 |
- {2: 7 }7 |
- {1:-- More --}^ |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {8: 6 }6 |
+ {8: 7 }7 |
+ {6:-- More --}^ |
]])
feed('<CR>')
screen:expect([[
- {2: 4 }4 |
- {2: 5 }5 |
- {2: 6 }6 |
- {2: 7 }7 |
- {2: 8 }8 |
- {1:-- More --}^ |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {8: 6 }6 |
+ {8: 7 }7 |
+ {8: 8 }8 |
+ {6:-- More --}^ |
]])
feed('<Down>')
screen:expect([[
- {2: 5 }5 |
- {2: 6 }6 |
- {2: 7 }7 |
- {2: 8 }8 |
- {2: 9 }9 |
- {1:-- More --}^ |
+ {8: 5 }5 |
+ {8: 6 }6 |
+ {8: 7 }7 |
+ {8: 8 }8 |
+ {8: 9 }9 |
+ {6:-- More --}^ |
]])
-- Down a screen with <Space>, f, or <PageDown>.
feed('f')
screen:expect([[
- {2: 10 }10 |
- {2: 11 }11 |
- {2: 12 }12 |
- {2: 13 }13 |
- {2: 14 }14 |
- {1:-- More --}^ |
+ {8: 10 }10 |
+ {8: 11 }11 |
+ {8: 12 }12 |
+ {8: 13 }13 |
+ {8: 14 }14 |
+ {6:-- More --}^ |
]])
feed('<Space>')
screen:expect([[
- {2: 15 }15 |
- {2: 16 }16 |
- {2: 17 }17 |
- {2: 18 }18 |
- {2: 19 }19 |
- {1:-- More --}^ |
+ {8: 15 }15 |
+ {8: 16 }16 |
+ {8: 17 }17 |
+ {8: 18 }18 |
+ {8: 19 }19 |
+ {6:-- More --}^ |
]])
feed('<PageDown>')
screen:expect([[
- {2: 20 }20 |
- {2: 21 }21 |
- {2: 22 }22 |
- {2: 23 }23 |
- {2: 24 }24 |
- {1:-- More --}^ |
+ {8: 20 }20 |
+ {8: 21 }21 |
+ {8: 22 }22 |
+ {8: 23 }23 |
+ {8: 24 }24 |
+ {6:-- More --}^ |
]])
-- Down a page (half a screen) with d.
feed('d')
screen:expect([[
- {2: 23 }23 |
- {2: 24 }24 |
- {2: 25 }25 |
- {2: 26 }26 |
- {2: 27 }27 |
- {1:-- More --}^ |
+ {8: 23 }23 |
+ {8: 24 }24 |
+ {8: 25 }25 |
+ {8: 26 }26 |
+ {8: 27 }27 |
+ {6:-- More --}^ |
]])
-- Down all the way with 'G'.
feed('G')
screen:expect([[
- {2: 96 }96 |
- {2: 97 }97 |
- {2: 98 }98 |
- {2: 99 }99 |
- {2:100 }100 |
- {1:Press ENTER or type command to continue}^ |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {8: 99 }99 |
+ {8:100 }100 |
+ {6:Press ENTER or type command to continue}^ |
]])
-- Up a line k, <BS> or <Up>.
feed('k')
screen:expect([[
- {2: 95 }95 |
- {2: 96 }96 |
- {2: 97 }97 |
- {2: 98 }98 |
- {2: 99 }99 |
- {1:-- More --}^ |
+ {8: 95 }95 |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {8: 99 }99 |
+ {6:-- More --}^ |
]])
feed('<BS>')
screen:expect([[
- {2: 94 }94 |
- {2: 95 }95 |
- {2: 96 }96 |
- {2: 97 }97 |
- {2: 98 }98 |
- {1:-- More --}^ |
+ {8: 94 }94 |
+ {8: 95 }95 |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {6:-- More --}^ |
]])
feed('<Up>')
screen:expect([[
- {2: 93 }93 |
- {2: 94 }94 |
- {2: 95 }95 |
- {2: 96 }96 |
- {2: 97 }97 |
- {1:-- More --}^ |
+ {8: 93 }93 |
+ {8: 94 }94 |
+ {8: 95 }95 |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {6:-- More --}^ |
]])
-- Up a screen with b or <PageUp>.
feed('b')
screen:expect([[
- {2: 88 }88 |
- {2: 89 }89 |
- {2: 90 }90 |
- {2: 91 }91 |
- {2: 92 }92 |
- {1:-- More --}^ |
+ {8: 88 }88 |
+ {8: 89 }89 |
+ {8: 90 }90 |
+ {8: 91 }91 |
+ {8: 92 }92 |
+ {6:-- More --}^ |
]])
feed('<PageUp>')
screen:expect([[
- {2: 83 }83 |
- {2: 84 }84 |
- {2: 85 }85 |
- {2: 86 }86 |
- {2: 87 }87 |
- {1:-- More --}^ |
+ {8: 83 }83 |
+ {8: 84 }84 |
+ {8: 85 }85 |
+ {8: 86 }86 |
+ {8: 87 }87 |
+ {6:-- More --}^ |
]])
-- Up a page (half a screen) with u.
feed('u')
screen:expect([[
- {2: 80 }80 |
- {2: 81 }81 |
- {2: 82 }82 |
- {2: 83 }83 |
- {2: 84 }84 |
- {1:-- More --}^ |
+ {8: 80 }80 |
+ {8: 81 }81 |
+ {8: 82 }82 |
+ {8: 83 }83 |
+ {8: 84 }84 |
+ {6:-- More --}^ |
]])
-- Up all the way with 'g'.
feed('g')
screen:expect([[
:%p# |
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {1:-- More --}^ |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {6:-- More --}^ |
]])
-- All the way down. Pressing f should do nothing but pressing
-- space should end the more prompt.
feed('G')
screen:expect([[
- {2: 96 }96 |
- {2: 97 }97 |
- {2: 98 }98 |
- {2: 99 }99 |
- {2:100 }100 |
- {1:Press ENTER or type command to continue}^ |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {8: 99 }99 |
+ {8:100 }100 |
+ {6:Press ENTER or type command to continue}^ |
]])
feed('f')
screen:expect_unchanged()
@@ -344,34 +340,34 @@ describe('messages', function()
-- Pressing g< shows the previous command output.
feed('g<lt>')
screen:expect([[
- {2: 96 }96 |
- {2: 97 }97 |
- {2: 98 }98 |
- {2: 99 }99 |
- {2:100 }100 |
- {1:Press ENTER or type command to continue}^ |
+ {8: 96 }96 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {8: 99 }99 |
+ {8:100 }100 |
+ {6:Press ENTER or type command to continue}^ |
]])
-- A command line that doesn't print text is appended to scrollback,
-- even if it invokes a nested command line.
feed([[:<C-R>=':'<CR>:<CR>g<lt>]])
screen:expect([[
- {2: 97 }97 |
- {2: 98 }98 |
- {2: 99 }99 |
- {2:100 }100 |
+ {8: 97 }97 |
+ {8: 98 }98 |
+ {8: 99 }99 |
+ {8:100 }100 |
::: |
- {1:Press ENTER or type command to continue}^ |
+ {6:Press ENTER or type command to continue}^ |
]])
feed(':%p#\n')
screen:expect([[
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {1:-- More --}^ |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {6:-- More --}^ |
]])
-- Stop command output with q, <Esc> or CTRL-C.
@@ -388,30 +384,30 @@ describe('messages', function()
-- Execute a : command from the more prompt
feed(':%p#\n')
screen:expect([[
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
- {1:-- More --}^ |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
+ {6:-- More --}^ |
]])
feed(':')
screen:expect([[
- {2: 1 }1 |
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
+ {8: 1 }1 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
:^ |
]])
feed("echo 'Hello'\n")
screen:expect([[
- {2: 2 }2 |
- {2: 3 }3 |
- {2: 4 }4 |
- {2: 5 }5 |
+ {8: 2 }2 |
+ {8: 3 }3 |
+ {8: 4 }4 |
+ {8: 5 }5 |
Hello |
- {1:Press ENTER or type command to continue}^ |
+ {6:Press ENTER or type command to continue}^ |
]])
end)
diff --git a/test/functional/legacy/number_spec.lua b/test/functional/legacy/number_spec.lua
index 3212524f9d..58a08b9a13 100644
--- a/test/functional/legacy/number_spec.lua
+++ b/test/functional/legacy/number_spec.lua
@@ -12,11 +12,9 @@ describe("'number' and 'relativenumber'", function()
-- oldtest: Test_relativenumber_colors()
it('LineNr, LineNrAbove and LineNrBelow', function()
local screen = Screen.new(50, 10)
- screen:set_default_attr_ids({
- [1] = { foreground = Screen.colors.Red },
- [2] = { foreground = Screen.colors.Blue },
- [3] = { foreground = Screen.colors.Green },
- })
+ screen:add_extra_attr_ids {
+ [100] = { foreground = Screen.colors.WebGreen },
+ }
exec([[
call setline(1, range(200))
111
@@ -24,54 +22,54 @@ describe("'number' and 'relativenumber'", function()
hi LineNr guifg=red
]])
screen:expect([[
- {1: 4 }106 |
- {1: 3 }107 |
- {1: 2 }108 |
- {1: 1 }109 |
- {1:111 }^110 |
- {1: 1 }111 |
- {1: 2 }112 |
- {1: 3 }113 |
- {1: 4 }114 |
+ {19: 4 }106 |
+ {19: 3 }107 |
+ {19: 2 }108 |
+ {19: 1 }109 |
+ {19:111 }^110 |
+ {19: 1 }111 |
+ {19: 2 }112 |
+ {19: 3 }113 |
+ {19: 4 }114 |
|
]])
command('hi LineNrAbove guifg=blue')
screen:expect([[
- {2: 4 }106 |
- {2: 3 }107 |
- {2: 2 }108 |
- {2: 1 }109 |
- {1:111 }^110 |
- {1: 1 }111 |
- {1: 2 }112 |
- {1: 3 }113 |
- {1: 4 }114 |
+ {18: 4 }106 |
+ {18: 3 }107 |
+ {18: 2 }108 |
+ {18: 1 }109 |
+ {19:111 }^110 |
+ {19: 1 }111 |
+ {19: 2 }112 |
+ {19: 3 }113 |
+ {19: 4 }114 |
|
]])
command('hi LineNrBelow guifg=green')
screen:expect([[
- {2: 4 }106 |
- {2: 3 }107 |
- {2: 2 }108 |
- {2: 1 }109 |
- {1:111 }^110 |
- {3: 1 }111 |
- {3: 2 }112 |
- {3: 3 }113 |
- {3: 4 }114 |
+ {18: 4 }106 |
+ {18: 3 }107 |
+ {18: 2 }108 |
+ {18: 1 }109 |
+ {19:111 }^110 |
+ {100: 1 }111 |
+ {100: 2 }112 |
+ {100: 3 }113 |
+ {100: 4 }114 |
|
]])
command('hi clear LineNrAbove')
screen:expect([[
- {1: 4 }106 |
- {1: 3 }107 |
- {1: 2 }108 |
- {1: 1 }109 |
- {1:111 }^110 |
- {3: 1 }111 |
- {3: 2 }112 |
- {3: 3 }113 |
- {3: 4 }114 |
+ {19: 4 }106 |
+ {19: 3 }107 |
+ {19: 2 }108 |
+ {19: 1 }109 |
+ {19:111 }^110 |
+ {100: 1 }111 |
+ {100: 2 }112 |
+ {100: 3 }113 |
+ {100: 4 }114 |
|
]])
end)
@@ -79,12 +77,11 @@ describe("'number' and 'relativenumber'", function()
-- oldtest: Test_relativenumber_colors_wrapped()
it('LineNr, LineNrAbove and LineNrBelow with wrapped lines', function()
local screen = Screen.new(50, 20)
- screen:set_default_attr_ids({
- [1] = { background = Screen.colors.Red, foreground = Screen.colors.Black },
- [2] = { background = Screen.colors.Blue, foreground = Screen.colors.Black },
- [3] = { background = Screen.colors.Green, foreground = Screen.colors.Black },
- [4] = { bold = true, foreground = Screen.colors.Blue },
- })
+ screen:add_extra_attr_ids {
+ [100] = { foreground = Screen.colors.Gray0, background = Screen.colors.Red },
+ [101] = { foreground = Screen.colors.Gray0, background = Screen.colors.Blue1 },
+ [102] = { foreground = Screen.colors.Gray0, background = Screen.colors.WebGreen },
+ }
exec([[
set display=lastline scrolloff=0
call setline(1, range(200)->map('v:val->string()->repeat(40)'))
@@ -95,117 +92,117 @@ describe("'number' and 'relativenumber'", function()
hi LineNrBelow guibg=green guifg=black
]])
screen:expect([[
- {2: 2 }1081081081081081081081081081081081081081081081|
- {2: }0810810810810810810810810810810810810810810810|
- {2: }8108108108108108108108108108 |
- {2: 1 }1091091091091091091091091091091091091091091091|
- {2: }0910910910910910910910910910910910910910910910|
- {2: }9109109109109109109109109109 |
- {1:111 }^1101101101101101101101101101101101101101101101|
- {1: }1011011011011011011011011011011011011011011011|
- {1: }0110110110110110110110110110 |
- {3: 1 }1111111111111111111111111111111111111111111111|
- {3: }1111111111111111111111111111111111111111111111|
- {3: }1111111111111111111111111111 |
- {3: 2 }1121121121121121121121121121121121121121121121|
- {3: }1211211211211211211211211211211211211211211211|
- {3: }2112112112112112112112112112 |
- {3: 3 }1131131131131131131131131131131131131131131131|
- {3: }1311311311311311311311311311311311311311311311|
- {3: }3113113113113113113113113113 |
- {3: 4 }1141141141141141141141141141141141141141141{4:@@@}|
+ {101: 2 }1081081081081081081081081081081081081081081081|
+ {101: }0810810810810810810810810810810810810810810810|
+ {101: }8108108108108108108108108108 |
+ {101: 1 }1091091091091091091091091091091091091091091091|
+ {101: }0910910910910910910910910910910910910910910910|
+ {101: }9109109109109109109109109109 |
+ {100:111 }^1101101101101101101101101101101101101101101101|
+ {100: }1011011011011011011011011011011011011011011011|
+ {100: }0110110110110110110110110110 |
+ {102: 1 }1111111111111111111111111111111111111111111111|
+ {102: }1111111111111111111111111111111111111111111111|
+ {102: }1111111111111111111111111111 |
+ {102: 2 }1121121121121121121121121121121121121121121121|
+ {102: }1211211211211211211211211211211211211211211211|
+ {102: }2112112112112112112112112112 |
+ {102: 3 }1131131131131131131131131131131131131131131131|
+ {102: }1311311311311311311311311311311311311311311311|
+ {102: }3113113113113113113113113113 |
+ {102: 4 }1141141141141141141141141141141141141141141{1:@@@}|
|
]])
feed('k')
screen:expect([[
- {2: 1 }1081081081081081081081081081081081081081081081|
- {2: }0810810810810810810810810810810810810810810810|
- {2: }8108108108108108108108108108 |
- {1:110 }^1091091091091091091091091091091091091091091091|
- {1: }0910910910910910910910910910910910910910910910|
- {1: }9109109109109109109109109109 |
- {3: 1 }1101101101101101101101101101101101101101101101|
- {3: }1011011011011011011011011011011011011011011011|
- {3: }0110110110110110110110110110 |
- {3: 2 }1111111111111111111111111111111111111111111111|
- {3: }1111111111111111111111111111111111111111111111|
- {3: }1111111111111111111111111111 |
- {3: 3 }1121121121121121121121121121121121121121121121|
- {3: }1211211211211211211211211211211211211211211211|
- {3: }2112112112112112112112112112 |
- {3: 4 }1131131131131131131131131131131131131131131131|
- {3: }1311311311311311311311311311311311311311311311|
- {3: }3113113113113113113113113113 |
- {3: 5 }1141141141141141141141141141141141141141141{4:@@@}|
+ {101: 1 }1081081081081081081081081081081081081081081081|
+ {101: }0810810810810810810810810810810810810810810810|
+ {101: }8108108108108108108108108108 |
+ {100:110 }^1091091091091091091091091091091091091091091091|
+ {100: }0910910910910910910910910910910910910910910910|
+ {100: }9109109109109109109109109109 |
+ {102: 1 }1101101101101101101101101101101101101101101101|
+ {102: }1011011011011011011011011011011011011011011011|
+ {102: }0110110110110110110110110110 |
+ {102: 2 }1111111111111111111111111111111111111111111111|
+ {102: }1111111111111111111111111111111111111111111111|
+ {102: }1111111111111111111111111111 |
+ {102: 3 }1121121121121121121121121121121121121121121121|
+ {102: }1211211211211211211211211211211211211211211211|
+ {102: }2112112112112112112112112112 |
+ {102: 4 }1131131131131131131131131131131131131131131131|
+ {102: }1311311311311311311311311311311311311311311311|
+ {102: }3113113113113113113113113113 |
+ {102: 5 }1141141141141141141141141141141141141141141{1:@@@}|
|
]])
feed('2j')
screen:expect([[
- {2: 3 }1081081081081081081081081081081081081081081081|
- {2: }0810810810810810810810810810810810810810810810|
- {2: }8108108108108108108108108108 |
- {2: 2 }1091091091091091091091091091091091091091091091|
- {2: }0910910910910910910910910910910910910910910910|
- {2: }9109109109109109109109109109 |
- {2: 1 }1101101101101101101101101101101101101101101101|
- {2: }1011011011011011011011011011011011011011011011|
- {2: }0110110110110110110110110110 |
- {1:112 }^1111111111111111111111111111111111111111111111|
- {1: }1111111111111111111111111111111111111111111111|
- {1: }1111111111111111111111111111 |
- {3: 1 }1121121121121121121121121121121121121121121121|
- {3: }1211211211211211211211211211211211211211211211|
- {3: }2112112112112112112112112112 |
- {3: 2 }1131131131131131131131131131131131131131131131|
- {3: }1311311311311311311311311311311311311311311311|
- {3: }3113113113113113113113113113 |
- {3: 3 }1141141141141141141141141141141141141141141{4:@@@}|
+ {101: 3 }1081081081081081081081081081081081081081081081|
+ {101: }0810810810810810810810810810810810810810810810|
+ {101: }8108108108108108108108108108 |
+ {101: 2 }1091091091091091091091091091091091091091091091|
+ {101: }0910910910910910910910910910910910910910910910|
+ {101: }9109109109109109109109109109 |
+ {101: 1 }1101101101101101101101101101101101101101101101|
+ {101: }1011011011011011011011011011011011011011011011|
+ {101: }0110110110110110110110110110 |
+ {100:112 }^1111111111111111111111111111111111111111111111|
+ {100: }1111111111111111111111111111111111111111111111|
+ {100: }1111111111111111111111111111 |
+ {102: 1 }1121121121121121121121121121121121121121121121|
+ {102: }1211211211211211211211211211211211211211211211|
+ {102: }2112112112112112112112112112 |
+ {102: 2 }1131131131131131131131131131131131131131131131|
+ {102: }1311311311311311311311311311311311311311311311|
+ {102: }3113113113113113113113113113 |
+ {102: 3 }1141141141141141141141141141141141141141141{1:@@@}|
|
]])
feed('2j')
screen:expect([[
- {2: 5 }1081081081081081081081081081081081081081081081|
- {2: }0810810810810810810810810810810810810810810810|
- {2: }8108108108108108108108108108 |
- {2: 4 }1091091091091091091091091091091091091091091091|
- {2: }0910910910910910910910910910910910910910910910|
- {2: }9109109109109109109109109109 |
- {2: 3 }1101101101101101101101101101101101101101101101|
- {2: }1011011011011011011011011011011011011011011011|
- {2: }0110110110110110110110110110 |
- {2: 2 }1111111111111111111111111111111111111111111111|
- {2: }1111111111111111111111111111111111111111111111|
- {2: }1111111111111111111111111111 |
- {2: 1 }1121121121121121121121121121121121121121121121|
- {2: }1211211211211211211211211211211211211211211211|
- {2: }2112112112112112112112112112 |
- {1:114 }^1131131131131131131131131131131131131131131131|
- {1: }1311311311311311311311311311311311311311311311|
- {1: }3113113113113113113113113113 |
- {3: 1 }1141141141141141141141141141141141141141141{4:@@@}|
+ {101: 5 }1081081081081081081081081081081081081081081081|
+ {101: }0810810810810810810810810810810810810810810810|
+ {101: }8108108108108108108108108108 |
+ {101: 4 }1091091091091091091091091091091091091091091091|
+ {101: }0910910910910910910910910910910910910910910910|
+ {101: }9109109109109109109109109109 |
+ {101: 3 }1101101101101101101101101101101101101101101101|
+ {101: }1011011011011011011011011011011011011011011011|
+ {101: }0110110110110110110110110110 |
+ {101: 2 }1111111111111111111111111111111111111111111111|
+ {101: }1111111111111111111111111111111111111111111111|
+ {101: }1111111111111111111111111111 |
+ {101: 1 }1121121121121121121121121121121121121121121121|
+ {101: }1211211211211211211211211211211211211211211211|
+ {101: }2112112112112112112112112112 |
+ {100:114 }^1131131131131131131131131131131131131131131131|
+ {100: }1311311311311311311311311311311311311311311311|
+ {100: }3113113113113113113113113113 |
+ {102: 1 }1141141141141141141141141141141141141141141{1:@@@}|
|
]])
feed('k')
screen:expect([[
- {2: 4 }1081081081081081081081081081081081081081081081|
- {2: }0810810810810810810810810810810810810810810810|
- {2: }8108108108108108108108108108 |
- {2: 3 }1091091091091091091091091091091091091091091091|
- {2: }0910910910910910910910910910910910910910910910|
- {2: }9109109109109109109109109109 |
- {2: 2 }1101101101101101101101101101101101101101101101|
- {2: }1011011011011011011011011011011011011011011011|
- {2: }0110110110110110110110110110 |
- {2: 1 }1111111111111111111111111111111111111111111111|
- {2: }1111111111111111111111111111111111111111111111|
- {2: }1111111111111111111111111111 |
- {1:113 }^1121121121121121121121121121121121121121121121|
- {1: }1211211211211211211211211211211211211211211211|
- {1: }2112112112112112112112112112 |
- {3: 1 }1131131131131131131131131131131131131131131131|
- {3: }1311311311311311311311311311311311311311311311|
- {3: }3113113113113113113113113113 |
- {3: 2 }1141141141141141141141141141141141141141141{4:@@@}|
+ {101: 4 }1081081081081081081081081081081081081081081081|
+ {101: }0810810810810810810810810810810810810810810810|
+ {101: }8108108108108108108108108108 |
+ {101: 3 }1091091091091091091091091091091091091091091091|
+ {101: }0910910910910910910910910910910910910910910910|
+ {101: }9109109109109109109109109109 |
+ {101: 2 }1101101101101101101101101101101101101101101101|
+ {101: }1011011011011011011011011011011011011011011011|
+ {101: }0110110110110110110110110110 |
+ {101: 1 }1111111111111111111111111111111111111111111111|
+ {101: }1111111111111111111111111111111111111111111111|
+ {101: }1111111111111111111111111111 |
+ {100:113 }^1121121121121121121121121121121121121121121121|
+ {100: }1211211211211211211211211211211211211211211211|
+ {100: }2112112112112112112112112112 |
+ {102: 1 }1131131131131131131131131131131131131131131131|
+ {102: }1311311311311311311311311311311311311311311311|
+ {102: }3113113113113113113113113113 |
+ {102: 2 }1141141141141141141141141141141141141141141{1:@@@}|
|
]])
end)
diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua
index 7c97127193..a0127e306e 100644
--- a/test/functional/legacy/scroll_opt_spec.lua
+++ b/test/functional/legacy/scroll_opt_spec.lua
@@ -1202,14 +1202,9 @@ describe('smoothscroll', function()
it('<<< marker shows with tabline, winbar and splits', function()
screen:try_resize(40, 12)
- screen:set_default_attr_ids({
- [1] = { foreground = Screen.colors.Blue1, bold = true },
- [2] = { reverse = true },
- [3] = { bold = true, reverse = true },
- [4] = { background = Screen.colors.LightMagenta },
- [5] = { bold = true },
- [31] = { foreground = Screen.colors.Fuchsia, bold = true },
- })
+ screen:add_extra_attr_ids {
+ [100] = { foreground = Screen.colors.Magenta1, bold = true },
+ }
exec([[
call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7))
set smoothscroll scrolloff=0
@@ -1232,7 +1227,7 @@ describe('smoothscroll', function()
exec('set showtabline=2')
feed('<C-E>')
screen:expect([[
- {5: }{31:2}{5:+ [No Name] }{2: }|
+ {5: }{100:2}{5:+ [No Name] }{2: }|
{1:<<<}e text with some text with some text |
with some text with some text |
Line with some text with some text with |
@@ -1248,7 +1243,7 @@ describe('smoothscroll', function()
exec('set winbar=winbar')
feed('<C-w>k<C-E>')
screen:expect([[
- {5: }{31:2}{5:+ [No Name] }{2: }|
+ {5: }{100:2}{5:+ [No Name] }{2: }|
{5:winbar }|
{1:<<<}e text with some text with some text |
^with some text with some text |
diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua
index db7d6bc34b..2678f11971 100644
--- a/test/functional/legacy/search_spec.lua
+++ b/test/functional/legacy/search_spec.lua
@@ -18,13 +18,6 @@ describe('search cmdline', function()
clear()
command('set nohlsearch inccommand=')
screen = Screen.new(20, 3)
- screen:set_default_attr_ids({
- inc = { reverse = true },
- err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
- more = { bold = true, foreground = Screen.colors.SeaGreen4 },
- tilde = { bold = true, foreground = Screen.colors.Blue1 },
- hl = { background = Screen.colors.Yellow },
- })
end)
local function tenlines()
@@ -60,57 +53,57 @@ describe('search cmdline', function()
feed('/the')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
feed('<C-G>')
screen:expect([[
2 these |
- 3 {inc:the} |
+ 3 {2:the} |
/the^ |
]])
eq({ 0, 0, 0, 0 }, fn.getpos('"'))
feed('<C-G>')
screen:expect([[
3 the |
- 4 {inc:the}ir |
+ 4 {2:the}ir |
/the^ |
]])
feed('<C-G>')
screen:expect([[
4 their |
- 5 {inc:the}re |
+ 5 {2:the}re |
/the^ |
]])
feed('<C-G>')
screen:expect([[
5 there |
- 6 {inc:the}ir |
+ 6 {2:the}ir |
/the^ |
]])
feed('<C-G>')
screen:expect([[
6 their |
- 7 {inc:the} |
+ 7 {2:the} |
/the^ |
]])
feed('<C-G>')
screen:expect([[
7 the |
- 8 {inc:the}m |
+ 8 {2:the}m |
/the^ |
]])
feed('<C-G>')
screen:expect([[
8 them |
- 9 {inc:the}se |
+ 9 {2:the}se |
/the^ |
]])
screen.bell = false
feed('<C-G>')
if wrapscan == 'wrapscan' then
screen:expect([[
- 2 {inc:the}se |
+ 2 {2:the}se |
3 the |
/the^ |
]])
@@ -118,7 +111,7 @@ describe('search cmdline', function()
screen:expect {
grid = [[
8 them |
- 9 {inc:the}se |
+ 9 {2:the}se |
/the^ |
]],
condition = function()
@@ -136,7 +129,7 @@ describe('search cmdline', function()
feed('?the')
screen:expect([[
- 9 {inc:the}se |
+ 9 {2:the}se |
10 foobar |
?the^ |
]])
@@ -144,7 +137,7 @@ describe('search cmdline', function()
if wrapscan == 'wrapscan' then
feed('<C-G>')
screen:expect([[
- 2 {inc:the}se |
+ 2 {2:the}se |
3 the |
?the^ |
]])
@@ -158,7 +151,7 @@ describe('search cmdline', function()
feed('<C-G>')
screen:expect {
grid = [[
- 9 {inc:the}se |
+ 9 {2:the}se |
10 foobar |
?the^ |
]],
@@ -176,13 +169,13 @@ describe('search cmdline', function()
command('$')
feed('?the')
screen:expect([[
- 9 {inc:the}se |
+ 9 {2:the}se |
10 foobar |
?the^ |
]])
feed('<C-T>')
screen:expect([[
- 8 {inc:the}m |
+ 8 {2:the}m |
9 these |
?the^ |
]])
@@ -195,7 +188,7 @@ describe('search cmdline', function()
end
end
screen:expect([[
- 2 {inc:the}se |
+ 2 {2:the}se |
3 the |
?the^ |
]])
@@ -203,14 +196,14 @@ describe('search cmdline', function()
feed('<C-T>')
if wrapscan == 'wrapscan' then
screen:expect([[
- 9 {inc:the}se |
+ 9 {2:the}se |
10 foobar |
?the^ |
]])
else
screen:expect {
grid = [[
- 2 {inc:the}se |
+ 2 {2:the}se |
3 the |
?the^ |
]],
@@ -245,24 +238,24 @@ describe('search cmdline', function()
feed('/the')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
feed('<C-L>')
screen:expect([[
1 |
- 2 {inc:thes}e |
+ 2 {2:thes}e |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
- 9 {inc:thes}e |
+ 9 {2:thes}e |
10 foobar |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
- 2 {inc:thes}e |
+ 2 {2:thes}e |
3 the |
/thes^ |
]])
@@ -278,18 +271,18 @@ describe('search cmdline', function()
feed('/the')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
feed('<C-L>')
screen:expect([[
1 |
- 2 {inc:thes}e |
+ 2 {2:thes}e |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
- 9 {inc:thes}e |
+ 9 {2:thes}e |
10 foobar |
/thes^ |
]])
@@ -309,41 +302,41 @@ describe('search cmdline', function()
feed('/thei')
screen:expect([[
3 the |
- 4 {inc:thei}r |
+ 4 {2:thei}r |
/thei^ |
]])
-- Match from initial cursor position when modifying search
feed('<BS>')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
-- New text advances to next match
feed('s')
screen:expect([[
1 |
- 2 {inc:thes}e |
+ 2 {2:thes}e |
/thes^ |
]])
-- Stay on this match when deleting a character
feed('<BS>')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
-- Advance to previous match
feed('<C-T>')
screen:expect([[
- 9 {inc:the}se |
+ 9 {2:the}se |
10 foobar |
/the^ |
]])
-- Extend search to include next character
feed('<C-L>')
screen:expect([[
- 9 {inc:thes}e |
+ 9 {2:thes}e |
10 foobar |
/thes^ |
]])
@@ -357,13 +350,13 @@ describe('search cmdline', function()
feed('the')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
feed('\\>')
screen:expect([[
2 these |
- 3 {inc:the} |
+ 3 {2:the} |
/the\>^ |
]])
end)
@@ -377,7 +370,7 @@ describe('search cmdline', function()
feed('/the')
screen:expect([[
1 |
- 2 {inc:the}se |
+ 2 {2:the}se |
/the^ |
]])
@@ -385,7 +378,7 @@ describe('search cmdline', function()
feed('<C-G>')
screen:expect([[
2 these |
- 3 {inc:the} theother |
+ 3 {2:the} theother |
/the^ |
]])
@@ -393,13 +386,13 @@ describe('search cmdline', function()
feed('<C-G>')
screen:expect([[
2 these |
- 3 the {inc:the}other |
+ 3 the {2:the}other |
/the^ |
]])
feed('<C-G>')
screen:expect([[
2 these |
- 3 the theo{inc:the}r |
+ 3 the theo{2:the}r |
/the^ |
]])
@@ -407,20 +400,20 @@ describe('search cmdline', function()
feed('<C-T>')
screen:expect([[
2 these |
- 3 the {inc:the}other |
+ 3 the {2:the}other |
/the^ |
]])
feed('<C-T>')
screen:expect([[
2 these |
- 3 {inc:the} theother |
+ 3 {2:the} theother |
/the^ |
]])
-- Previous match, different line
feed('<C-T>')
screen:expect([[
- 2 {inc:the}se |
+ 2 {2:the}se |
3 the theother |
/the^ |
]])
@@ -436,7 +429,7 @@ describe('search cmdline', function()
7 the |
8 them |
9 these |
- 10 {inc:foo}bar |
+ 10 {2:foo}bar |
/foo^ |
]])
feed('<BS>')
@@ -445,7 +438,7 @@ describe('search cmdline', function()
7 the |
8 them |
9 these |
- 10 {inc:fo}obar |
+ 10 {2:fo}obar |
/fo^ |
]])
feed('<CR>')
@@ -476,7 +469,7 @@ describe('search cmdline', function()
feed('/foo')
screen:expect([[
9 these |
- 10 {inc:foo}bar |
+ 10 {2:foo}bar |
/foo^ |
]])
feed('<C-W>')
@@ -488,8 +481,8 @@ describe('search cmdline', function()
feed('<CR>')
screen:expect([[
/ |
- {err:E35: No previous regular expression} |
- {more:Press ENTER or type command to continue}^ |
+ {9:E35: No previous regular expression} |
+ {6:Press ENTER or type command to continue}^ |
]])
feed('<CR>')
eq({
@@ -613,9 +606,9 @@ describe('search cmdline', function()
feed(':sort ni u /on')
screen:expect([[
- another {inc:on}e 2 |
- that {hl:on}e 3 |
- the {hl:on}e 1 |
+ another {2:on}e 2 |
+ that {10:on}e 3 |
+ the {10:on}e 1 |
:sort ni u /on^ |
]])
feed('<esc>')
@@ -629,27 +622,27 @@ describe('search cmdline', function()
feed(':vimgrep on')
screen:expect([[
- another {inc:on}e 2 |
- that {hl:on}e 3 |
- the {hl:on}e 1 |
+ another {2:on}e 2 |
+ that {10:on}e 3 |
+ the {10:on}e 1 |
:vimgrep on^ |
]])
feed('<esc>')
feed(':vimg /on/ *.txt')
screen:expect([[
- another {inc:on}e 2 |
- that {hl:on}e 3 |
- the {hl:on}e 1 |
+ another {2:on}e 2 |
+ that {10:on}e 3 |
+ the {10:on}e 1 |
:vimg /on/ *.txt^ |
]])
feed('<esc>')
feed(':vimgrepadd "\\<LT>on')
screen:expect([[
- another {inc:on}e 2 |
- that {hl:on}e 3 |
- the {hl:on}e 1 |
+ another {2:on}e 2 |
+ that {10:on}e 3 |
+ the {10:on}e 1 |
:vimgrepadd "\<on^ |
]])
feed('<esc>')
@@ -657,7 +650,7 @@ describe('search cmdline', function()
feed(':lv "tha')
screen:expect([[
another one 2 |
- {inc:tha}t one 3 |
+ {2:tha}t one 3 |
the one 1 |
:lv "tha^ |
]])
@@ -665,9 +658,9 @@ describe('search cmdline', function()
feed(':lvimgrepa "the" **/*.txt')
screen:expect([[
- ano{inc:the}r one 2 |
+ ano{2:the}r one 2 |
that one 3 |
- {hl:the} one 1 |
+ {10:the} one 1 |
:lvimgrepa "the" **/*.txt^ |
]])
feed('<esc>')
@@ -718,7 +711,7 @@ describe('search cmdline', function()
call feedkeys("2\<C-E>", 't')
]])
local s = [[
- {tilde:<<<} 18 19 20 21 22 2|
+ {1:<<<} 18 19 20 21 22 2|
^3 24 |
|*4
]]
@@ -726,13 +719,13 @@ describe('search cmdline', function()
feed('/xx')
screen:expect([[
|*4
- {inc:xx}x |
+ {2:xx}x |
/xx^ |
]])
feed('x')
screen:expect([[
|*4
- {inc:xxx} |
+ {2:xxx} |
/xxx^ |
]])
feed('<Esc>')
@@ -764,13 +757,14 @@ describe('Search highlight', function()
-- oldtest: Test_hlsearch_and_visual()
it('is combined with Visual highlight vim-patch:8.2.2797', function()
local screen = Screen.new(40, 6)
- screen:set_default_attr_ids({
- [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
- [2] = { bold = true }, -- ModeMsg, Search
- [3] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual
- [4] = { background = Screen.colors.Yellow, bold = true }, -- Search
- [5] = { background = Screen.colors.LightGrey, bold = true, foreground = Screen.colors.Black },
- })
+ screen:add_extra_attr_ids {
+ [100] = {
+ foreground = Screen.colors.Black,
+ bold = true,
+ background = Screen.colors.LightGrey,
+ },
+ [101] = { bold = true, background = Screen.colors.Yellow },
+ }
exec([[
set hlsearch noincsearch
call setline(1, repeat(["xxx yyy zzz"], 3))
@@ -780,11 +774,11 @@ describe('Search highlight', function()
]])
feed('vjj')
screen:expect([[
- xxx {4:y}{5:yy}{3: zzz} |
- {3:xxx }{5:yyy}{3: zzz} |
- {3:xxx }{5:y}{4:^yy} zzz |
+ xxx {101:y}{100:yy}{17: zzz} |
+ {17:xxx }{100:yyy}{17: zzz} |
+ {17:xxx }{100:y}{101:^yy} zzz |
{1:~ }|*2
- {2:-- VISUAL --} |
+ {5:-- VISUAL --} |
]])
end)
end)
diff --git a/test/functional/legacy/search_stat_spec.lua b/test/functional/legacy/search_stat_spec.lua
index 1ffae89b0c..1ba55e583b 100644
--- a/test/functional/legacy/search_stat_spec.lua
+++ b/test/functional/legacy/search_stat_spec.lua
@@ -8,13 +8,6 @@ describe('search stat', function()
before_each(function()
clear()
screen = Screen.new(30, 10)
- screen:set_default_attr_ids({
- [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
- [2] = { background = Screen.colors.Yellow }, -- Search
- [3] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey }, -- Folded
- [4] = { reverse = true }, -- IncSearch, TabLineFill
- [5] = { foreground = Screen.colors.Red }, -- WarningMsg
- })
end)
-- oldtest: Test_search_stat_screendump()
@@ -32,7 +25,7 @@ describe('search stat', function()
]])
screen:expect([[
foobar |
- {2:^find this} |
+ {10:^find this} |
fooooobar |
foba |
foobar |*2
@@ -45,7 +38,7 @@ describe('search stat', function()
feed('gg0n')
screen:expect([[
foobar |
- {2:^find this} |
+ {10:^find this} |
fooooobar |
foba |
foobar |*2
@@ -68,7 +61,7 @@ describe('search stat', function()
]])
screen:expect([[
if |
- {3:^+-- 2 lines: foo·············}|
+ {13:^+-- 2 lines: foo·············}|
endif |
|
{1:~ }|*5
@@ -91,16 +84,16 @@ describe('search stat', function()
feed('/dog<CR>')
screen:expect([[
int cat; |
- int {2:^dog}; |
- cat = {2:dog}; |
+ int {10:^dog}; |
+ cat = {10:dog}; |
{1:~ }|*6
/dog [1/2] |
]])
feed('G0gD')
screen:expect([[
- int {2:^cat}; |
+ int {10:^cat}; |
int dog; |
- {2:cat} = dog; |
+ {10:cat} = dog; |
{1:~ }|*6
|
]])
@@ -128,30 +121,30 @@ describe('search stat', function()
feed('/abc')
screen:expect([[
- {4: }|
- {2:abc}--c |
- --------{4:abc} |
- --{2:abc} |
+ {2: }|
+ {10:abc}--c |
+ --------{2:abc} |
+ --{10:abc} |
{1:~ }|*5
/abc^ |
]])
feed('<C-G>')
screen:expect([[
- {4:3/3 }|
- {2:abc}--c |
- --------{2:abc} |
- --{4:abc} |
+ {2:3/3 }|
+ {10:abc}--c |
+ --------{10:abc} |
+ --{2:abc} |
{1:~ }|*5
/abc^ |
]])
feed('<C-G>')
screen:expect([[
- {4:1/3 }|
- {4:abc}--c |
- --------{2:abc} |
- --{2:abc} |
+ {2:1/3 }|
+ {2:abc}--c |
+ --------{10:abc} |
+ --{10:abc} |
{1:~ }|*5
/abc^ |
]])
@@ -167,7 +160,7 @@ describe('search stat', function()
feed('*')
screen:expect([[
- {2:^test} |
+ {10:^test} |
|
{1:~ }|*7
/\<test\> [1/1] |
@@ -175,7 +168,7 @@ describe('search stat', function()
feed('N')
screen:expect([[
- {2:^test} |
+ {10:^test} |
|
{1:~ }|*7
?\<test\> [1/1] |
@@ -185,10 +178,10 @@ describe('search stat', function()
feed('N')
-- shows "Search Hit Bottom.."
screen:expect([[
- {2:^test} |
+ {10:^test} |
|
{1:~ }|*7
- {5:search hit TOP, continuing at BOTTOM} |
+ {19:search hit TOP, continuing at BOTTOM} |
]])
end)
end)
diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua
index fb55611198..cb3b436f9e 100644
--- a/test/functional/lua/commands_spec.lua
+++ b/test/functional/lua/commands_spec.lua
@@ -173,8 +173,13 @@ describe(':lua', function()
exec_lua('x = 5')
eq('5', exec_capture(':lua =x'))
eq('5', exec_capture('=x'))
+ exec_lua('x = "5"')
+ eq('"5"', exec_capture(':lua =x'))
+ eq('"5"', exec_capture('=x'))
exec_lua("function x() return 'hello' end")
- eq('hello', exec_capture(':lua = x()'))
+ eq('"hello"', exec_capture(':lua = x()'))
+ exec_lua("function x() return 'hello ' end")
+ eq('"hello "', exec_capture(':lua = x()'))
exec_lua('x = {a = 1, b = 2}')
eq('{\n a = 1,\n b = 2\n}', exec_capture(':lua =x'))
exec_lua(function()
@@ -189,14 +194,14 @@ describe(':lua', function()
eq(
dedent [[
true
- Return value]],
+ "Return value"]],
exec_capture(':lua =x(true)')
)
eq(
dedent [[
false
nil
- Error message]],
+ "Error message"]],
exec_capture('=x(false)')
)
end)
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index a19f558ef2..e9636742d7 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -2231,6 +2231,110 @@ describe('vim.diagnostic', function()
eq('Another error there!', result[2][4].virt_lines[1][3][1])
end)
+ it('highlights multiple diagnostics in a single line by default', function()
+ local result = exec_lua(function()
+ vim.api.nvim_buf_set_lines(
+ _G.diagnostic_bufnr,
+ 0,
+ -1,
+ false,
+ { 'def foo(x: int, /, y: str, *, z: float) -> None: ...' }
+ )
+
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, 8, 0, 9, 'foo_server'),
+ _G.make_error('Another error there!', 0, 19, 0, 20, 'foo_server'),
+ _G.make_error('And another one!', 0, 30, 0, 31, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ --[[
+ |def foo(x: int, /, y: str, *, z: float) -> None: ...
+ | │ │ └──── And another one!
+ | │ └──── Another error there!
+ | └──── Error here!
+ | ^ col=8
+ | ^ col=19
+ | ^ col=30
+
+ 11 cols between each diagnostic after the first one (10 spaces + |)
+ ]]
+
+ eq(1, #result)
+ local virt_lines = result[1][4].virt_lines
+ eq(8, virt_lines[1][1][1]:len()) -- first space
+ eq(10, virt_lines[1][3][1]:len()) -- second space
+ eq(10, virt_lines[1][5][1]:len()) -- third space
+ eq('And another one!', virt_lines[1][7][1])
+ eq(8, virt_lines[2][1][1]:len()) -- first space
+ eq(10, virt_lines[2][3][1]:len()) -- second space
+ eq('Another error there!', virt_lines[2][5][1])
+ eq(8, virt_lines[3][1][1]:len()) -- first space
+ eq('Error here!', virt_lines[3][3][1])
+ end)
+
+ it('highlights overlapping diagnostics on a single line', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, 10, 0, 11, 'foo_server'),
+ _G.make_error('Another error here!', 0, 10, 0, 11, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ --[[
+ |1234567890x
+ | ├──── Another error here!
+ | └──── Error here!
+ ]]
+
+ eq(1, #result)
+ local virt_lines = result[1][4].virt_lines
+ eq(10, virt_lines[1][1][1]:len()) -- first space
+ eq('├──── ', virt_lines[1][2][1])
+ eq('Another error here!', virt_lines[1][3][1])
+ eq(10, virt_lines[2][1][1]:len()) -- second space
+ eq('└──── ', virt_lines[2][2][1])
+ eq('Error here!', virt_lines[2][3][1])
+ end)
+
+ it('handles multi-line diagnostic message', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!\ngot another line', 0, 10, 0, 11, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ --[[
+ |1234567890x
+ | └──── Error here!
+ | got another line
+ ]]
+
+ eq(1, #result)
+ local virt_lines = result[1][4].virt_lines
+ eq(10, virt_lines[1][1][1]:len()) -- first space
+ eq('└──── ', virt_lines[1][2][1])
+ eq('Error here!', virt_lines[1][3][1])
+ eq(10, virt_lines[2][1][1]:len()) -- second line space
+ eq(6, virt_lines[2][2][1]:len()) -- extra padding
+ eq('got another line', virt_lines[2][3][1])
+ end)
+
it('can highlight diagnostics only in the current line', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
diff --git a/test/functional/lua/inspector_spec.lua b/test/functional/lua/inspector_spec.lua
index 3a1263f6a3..89a103f51f 100644
--- a/test/functional/lua/inspector_spec.lua
+++ b/test/functional/lua/inspector_spec.lua
@@ -12,7 +12,7 @@ describe('vim.inspect_pos', function()
end)
it('it returns items', function()
- local buf, items, other_buf_syntax = exec_lua(function()
+ local buf, ns1, ns2, items, other_buf_syntax = exec_lua(function()
local buf = vim.api.nvim_create_buf(true, false)
local buf1 = vim.api.nvim_create_buf(true, false)
local ns1 = vim.api.nvim_create_namespace('ns1')
@@ -25,7 +25,7 @@ describe('vim.inspect_pos', function()
vim.api.nvim_buf_set_extmark(buf, ns1, 0, 10, { hl_group = 'Normal' })
vim.api.nvim_buf_set_extmark(buf, ns2, 0, 10, { hl_group = 'Normal' })
vim.cmd('syntax on')
- return buf, vim.inspect_pos(0, 0, 10), vim.inspect_pos(buf1, 0, 10).syntax
+ return buf, ns1, ns2, vim.inspect_pos(0, 0, 10), vim.inspect_pos(buf1, 0, 10).syntax
end)
eq('', eval('v:errmsg'))
@@ -40,12 +40,12 @@ describe('vim.inspect_pos', function()
end_row = 0,
id = 1,
ns = 'ns1',
- ns_id = 1,
+ ns_id = ns1,
opts = {
hl_eol = false,
hl_group = 'Normal',
hl_group_link = 'Normal',
- ns_id = 1,
+ ns_id = ns1,
priority = 4096,
right_gravity = true,
},
@@ -57,12 +57,12 @@ describe('vim.inspect_pos', function()
end_row = 0,
id = 1,
ns = '',
- ns_id = 2,
+ ns_id = ns2,
opts = {
hl_eol = false,
hl_group = 'Normal',
hl_group_link = 'Normal',
- ns_id = 2,
+ ns_id = ns2,
priority = 4096,
right_gravity = true,
},
diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua
index 8df2900368..fbf70d7be7 100644
--- a/test/functional/lua/loop_spec.lua
+++ b/test/functional/lua/loop_spec.lua
@@ -194,4 +194,52 @@ describe('vim.uv', function()
feed('<cr>')
n.assert_alive()
end)
+
+ it("doesn't crash on async callbacks throwing nil error", function()
+ local screen = Screen.new(50, 4)
+
+ exec_lua(function()
+ _G.idle = vim.uv.new_idle()
+ _G.idle:start(function()
+ _G.idle:stop()
+ error()
+ end)
+ end)
+
+ screen:expect([[
+ {3: }|
+ {9:Error executing callback:} |
+ {9:[NULL]} |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+
+ exec_lua(function()
+ _G.idle:close()
+ end)
+ end)
+
+ it("doesn't crash on async callbacks throwing object as an error", function()
+ local screen = Screen.new(50, 4)
+
+ exec_lua(function()
+ _G.idle = vim.uv.new_idle()
+ _G.idle:start(function()
+ _G.idle:stop()
+ error(_G.idle) -- userdata with __tostring method
+ end)
+ end)
+
+ screen:expect([[
+ {3: }|
+ {9:Error executing callback:} |
+ {9:uv_idle_t: 0x{MATCH:%w+}} |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+
+ exec_lua(function()
+ _G.idle:close()
+ end)
+ end)
end)
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index ddb10127e4..8159a4c91c 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -372,7 +372,7 @@ describe('vim.ui_attach', function()
|
{3: }|
{9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 1.} |
+ {9:back from ns: 2.} |
{100:Press ENTER or type command to continue}^ |
]])
feed('<cr>')
@@ -419,7 +419,7 @@ describe('vim.ui_attach', function()
|
{3: }|
{9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 2.} |
+ {9:back from ns: 3.} |
{100:Press ENTER or type command to continue}^ |
]])
end)
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index ccefc3cb99..7bfb354c2e 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -368,6 +368,7 @@ describe('vim.lsp.util', function()
]])
-- Correct height when float inherits 'conceallevel' >= 2 #32639
command('close | set conceallevel=2')
+ feed('<Ignore>') -- Prevent CursorMoved closing the next float immediately
exec_lua([[
vim.lsp.util.open_floating_preview({ '```lua', 'local foo', '```' }, 'markdown', {
border = 'single',
@@ -382,5 +383,47 @@ describe('vim.lsp.util', function()
{1:~ }|*9
|
]])
+ -- This tests the valid winline code path (why doesn't the above?).
+ exec_lua([[
+ vim.cmd.only()
+ vim.lsp.util.open_floating_preview({ 'foo', '```lua', 'local bar', '```' }, 'markdown', {
+ border = 'single',
+ focus = false,
+ })
+ ]])
+ feed('<C-W>wG')
+ screen:expect([[
+ |
+ ┌─────────┐{1: }|
+ │{4:foo }│{1: }|
+ │{100:^local}{101: }{102:bar}│{1: }|
+ └─────────┘{1: }|
+ {1:~ }|*8
+ |
+ ]])
+ end)
+
+ it('open_floating_preview height does not exceed max_height', function()
+ local screen = Screen.new()
+ exec_lua([[
+ vim.lsp.util.open_floating_preview(vim.fn.range(1, 10), 'markdown', {
+ border = 'single',
+ width = 5,
+ max_height = 5,
+ focus = false,
+ })
+ ]])
+ screen:expect([[
+ ^ |
+ ┌─────┐{1: }|
+ │{4:1 }│{1: }|
+ │{4:2 }│{1: }|
+ │{4:3 }│{1: }|
+ │{4:4 }│{1: }|
+ │{4:5 }│{1: }|
+ └─────┘{1: }|
+ {1:~ }|*5
+ |
+ ]])
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 17e3fbbf70..22c9e4658f 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -2526,7 +2526,7 @@ describe('LSP', function()
)
end)
- it('Supports file creation with CreateFile payload', function()
+ it('supports file creation with CreateFile payload', function()
local tmpfile = tmpname(false)
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
local edit = {
@@ -2544,7 +2544,7 @@ describe('LSP', function()
end)
it(
- 'Supports file creation in folder that needs to be created with CreateFile payload',
+ 'supports file creation in folder that needs to be created with CreateFile payload',
function()
local tmpfile = tmpname(false) .. '/dummy/x/'
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
@@ -2647,7 +2647,7 @@ describe('LSP', function()
describe('lsp.util.rename', function()
local pathsep = n.get_pathsep()
- it('Can rename an existing file', function()
+ it('can rename an existing file', function()
local old = tmpname()
write_file(old, 'Test content')
local new = tmpname(false)
@@ -2668,7 +2668,7 @@ describe('LSP', function()
os.remove(new)
end)
- it('Can rename a directory', function()
+ it('can rename a directory', function()
-- only reserve the name, file must not exist for the test scenario
local old_dir = tmpname(false)
local new_dir = tmpname(false)
@@ -2695,7 +2695,7 @@ describe('LSP', function()
os.remove(new_dir)
end)
- it('Does not touch buffers that do not match path prefix', function()
+ it('does not touch buffers that do not match path prefix', function()
local old = tmpname(false)
local new = tmpname(false)
n.mkdir_p(old)
@@ -2730,7 +2730,7 @@ describe('LSP', function()
end)
it(
- 'Does not rename file if target exists and ignoreIfExists is set or overwrite is false',
+ 'does not rename file if target exists and ignoreIfExists is set or overwrite is false',
function()
local old = tmpname()
write_file(old, 'Old File')
@@ -2753,7 +2753,7 @@ describe('LSP', function()
end
)
- it('Maintains undo information for loaded buffer', function()
+ it('maintains undo information for loaded buffer', function()
local old = tmpname()
write_file(old, 'line')
local new = tmpname(false)
@@ -2777,7 +2777,7 @@ describe('LSP', function()
eq(true, undo_kept)
end)
- it('Maintains undo information for unloaded buffer', function()
+ it('maintains undo information for unloaded buffer', function()
local old = tmpname()
write_file(old, 'line')
local new = tmpname(false)
@@ -2798,7 +2798,7 @@ describe('LSP', function()
eq(true, undo_kept)
end)
- it('Does not rename file when it conflicts with a buffer without file', function()
+ it('does not rename file when it conflicts with a buffer without file', function()
local old = tmpname()
write_file(old, 'Old File')
local new = tmpname(false)
@@ -2817,7 +2817,7 @@ describe('LSP', function()
eq('Old File', read_file(old))
end)
- it('Does override target if overwrite is true', function()
+ it('does override target if overwrite is true', function()
local old = tmpname()
write_file(old, 'Old file')
local new = tmpname()
@@ -2833,7 +2833,7 @@ describe('LSP', function()
end)
describe('lsp.util.locations_to_items', function()
- it('Convert Location[] to items', function()
+ it('convert Location[] to items', function()
local expected_template = {
{
filename = '/fake/uri',
@@ -2879,7 +2879,7 @@ describe('LSP', function()
end
end)
- it('Convert LocationLink[] to items', function()
+ it('convert LocationLink[] to items', function()
local expected = {
{
filename = '/fake/uri',
@@ -2926,7 +2926,7 @@ describe('LSP', function()
describe('lsp.util.symbols_to_items', function()
describe('convert DocumentSymbol[] to items', function()
- it('DocumentSymbol has children', function()
+ it('documentSymbol has children', function()
local expected = {
{
col = 1,
@@ -3047,7 +3047,7 @@ describe('LSP', function()
)
end)
- it('DocumentSymbol has no children', function()
+ it('documentSymbol has no children', function()
local expected = {
{
col = 1,
@@ -4387,7 +4387,7 @@ describe('LSP', function()
end)
describe('vim.lsp.buf.code_action', function()
- it('Calls client side command if available', function()
+ it('calls client side command if available', function()
local client --- @type vim.lsp.Client
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
@@ -4431,7 +4431,7 @@ describe('LSP', function()
}
end)
- it('Calls workspace/executeCommand if no client side command', function()
+ it('calls workspace/executeCommand if no client side command', function()
local client --- @type vim.lsp.Client
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
@@ -4472,7 +4472,7 @@ describe('LSP', function()
})
end)
- it('Filters and automatically applies action if requested', function()
+ it('filters and automatically applies action if requested', function()
local client --- @type vim.lsp.Client
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
@@ -4547,7 +4547,7 @@ describe('LSP', function()
}
end)
- it('Fallback to command execution on resolve error', function()
+ it('fallback to command execution on resolve error', function()
clear()
exec_lua(create_server_definition)
local result = exec_lua(function()
@@ -4591,17 +4591,62 @@ describe('LSP', function()
eq('workspace/executeCommand', result[5].method)
eq('command:1', result[5].params.command)
end)
+
+ it('resolves command property', function()
+ clear()
+ exec_lua(create_server_definition)
+ local result = exec_lua(function()
+ local server = _G._create_server({
+ capabilities = {
+ executeCommandProvider = {
+ commands = { 'command:1' },
+ },
+ codeActionProvider = {
+ resolveProvider = true,
+ },
+ },
+ handlers = {
+ ['textDocument/codeAction'] = function(_, _, callback)
+ callback(nil, {
+ { title = 'Code Action 1' },
+ })
+ end,
+ ['codeAction/resolve'] = function(_, _, callback)
+ callback(nil, {
+ title = 'Code Action 1',
+ command = {
+ title = 'Command 1',
+ command = 'command:1',
+ },
+ })
+ end,
+ },
+ })
+
+ local client_id = assert(vim.lsp.start({
+ name = 'dummy',
+ cmd = server.cmd,
+ }))
+
+ vim.lsp.buf.code_action({ apply = true })
+ vim.lsp.stop_client(client_id)
+ return server.messages
+ end)
+ eq('codeAction/resolve', result[4].method)
+ eq('workspace/executeCommand', result[5].method)
+ eq('command:1', result[5].params.command)
+ end)
end)
describe('vim.lsp.commands', function()
- it('Accepts only string keys', function()
+ it('accepts only string keys', function()
matches(
'.*The key for commands in `vim.lsp.commands` must be a string',
pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end')
)
end)
- it('Accepts only function values', function()
+ it('accepts only function values', function()
matches(
'.*Command added to `vim.lsp.commands` must be a function',
pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10')
@@ -4835,7 +4880,7 @@ describe('LSP', function()
end)
describe('vim.lsp.buf.format', function()
- it('Aborts with notify if no client matches filter', function()
+ it('aborts with notify if no client matches filter', function()
local client --- @type vim.lsp.Client
test_rpc_server {
test_name = 'basic_init',
@@ -4861,7 +4906,7 @@ describe('LSP', function()
}
end)
- it('Sends textDocument/formatting request to format buffer', function()
+ it('sends textDocument/formatting request to format buffer', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
@@ -4895,7 +4940,7 @@ describe('LSP', function()
}
end)
- it('Sends textDocument/rangeFormatting request to format a range', function()
+ it('sends textDocument/rangeFormatting request to format a range', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
@@ -4936,7 +4981,7 @@ describe('LSP', function()
}
end)
- it('Sends textDocument/rangesFormatting request to format multiple ranges', function()
+ it('sends textDocument/rangesFormatting request to format multiple ranges', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
@@ -4983,7 +5028,7 @@ describe('LSP', function()
}
end)
- it('Can format async', function()
+ it('can format async', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
{ NIL, {}, { method = 'start', client_id = 1 } },
@@ -5112,7 +5157,7 @@ describe('LSP', function()
eq(expected_range, result[5].params.range)
end)
- it('Aborts with notify if no clients support requested method', function()
+ it('aborts with notify if no clients support requested method', function()
exec_lua(create_server_definition)
exec_lua(function()
vim.notify = function(msg, _)
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index f2d679bd5d..599adc87e1 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -333,60 +333,151 @@ describe(':terminal buffer', function()
command('bdelete!')
end)
- it('emits TermRequest events #26972', function()
- local term = api.nvim_open_term(0, {})
- local termbuf = api.nvim_get_current_buf()
-
- -- Test that <abuf> is the terminal buffer, not the current buffer
- command('au TermRequest * let g:termbuf = +expand("<abuf>")')
- command('wincmd p')
-
- -- cwd will be inserted in a file URI, which cannot contain backs
- local cwd = t.fix_slashes(fn.getcwd())
- local parent = cwd:match('^(.+/)')
- local expected = '\027]7;file://host' .. parent
- api.nvim_chan_send(term, string.format('%s\027\\', expected))
- eq(expected, eval('v:termrequest'))
- eq(termbuf, eval('g:termbuf'))
- end)
-
- it('emits TermRequest events for APC', function()
- local term = api.nvim_open_term(0, {})
-
- -- cwd will be inserted in a file URI, which cannot contain backs
- local cwd = t.fix_slashes(fn.getcwd())
- local parent = cwd:match('^(.+/)')
- local expected = '\027_Gfile://host' .. parent
- api.nvim_chan_send(term, string.format('%s\027\\', expected))
- eq(expected, eval('v:termrequest'))
- end)
-
- it('TermRequest synchronization #27572', function()
- command('autocmd! nvim.terminal TermRequest')
- local term = exec_lua([[
- _G.input = {}
- local term = vim.api.nvim_open_term(0, {
- on_input = function(_, _, _, data)
- table.insert(_G.input, data)
- end,
- force_crlf = false,
- })
- vim.api.nvim_create_autocmd('TermRequest', {
- callback = function(args)
- if args.data == '\027]11;?' then
- table.insert(_G.input, '\027]11;rgb:0000/0000/0000\027\\')
+ describe('TermRequest', function()
+ it('emits events #26972', function()
+ local term = api.nvim_open_term(0, {})
+ local termbuf = api.nvim_get_current_buf()
+
+ -- Test that <abuf> is the terminal buffer, not the current buffer
+ command('au TermRequest * let g:termbuf = +expand("<abuf>")')
+ command('wincmd p')
+
+ -- cwd will be inserted in a file URI, which cannot contain backs
+ local cwd = t.fix_slashes(fn.getcwd())
+ local parent = cwd:match('^(.+/)')
+ local expected = '\027]7;file://host' .. parent
+ api.nvim_chan_send(term, string.format('%s\027\\', expected))
+ eq(expected, eval('v:termrequest'))
+ eq(termbuf, eval('g:termbuf'))
+ end)
+
+ it('emits events for APC', function()
+ local term = api.nvim_open_term(0, {})
+
+ -- cwd will be inserted in a file URI, which cannot contain backs
+ local cwd = t.fix_slashes(fn.getcwd())
+ local parent = cwd:match('^(.+/)')
+ local expected = '\027_Gfile://host' .. parent
+ api.nvim_chan_send(term, string.format('%s\027\\', expected))
+ eq(expected, eval('v:termrequest'))
+ end)
+
+ it('synchronization #27572', function()
+ command('autocmd! nvim.terminal TermRequest')
+ local term = exec_lua([[
+ _G.input = {}
+ local term = vim.api.nvim_open_term(0, {
+ on_input = function(_, _, _, data)
+ table.insert(_G.input, data)
+ end,
+ force_crlf = false,
+ })
+ vim.api.nvim_create_autocmd('TermRequest', {
+ callback = function(args)
+ if args.data.sequence == '\027]11;?' then
+ table.insert(_G.input, '\027]11;rgb:0000/0000/0000\027\\')
+ end
end
- end
- })
- return term
- ]])
- api.nvim_chan_send(term, '\027]11;?\007\027[5n\027]11;?\007\027[5n')
- eq({
- '\027]11;rgb:0000/0000/0000\027\\',
- '\027[0n',
- '\027]11;rgb:0000/0000/0000\027\\',
- '\027[0n',
- }, exec_lua('return _G.input'))
+ })
+ return term
+ ]])
+ api.nvim_chan_send(term, '\027]11;?\007\027[5n\027]11;?\007\027[5n')
+ eq({
+ '\027]11;rgb:0000/0000/0000\027\\',
+ '\027[0n',
+ '\027]11;rgb:0000/0000/0000\027\\',
+ '\027[0n',
+ }, exec_lua('return _G.input'))
+ end)
+
+ it('works with vim.wait() from another autocommand #32706', function()
+ command('autocmd! nvim.terminal TermRequest')
+ exec_lua([[
+ local term = vim.api.nvim_open_term(0, {})
+ vim.api.nvim_create_autocmd('TermRequest', {
+ buffer = 0,
+ callback = function(ev)
+ _G.sequence = ev.data.sequence
+ _G.v_termrequest = vim.v.termrequest
+ end,
+ })
+ vim.api.nvim_create_autocmd('TermEnter', {
+ buffer = 0,
+ callback = function()
+ vim.api.nvim_chan_send(term, '\027]11;?\027\\')
+ _G.result = vim.wait(3000, function()
+ local expected = '\027]11;?'
+ return _G.sequence == expected and _G.v_termrequest == expected
+ end)
+ end,
+ })
+ ]])
+ feed('i')
+ retry(nil, 4000, function()
+ eq(true, exec_lua('return _G.result'))
+ end)
+ end)
+
+ it('includes cursor position #31609', function()
+ command('autocmd! nvim.terminal TermRequest')
+ local screen = Screen.new(50, 10)
+ local term = exec_lua([[
+ _G.cursor = {}
+ local term = vim.api.nvim_open_term(0, {})
+ vim.api.nvim_create_autocmd('TermRequest', {
+ callback = function(args)
+ _G.cursor = args.data.cursor
+ end
+ })
+ return term
+ ]])
+ -- Enter terminal mode so that the cursor follows the output
+ feed('a')
+
+ -- Put some lines into the scrollback. This tests the conversion from terminal line to buffer
+ -- line.
+ api.nvim_chan_send(term, string.rep('>\n', 20))
+ screen:expect([[
+ > |*8
+ ^ |
+ {5:-- TERMINAL --} |
+ ]])
+
+ -- Emit an OSC escape sequence
+ api.nvim_chan_send(term, 'Hello\nworld!\027]133;D\027\\')
+ screen:expect([[
+ > |*7
+ Hello |
+ world!^ |
+ {5:-- TERMINAL --} |
+ ]])
+ eq({ 22, 6 }, exec_lua('return _G.cursor'))
+ end)
+
+ it('does not cause hang in vim.wait() #32753', function()
+ local screen = Screen.new(50, 10)
+
+ exec_lua(function()
+ local term = vim.api.nvim_open_term(0, {})
+
+ -- Write OSC sequence with pending scrollback. TermRequest will
+ -- reschedule itself onto an event queue until the pending scrollback is
+ -- processed (i.e. the terminal is refreshed).
+ vim.api.nvim_chan_send(term, string.format('%s\027]133;;\007', string.rep('a\n', 100)))
+
+ -- vim.wait() drains the event queue. The terminal won't be refreshed
+ -- until the event queue is empty. This test ensures that TermRequest
+ -- does not continuously reschedule itself onto the same event queue,
+ -- causing an infinite loop.
+ vim.wait(100)
+ end)
+
+ screen:expect([[
+ ^a |
+ a |*8
+ |
+ ]])
+ end)
end)
it('no heap-buffer-overflow when using jobstart("echo",{term=true}) #3161', function()
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
index 8ebad14f15..93e55bb103 100644
--- a/test/functional/terminal/cursor_spec.lua
+++ b/test/functional/terminal/cursor_spec.lua
@@ -149,6 +149,73 @@ describe(':terminal cursor', function()
|*5
{3:-- TERMINAL --} |
]])
+
+ -- Cursor is hidden; now request to show it while in a TermLeave autocmd.
+ -- Process events (via :sleep) to handle the escape sequence now.
+ command([[autocmd TermLeave * ++once call chansend(&channel, "\e[?25h") | sleep 1m]])
+ feed([[<C-\><C-N>]]) -- Exit terminal mode; cursor should not remain hidden
+ screen:expect([[
+ tty ready |
+ ^ |
+ |*5
+ ]])
+
+ command('bwipeout! | let chan = nvim_open_term(0, {})')
+ feed('i')
+ -- Hide the cursor, switch to a non-terminal buffer, then show the cursor; it shouldn't remain
+ -- hidden after we're kicked out of terminal mode in the new buffer.
+ -- Must ensure these actions happen within the same terminal_execute call. The stream is
+ -- internal, so polling the event loop isn't necessary (terminal_receive is directly called).
+ command([[call chansend(chan, "\e[?25l") | new floob | call chansend(chan, "\e[?25h")]])
+ screen:expect([[
+ ^ |
+ {4:~ }|
+ {5:floob }|
+ |*2
+ {18:[Scratch] }|
+ |
+ ]])
+
+ feed('<C-W>pi')
+ screen:expect([[
+ |
+ {4:~ }|
+ {1:floob }|
+ ^ |
+ |
+ {17:[Scratch] }|
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
+ it('becomes visible on TermLeave if hidden immediately by events #32456', function()
+ skip(is_os('win'), '#31587')
+ -- Reproducing the issue is quite fragile; it's easiest done in a lone test case like this
+ -- with no prior commands.
+ feed([[<C-\><C-N>]])
+ screen:expect([[
+ tty ready |
+ ^ |
+ |*5
+ ]])
+
+ -- Hide the cursor such that the escape sequence is processed as a side effect of showmode in
+ -- terminal_enter handling events (skip_showmode -> char_avail -> vpeekc -> os_breakcheck).
+ -- This requires a particular set of actions; :startinsert repros better than feed('i') here.
+ hide_cursor()
+ command('mode | startinsert')
+ screen:expect([[
+ tty ready |
+ |*5
+ {3:-- TERMINAL --} |
+ ]])
+
+ feed([[<C-\><C-N>]])
+ screen:expect([[
+ tty ready |
+ ^ |
+ |*5
+ ]])
end)
end)
@@ -358,55 +425,50 @@ describe(':terminal cursor', function()
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end)
- it('restores visibility on TermLeave #32456', function()
- skip(is_os('win'), '#31587')
- feed([[<C-\><C-N>]]) -- Exit terminal mode
- screen:expect([[
- tty ready |
- ^ |
- |*5
- ]])
-
- tt.hide_cursor()
- -- :startinsert repros the issue more reliably than feed('i')
- command('mode | startinsert')
- screen:expect([[
- tty ready |
- |*5
- {3:-- TERMINAL --} |
- ]])
-
- feed([[<C-\><C-N>]]) -- Exit terminal mode
- screen:expect([[
- tty ready |
- ^ |
- |*5
+ it('uses the correct attributes', function()
+ feed([[<C-\><C-N>]])
+ command([[
+ bwipeout!
+ let chan1 = nvim_open_term(0, {})
+ vnew
+ let chan2 = nvim_open_term(0, {})
]])
-
feed('i')
screen:expect([[
- tty ready |
- |*5
+ ^ │ |
+ │ |*4
+ {17:[Scratch] }{18:[Scratch] }|
{3:-- TERMINAL --} |
]])
+ eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
- -- Cursor currently hidden; request to show it while in a TermLeave autocmd.
- -- Process events (via :sleep) to handle the escape sequence immediately.
- command([[autocmd TermLeave * ++once call chansend(b:terminal_job_id, "\e[?25h") | sleep 1m]])
- feed([[<C-\><C-N>]]) -- Exit terminal mode
- screen:expect([[
- tty ready |
- ^ |
- |*5
- ]])
+ -- Modify cursor in the non-current terminal; should not affect this cursor.
+ command([[call chansend(chan1, "\e[4 q")]])
+ screen:expect_unchanged()
+ eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
- feed('i')
+ -- Modify cursor in the current terminal.
+ command([[call chansend(chan2, "\e[6 q")]])
+ screen:expect_unchanged()
+ eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
+
+ -- Check the cursor in the other terminal reflects our changes from before.
+ command('wincmd p')
screen:expect([[
- tty ready |
- ^ |
- |*4
+ │^ |
+ │ |*4
+ {18:[Scratch] }{17:[Scratch] }|
{3:-- TERMINAL --} |
]])
+ eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
end)
end)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index e2adcb66df..5e452ce9c9 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -215,7 +215,7 @@ describe('TUI', function()
_G.termresponse = nil
vim.api.nvim_create_autocmd('TermResponse', {
once = true,
- callback = function(ev) _G.termresponse = ev.data end,
+ callback = function(ev) _G.termresponse = ev.data.sequence end,
})
]])
feed_data('\027P0$r\027\\')
@@ -2199,7 +2199,7 @@ describe('TUI', function()
vim.api.nvim_create_autocmd('TermRequest', {
buffer = buf,
callback = function(args)
- local req = args.data
+ local req = args.data.sequence
if not req then
return
end
@@ -2222,6 +2222,32 @@ describe('TUI', function()
eq({ { id = 0xE1EA0000, url = 'https://example.com' } }, exec_lua([[return _G.urls]]))
end)
end)
+
+ it('TermResponse works with vim.wait() from another autocommand #32706', function()
+ child_exec_lua([[
+ _G.termresponse = nil
+ vim.api.nvim_create_autocmd('TermResponse', {
+ callback = function(ev)
+ _G.sequence = ev.data.sequence
+ _G.v_termresponse = vim.v.termresponse
+ end,
+ })
+ vim.api.nvim_create_autocmd('InsertEnter', {
+ buffer = 0,
+ callback = function()
+ _G.result = vim.wait(3000, function()
+ local expected = '\027P1+r5463'
+ return _G.sequence == expected and _G.v_termresponse == expected
+ end)
+ end,
+ })
+ ]])
+ feed_data('i')
+ feed_data('\027P1+r5463\027\\')
+ retry(nil, 4000, function()
+ eq(true, child_exec_lua('return _G.result'))
+ end)
+ end)
end)
describe('TUI', function()
@@ -3171,12 +3197,12 @@ describe('TUI', function()
exec_lua([[
vim.api.nvim_create_autocmd('TermRequest', {
callback = function(args)
- local req = args.data
- local payload = req:match('^\027P%+q([%x;]+)$')
- if payload then
+ local req = args.data.sequence
+ local sequence = req:match('^\027P%+q([%x;]+)$')
+ if sequence then
local t = {}
- for cap in vim.gsplit(payload, ';') do
- local resp = string.format('\027P1+r%s\027\\', payload)
+ for cap in vim.gsplit(sequence, ';') do
+ local resp = string.format('\027P1+r%s\027\\', sequence)
vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
t[vim.text.hexdecode(cap)] = true
end
@@ -3222,7 +3248,7 @@ describe('TUI', function()
exec_lua([[
vim.api.nvim_create_autocmd('TermRequest', {
callback = function(args)
- local req = args.data
+ local req = args.data.sequence
vim.g.termrequest = req
local xtgettcap = req:match('^\027P%+q([%x;]+)$')
if xtgettcap then
@@ -3274,10 +3300,10 @@ describe('TUI', function()
exec_lua([[
vim.api.nvim_create_autocmd('TermRequest', {
callback = function(args)
- local req = args.data
- local payload = req:match('^\027P%+q([%x;]+)$')
- if payload and vim.text.hexdecode(payload) == 'Ms' then
- local resp = string.format('\027P1+r%s=%s\027\\', payload, vim.text.hexencode('\027]52;;\027\\'))
+ local req = args.data.sequence
+ local sequence = req:match('^\027P%+q([%x;]+)$')
+ if sequence and vim.text.hexdecode(sequence) == 'Ms' then
+ local resp = string.format('\027P1+r%s=%s\027\\', sequence, vim.text.hexencode('\027]52;;\027\\'))
vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
return true
end
@@ -3353,7 +3379,7 @@ describe('TUI bg color', function()
exec_lua([[
vim.api.nvim_create_autocmd('TermRequest', {
callback = function(args)
- local req = args.data
+ local req = args.data.sequence
if req == '\027]11;?' then
vim.g.oscrequest = true
return true
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index b7cf8504ff..58fd50e724 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -577,7 +577,7 @@ describe('Buffer highlighting', function()
end)
describe('virtual text decorations', function()
- local id1, id2
+ local id1, id2 ---@type integer, integer
before_each(function()
insert([[
1 + 2
@@ -709,7 +709,7 @@ describe('Buffer highlighting', function()
0,
0,
{
- ns_id = 1,
+ ns_id = id1,
priority = 0,
virt_text = s1,
-- other details
@@ -729,7 +729,7 @@ describe('Buffer highlighting', function()
lastline,
0,
{
- ns_id = 1,
+ ns_id = id1,
priority = 0,
virt_text = s2,
-- other details
@@ -898,11 +898,15 @@ describe('Buffer highlighting', function()
end)
it('and virtual text use the same namespace counter', function()
- eq(1, add_highlight(0, 0, 'String', 0, 0, -1))
- eq(2, set_virtual_text(0, 0, 0, { { '= text', 'Comment' } }, {}))
- eq(3, api.nvim_create_namespace('my-ns'))
- eq(4, add_highlight(0, 0, 'String', 0, 0, -1))
- eq(5, set_virtual_text(0, 0, 0, { { '= text', 'Comment' } }, {}))
- eq(6, api.nvim_create_namespace('other-ns'))
+ local base = vim.iter(api.nvim_get_namespaces()):fold(0, function(acc, _, v)
+ return math.max(acc, v)
+ end)
+
+ eq(base + 1, add_highlight(0, 0, 'String', 0, 0, -1))
+ eq(base + 2, set_virtual_text(0, 0, 0, { { '= text', 'Comment' } }, {}))
+ eq(base + 3, api.nvim_create_namespace('my-ns'))
+ eq(base + 4, add_highlight(0, 0, 'String', 0, 0, -1))
+ eq(base + 5, set_virtual_text(0, 0, 0, { { '= text', 'Comment' } }, {}))
+ eq(base + 6, api.nvim_create_namespace('other-ns'))
end)
end)
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 0eb8391385..c0c0bf4fc1 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -16,7 +16,7 @@ local assert_alive = n.assert_alive
local pcall_err = t.pcall_err
describe('decorations providers', function()
- local screen
+ local screen ---@type test.functional.ui.screen
before_each(function()
clear()
screen = Screen.new(40, 8)
@@ -52,6 +52,7 @@ describe('decorations providers', function()
posp = getmark(mark, false);
restore_buffer(&save_buf); ]]
+ --- @return integer
local function setup_provider(code)
return exec_lua ([[
local api = vim.api
@@ -848,7 +849,8 @@ for _,item in ipairs(items) do
end]]
describe('extmark decorations', function()
- local screen, ns
+ local screen ---@type test.functional.ui.screen
+ local ns ---@type integer
before_each( function()
clear()
screen = Screen.new(50, 15)
@@ -2232,7 +2234,7 @@ describe('extmark decorations', function()
eq({ { 1, 0, 8, { end_col = 13, end_right_gravity = false, end_row = 0,
hl_eol = false, hl_group = "NonText", undo_restore = false,
- ns_id = 1, priority = 4096, right_gravity = true } } },
+ ns_id = ns, priority = 4096, right_gravity = true } } },
api.nvim_buf_get_extmarks(0, ns, {0,0}, {0, -1}, {details=true}))
end)
@@ -2894,11 +2896,67 @@ describe('extmark decorations', function()
{1:~ }|*4
|
]])
+ -- Also with above virtual line #32744
+ command('set nornu')
+ api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = { { { "virt_below 4" } } } })
+ feed('6G')
+ screen:expect([[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 4 } hl_id = hl_id_cell |
+ {2: }virt_below 4 |
+ {2: 6 } ^for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*3
+ |
+ ]])
+ feed('j')
+ screen:expect([[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 4 } hl_id = hl_id_cell |
+ {2: }virt_below 4 |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } ^ local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*3
+ |
+ ]])
+ -- Even when virtual line is added as line is concealed #32762
+ feed('5G')
+ api.nvim_buf_clear_namespace(0, ns, 3, 4)
+ feed('j')
+ api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = { { { "virt_below 4" } } } })
+ screen:expect([[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 4 } hl_id = hl_id_cell |
+ {2: }virt_below 4 |
+ {2: 6 }^ for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*3
+ |
+ ]])
end)
end)
describe('decorations: inline virtual text', function()
- local screen, ns
+ local screen ---@type test.functional.ui.screen
+ local ns ---@type integer
before_each( function()
clear()
screen = Screen.new(50, 3)
@@ -4640,7 +4698,9 @@ describe('decorations: inline virtual text', function()
end)
describe('decorations: virtual lines', function()
- local screen, ns
+ local screen ---@type test.functional.ui.screen
+ local ns ---@type integer
+
before_each(function()
clear()
screen = Screen.new(50, 12)
@@ -5697,7 +5757,9 @@ if (h->n_buckets < new_n_buckets) { // expand
end)
describe('decorations: signs', function()
- local screen, ns
+ local screen ---@type test.functional.ui.screen
+ local ns ---@type integer
+
before_each(function()
clear()
screen = Screen.new(50, 10)
@@ -6307,7 +6369,7 @@ l5
end)
describe('decorations: virt_text', function()
- local screen
+ local screen ---@type test.functional.ui.screen
before_each(function()
clear()
@@ -6381,7 +6443,10 @@ describe('decorations: virt_text', function()
end)
describe('decorations: window scoped', function()
- local screen, ns, win_other
+ local screen ---@type test.functional.ui.screen
+ local ns ---@type integer
+ local win_other ---@type integer
+
local url = 'https://example.com'
before_each(function()
clear()
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index f6c9236037..6f3b38ce83 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -1851,15 +1851,16 @@ describe('builtin popupmenu', function()
end)
describe('floating window preview popup', function()
- it('pum popup preview', function()
+ before_each(function()
--row must > 10
screen:try_resize(40, 11)
exec([[
+ let g:list = [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "looooooooooooooong"}]
funct Omni_test(findstart, base)
if a:findstart
return col(".") - 1
endif
- return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "looooooooooooooong"}]
+ return g:list
endfunc
set omnifunc=Omni_test
set completeopt=menu,popup
@@ -1886,28 +1887,42 @@ describe('builtin popupmenu', function()
autocmd! Group
autocmd CompleteChanged * call TsHl()
endfunc
+ funct Append_multipe()
+ call extend(g:list, [#{word: "for .. ipairs", info: "```lua\nfor index, value in ipairs(t) do\n\t\nend\n```"}])
+ endfunc
]])
+ end)
+
+ it('pum popup preview', function()
feed('Gi<C-x><C-o>')
--floating preview in right
if multigrid then
- screen:expect {
+ screen:expect({
grid = [[
- ## grid 1
- [2:----------------------------------------]|*10
- [3:----------------------------------------]|
- ## grid 2
- one^ |
- {1:~ }|*9
- ## grid 3
- {2:-- }{5:match 1 of 3} |
- ## grid 4
- {n:1info}|
- {n: }|
- ## grid 5
- {s:one }|
- {n:two }|
- {n:looooooooooooooong }|
- ]],
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ one^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 4
+ {n:1info}|
+ ## grid 5
+ {s:one }|
+ {n:two }|
+ {n:looooooooooooooong }|
+ ]],
+ win_pos = {
+ [2] = {
+ height = 10,
+ startcol = 0,
+ startrow = 0,
+ width = 40,
+ win = 1000,
+ },
+ },
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
[4] = { 1001, 'NW', 1, 1, 19, false, 50 },
@@ -1925,25 +1940,39 @@ describe('builtin popupmenu', function()
[4] = {
win = 1001,
topline = 0,
- botline = 2,
+ botline = 1,
curline = 0,
curcol = 0,
linecount = 1,
sum_scroll_delta = 0,
},
},
- }
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ [4] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1001,
+ },
+ },
+ })
else
- screen:expect {
- grid = [[
+ screen:expect([[
one^ |
{s:one }{n:1info}{1: }|
- {n:two }{1: }|
+ {n:two }{1: }|
{n:looooooooooooooong }{1: }|
{1:~ }|*6
{2:-- }{5:match 1 of 3} |
- ]],
- }
+ ]])
end
-- delete one character make the pum width smaller than before
@@ -1962,10 +1991,18 @@ describe('builtin popupmenu', function()
{2:-- }{5:match 1 of 3} |
## grid 4
{n:1info}|
- {n: }|
## grid 5
{s:one }|
]],
+ win_pos = {
+ [2] = {
+ height = 10,
+ startcol = 0,
+ startrow = 0,
+ width = 40,
+ win = 1000,
+ },
+ },
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
[4] = { 1001, 'NW', 1, 1, 15, false, 50 },
@@ -1983,7 +2020,7 @@ describe('builtin popupmenu', function()
[4] = {
win = 1001,
topline = 0,
- botline = 2,
+ botline = 1,
curline = 0,
curcol = 0,
linecount = 1,
@@ -2008,15 +2045,12 @@ describe('builtin popupmenu', function()
},
})
else
- screen:expect({
- grid = [[
- on^ |
- {s:one }{n:1info}{1: }|
- {1:~ }{n: }{1: }|
- {1:~ }|*7
- {2:-- }{5:match 1 of 3} |
- ]],
- })
+ screen:expect([[
+ on^ |
+ {s:one }{n:1info}{1: }|
+ {1:~ }|*8
+ {2:-- }{5:match 1 of 3} |
+ ]])
end
-- when back to original the preview float should be closed.
@@ -2034,7 +2068,6 @@ describe('builtin popupmenu', function()
{2:-- }{8:Back at original} |
## grid 4 (hidden)
{n:1info}|
- {n: }|
## grid 5
{n:one }|
]],
@@ -2063,7 +2096,7 @@ describe('builtin popupmenu', function()
[4] = {
win = 1001,
topline = 0,
- botline = 2,
+ botline = 1,
curline = 0,
curcol = 0,
linecount = 1,
@@ -2097,9 +2130,11 @@ describe('builtin popupmenu', function()
]],
})
end
+ feed('<C-E><ESC>')
+ end)
- -- test nvim__complete_set_info
- feed('<ESC>S<C-X><C-O><C-N><C-N>')
+ it('nvim__set_complete', function()
+ feed('S<C-X><C-O><C-N><C-N>')
if multigrid then
screen:expect({
grid = [[
@@ -2111,13 +2146,12 @@ describe('builtin popupmenu', function()
{1:~ }|*9
## grid 3
{2:-- }{5:match 3 of 3} |
+ ## grid 4
+ {n:3info}|
## grid 5
{n:one }|
{n:two }|
{s:looooooooooooooong }|
- ## grid 6
- {n:3info}|
- {n: }|
]],
win_pos = {
[2] = {
@@ -2130,7 +2164,7 @@ describe('builtin popupmenu', function()
},
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
- [6] = { 1002, 'NW', 1, 1, 19, false, 50 },
+ [4] = { 1001, 'NW', 1, 1, 19, false, 50 },
},
win_viewport = {
[2] = {
@@ -2142,10 +2176,10 @@ describe('builtin popupmenu', function()
linecount = 1,
sum_scroll_delta = 0,
},
- [6] = {
- win = 1002,
+ [4] = {
+ win = 1001,
topline = 0,
- botline = 2,
+ botline = 1,
curline = 0,
curcol = 0,
linecount = 1,
@@ -2160,30 +2194,29 @@ describe('builtin popupmenu', function()
top = 0,
win = 1000,
},
- [6] = {
+ [4] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
- win = 1002,
+ win = 1001,
},
},
})
else
- screen:expect {
- grid = [[
+ screen:expect([[
looooooooooooooong^ |
{n:one 3info}{1: }|
- {n:two }{1: }|
+ {n:two }{1: }|
{s:looooooooooooooong }{1: }|
{1:~ }|*6
{2:-- }{5:match 3 of 3} |
- ]],
- }
+ ]])
end
+ feed('<C-E><ESC>')
+ end)
- -- preview in left
- feed('<ESC>cc')
+ it('popup preview place in left', function()
insert(('test'):rep(5))
feed('i<C-x><C-o>')
if multigrid then
@@ -2193,17 +2226,16 @@ describe('builtin popupmenu', function()
[2:----------------------------------------]|*10
[3:----------------------------------------]|
## grid 2
- itesttesttesttesttesone^t |
+ testtesttesttesttesone^t |
{1:~ }|*9
## grid 3
{2:-- }{5:match 1 of 3} |
+ ## grid 4
+ {n:1info}|
## grid 5
{s: one }|
{n: two }|
{n: looooooooooooooong }|
- ## grid 7
- {n:1info}|
- {n: }|
]],
win_pos = {
[2] = {
@@ -2215,8 +2247,8 @@ describe('builtin popupmenu', function()
},
},
float_pos = {
- [7] = { 1003, 'NW', 1, 1, 14, false, 50 },
- [5] = { -1, 'NW', 2, 1, 19, false, 100 },
+ [5] = { -1, 'NW', 2, 1, 18, false, 100 },
+ [4] = { 1001, 'NW', 1, 1, 13, false, 50 },
},
win_viewport = {
[2] = {
@@ -2224,14 +2256,14 @@ describe('builtin popupmenu', function()
topline = 0,
botline = 2,
curline = 0,
- curcol = 23,
+ curcol = 22,
linecount = 1,
sum_scroll_delta = 0,
},
- [7] = {
- win = 1003,
+ [4] = {
+ win = 1001,
topline = 0,
- botline = 2,
+ botline = 1,
curline = 0,
curcol = 0,
linecount = 1,
@@ -2246,30 +2278,29 @@ describe('builtin popupmenu', function()
top = 0,
win = 1000,
},
- [7] = {
+ [4] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
- win = 1003,
+ win = 1001,
},
},
})
else
- screen:expect {
- grid = [[
- itesttesttesttesttesone^t |
- {1:~ }{n:1info}{s: one }{1: }|
- {1:~ }{n: two }{1: }|
- {1:~ }{n: looooooooooooooong }{1: }|
+ screen:expect([[
+ testtesttesttesttesone^t |
+ {1:~ }{n:1info}{s: one }{1: }|
+ {1:~ }{n: two }{1: }|
+ {1:~ }{n: looooooooooooooong }{1: }|
{1:~ }|*6
{2:-- }{5:match 1 of 3} |
- ]],
- }
+ ]])
end
feed('<C-E><Esc>')
+ end)
- -- works when scroll with treesitter highlight
+ it('works when scroll with treesitter highlight', function()
command('call TestTs()')
feed('S<C-x><C-o>')
if multigrid then
@@ -2283,17 +2314,16 @@ describe('builtin popupmenu', function()
{1:~ }|*9
## grid 3
{2:-- }{5:match 1 of 3} |
- ## grid 5
- {s:one }|
- {n:two }|
- {n:looooooooooooooong }|
- ## grid 8
+ ## grid 4
{n:```lua }|
{n:function test()}|
{n: print('foo') }|
{n:end }|
{n:``` }|
- {n: }|
+ ## grid 5
+ {s:one }|
+ {n:two }|
+ {n:looooooooooooooong }|
]],
win_pos = {
[2] = {
@@ -2306,7 +2336,7 @@ describe('builtin popupmenu', function()
},
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
- [8] = { 1004, 'NW', 1, 1, 19, false, 50 },
+ [4] = { 1001, 'NW', 1, 1, 19, false, 50 },
},
win_viewport = {
[2] = {
@@ -2318,10 +2348,10 @@ describe('builtin popupmenu', function()
linecount = 1,
sum_scroll_delta = 0,
},
- [8] = {
- win = 1004,
+ [4] = {
+ win = 1001,
topline = 0,
- botline = 6,
+ botline = 5,
curline = 0,
curcol = 0,
linecount = 5,
@@ -2336,30 +2366,127 @@ describe('builtin popupmenu', function()
top = 0,
win = 1000,
},
- [8] = {
+ [4] = {
bottom = 0,
left = 0,
right = 0,
top = 0,
- win = 1004,
+ win = 1001,
},
},
})
else
+ screen:expect([[
+ one^ |
+ {s:one }{n:```lua }{1: }|
+ {n:two function test()}{1: }|
+ {n:looooooooooooooong print('foo') }{1: }|
+ {1:~ }{n:end }{1: }|
+ {1:~ }{n:``` }{1: }|
+ {1:~ }|*4
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ end
+ feed('<C-E><ESC>')
+ end)
+
+ it('#avoid modified original info text', function()
+ screen:try_resize(40, 11)
+ command('call Append_multipe()')
+ feed('S<C-x><C-o><C-P><C-P>')
+ if multigrid then
screen:expect({
grid = [[
- one^ |
- {s:one }{n:```lua }{1: }|
- {n:two function test()}{1: }|
- {n:looooooooooooooong print('foo') }{1: }|
- {1:~ }{n:end }{1: }|
- {1:~ }{n:``` }{1: }|
- {1:~ }{n: }{1: }|
- {1:~ }|*3
- {2:-- }{5:match 1 of 3} |
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ for .. ipairs^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{5:match 1 of 4} |
+ ## grid 4
+ {n:one }|
+ {n:two }|
+ {n:looooooooooooooong }|
+ {s:for .. ipairs }|
+ ## grid 5
+ {n:```lua }|
+ {n:for index, value in }|
+ {n:ipairs(t) do }|
+ {n: }|
+ {n:end }|
+ {n:``` }|
]],
+ win_pos = {
+ [2] = {
+ height = 10,
+ startcol = 0,
+ startrow = 0,
+ width = 40,
+ win = 1000,
+ },
+ },
+ float_pos = {
+ [5] = { 1001, 'NW', 1, 1, 19, false, 50 },
+ [4] = { -1, 'NW', 2, 1, 0, false, 100 },
+ },
+ win_viewport = {
+ [2] = {
+ win = 1000,
+ topline = 0,
+ botline = 2,
+ curline = 0,
+ curcol = 13,
+ linecount = 1,
+ sum_scroll_delta = 0,
+ },
+ [5] = {
+ win = 1001,
+ topline = 0,
+ botline = 5,
+ curline = 0,
+ curcol = 0,
+ linecount = 5,
+ sum_scroll_delta = 0,
+ },
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ [5] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1001,
+ },
+ },
})
+ else
+ screen:expect([[
+ for .. ipairs^ |
+ {n:one ```lua }{1: }|
+ {n:two for index, value in }{1: }|
+ {n:looooooooooooooong ipairs(t) do }{1: }|
+ {s:for .. ipairs }{n: }{1: }|
+ {1:~ }{n:end }{1: }|
+ {1:~ }{n:``` }{1: }|
+ {1:~ }|*3
+ {2:-- }{5:match 1 of 4} |
+ ]])
end
+
+ feed('<C-N><C-N><C-N><C-N><C-N>')
+ if not multigrid then
+ screen:expect_unchanged()
+ end
+ feed('<C-E><ESC>')
end)
end)
@@ -5145,6 +5272,29 @@ describe('builtin popupmenu', function()
]])
end)
+ -- oldtest: Test_wildmenu_pum_hl_nonfirst()
+ it('highlight matched text in the middle in cmdline pum', function()
+ exec([[
+ set wildoptions=pum wildchar=<tab> wildmode=noselect,full
+ hi PmenuMatchSel guifg=Blue guibg=Grey
+ hi PmenuMatch guifg=Blue guibg=Plum1
+ func T(a, c, p)
+ return ["oneA", "o neBneB", "aoneC"]
+ endfunc
+ command -nargs=1 -complete=customlist,T MyCmd
+ ]])
+
+ feed(':MyCmd ne<tab>')
+ screen:expect([[
+ |
+ {1:~ }|*15
+ {1:~ }{n: o}{mn:ne}{n:A }{1: }|
+ {1:~ }{n: o }{mn:ne}{n:BneB }{1: }|
+ {1:~ }{n: ao}{mn:ne}{n:C }{1: }|
+ :MyCmd ne^ |
+ ]])
+ end)
+
it(
'cascading highlights for matched text (PmenuMatch, PmenuMatchSel) in cmdline pum',
function()
@@ -7287,6 +7437,7 @@ describe('builtin popupmenu', function()
return [#{word: "func ()\n\t\nend", abbr: "function ()",}, #{word: "foobar"}, #{word: "你好\n\t\n我好"}]
endfunc
set omnifunc=Omni_test
+ inoremap <F5> <Cmd>call complete(col('.'), [ "my\n\tmulti\nline", "my\n\t\tmulti\nline" ])<CR>
]])
feed('S<C-X><C-O>')
@@ -7429,6 +7580,21 @@ describe('builtin popupmenu', function()
{1:~ }|*14
{2:-- INSERT --} |
]])
+
+ feed('<Esc>ggVGd')
+ command('filetype indent on')
+ command('setlocal noautoindent shiftwidth& tabstop&')
+ command('setlocal ft=lua')
+ feed('S<F5>')
+ screen:expect([[
+ {8:my} |
+ {8: multi} |
+ {8:line}^ |
+ {s:my^@ multi^@line }{1: }|
+ {n:my^@ multi^@line }{1: }|
+ {1:~ }|*14
+ {2:-- INSERT --} |
+ ]])
end)
end
end
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
index 64876ce318..d1d27cf578 100644
--- a/test/old/testdir/gen_opt_test.vim
+++ b/test/old/testdir/gen_opt_test.vim
@@ -352,6 +352,7 @@ let test_values = {
\ 'bs'],
\ ['xxx']],
\ 'wildmode': [['', 'full', 'longest', 'list', 'lastused', 'list:full',
+ \ 'noselect', 'noselect,full', 'noselect:lastused,full',
\ 'full,longest', 'full,full,full,full'],
\ ['xxx', 'a4', 'full,full,full,full,full']],
\ 'wildoptions': [['', 'tagfile', 'pum', 'fuzzy'], ['xxx']],
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index d4ad63d43e..639a4c1d32 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -2168,22 +2168,58 @@ func Wildmode_tests()
call assert_equal('AAA AAAA AAAAA', g:Sline)
call assert_equal('"b A', @:)
+ " When 'wildmenu' is not set, 'noselect' completes first item
+ set wildmode=noselect
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+
+ " When 'noselect' is present, do not complete first <tab>.
+ set wildmenu
+ set wildmode=noselect
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd o', @:)
+ call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd o', @:)
+ call feedkeys(":MyCmd o\t\t\<C-Y>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd o', @:)
+
+ " When 'full' is present, complete after first <tab>.
+ set wildmode=noselect,full
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd o', @:)
+ call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+ call feedkeys(":MyCmd o\t\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneB', @:)
+ call feedkeys(":MyCmd o\t\t\t\<C-Y>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneB', @:)
+
+ " 'noselect' has no effect when 'longest' is present.
+ set wildmode=noselect:longest
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd one', @:)
+
+ " Complete 'noselect' value in 'wildmode' option
+ set wildmode&
+ call feedkeys(":set wildmode=n\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set wildmode=noselect', @:)
+ call feedkeys(":set wildmode=\t\t\t\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set wildmode=noselect', @:)
+
" when using longest completion match, matches shorter than the argument
" should be ignored (happens with :help)
set wildmode=longest,full
- set wildmenu
call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt')
call assert_equal('"help a', @:)
" non existing file
call feedkeys(":e a1b2y3z4\t\<C-B>\"\<CR>", 'xt')
call assert_equal('"e a1b2y3z4', @:)
- set wildmenu&
" Test for longest file name completion with 'fileignorecase'
" On MS-Windows, file names are case insensitive.
if has('unix')
- call writefile([], 'XTESTfoo')
- call writefile([], 'Xtestbar')
+ call writefile([], 'XTESTfoo', 'D')
+ call writefile([], 'Xtestbar', 'D')
set nofileignorecase
call feedkeys(":e XT\<Tab>\<C-B>\"\<CR>", 'xt')
call assert_equal('"e XTESTfoo', @:)
@@ -2195,10 +2231,23 @@ func Wildmode_tests()
call feedkeys(":e Xt\<Tab>\<C-B>\"\<CR>", 'xt')
call assert_equal('"e Xtest', @:)
set fileignorecase&
- call delete('XTESTfoo')
- call delete('Xtestbar')
endif
+ " If 'noselect' is present, single item menu should not insert item
+ func! T(a, c, p)
+ return "oneA"
+ endfunc
+ command! -nargs=1 -complete=custom,T MyCmd
+ set wildmode=noselect,full
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd o', @:)
+ call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+ " 'nowildmenu' should make 'noselect' ineffective
+ set nowildmenu
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+
%argdelete
delcommand MyCmd
delfunc T
@@ -2976,6 +3025,28 @@ func Test_wildmenu_pum_rightleft()
call StopVimInTerminal(buf)
endfunc
+" Test highlighting when pattern matches non-first character of item
+func Test_wildmenu_pum_hl_nonfirst()
+ CheckScreendump
+ let lines =<< trim END
+ set wildoptions=pum wildchar=<tab> wildmode=noselect,full
+ hi PmenuMatchSel ctermfg=6 ctermbg=7
+ hi PmenuMatch ctermfg=4 ctermbg=225
+ func T(a, c, p)
+ return ["oneA", "o neBneB", "aoneC"]
+ endfunc
+ command -nargs=1 -complete=customlist,T MyCmd
+ END
+
+ call writefile(lines, 'Xwildmenu_pum_hl_nonf', 'D')
+ let buf = RunVimInTerminal('-S Xwildmenu_pum_hl_nonf', #{rows: 10, cols: 50})
+
+ call term_sendkeys(buf, ":MyCmd ne\<tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_nonf', {})
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
" Test highlighting matched text in cmdline completion popup menu.
func Test_wildmenu_pum_hl_match()
CheckScreendump
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 37337d9b54..296062f92c 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -88,6 +88,7 @@ func s:GetFilenameChecks() abort
\ 'abap': ['file.abap'],
\ 'abc': ['file.abc'],
\ 'abel': ['file.abl'],
+ \ 'abnf': ['file.abnf'],
\ 'acedb': ['file.wrm'],
\ 'ada': ['file.adb', 'file.ads', 'file.ada', 'file.gpr'],
\ 'ahdl': ['file.tdf'],
@@ -800,6 +801,7 @@ func s:GetFilenameChecks() abort
\ 'teal': ['file.tl'],
\ 'templ': ['file.templ'],
\ 'template': ['file.tmpl'],
+ \ 'tera': ['file.tera', 'file.toml.tera', 'file.html.tera', 'file.css.tera'],
\ 'teraterm': ['file.ttl'],
\ 'terminfo': ['file.ti'],
\ 'terraform-vars': ['file.tfvars'],
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
index d339bbe4e1..c2062a355f 100644
--- a/test/old/testdir/test_ins_complete.vim
+++ b/test/old/testdir/test_ins_complete.vim
@@ -1208,8 +1208,8 @@ func Test_complete_cmdline()
call assert_equal('abcxyz(', getline(3))
com! -buffer TestCommand1 echo 'TestCommand1'
com! -buffer TestCommand2 echo 'TestCommand2'
- write TestCommand1Test
- write TestCommand2Test
+ write! TestCommand1Test
+ write! TestCommand2Test
" Test repeating <CTRL-X> <CTRL-V> and switching to another CTRL-X mode
exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<C-X>\<C-F>\<Esc>"
call assert_equal('TestCommand2Test', getline(4))
diff --git a/test/old/testdir/test_plugin_matchparen.vim b/test/old/testdir/test_plugin_matchparen.vim
index 7d80e43046..0739906a42 100644
--- a/test/old/testdir/test_plugin_matchparen.vim
+++ b/test/old/testdir/test_plugin_matchparen.vim
@@ -139,4 +139,36 @@ func Test_matchparen_mbyte()
call StopVimInTerminal(buf)
endfunc
+" Test for ignoring certain parenthesis
+func Test_matchparen_ignore_sh_case()
+ CheckScreendump
+
+ let lines =<< trim END
+ source $VIMRUNTIME/plugin/matchparen.vim
+ set ft=sh
+ call setline(1, [
+ \ '#!/bin/sh',
+ \ 'SUSUWU_PRINT() (',
+ \ ' case "${LEVEL}" in',
+ \ ' "$SUSUWU_SH_NOTICE")',
+ \ ' ${SUSUWU_S} && return 1',
+ \ ' ;;',
+ \ ' "$SUSUWU_SH_DEBUG")',
+ \ ' (! ${SUSUWU_VERBOSE}) && return 1',
+ \ ' ;;',
+ \ ' esac',
+ \ ' # snip',
+ \ ')'
+ \ ])
+ call cursor(4, 26)
+ END
+
+ let filename = 'Xmatchparen_sh'
+ call writefile(lines, filename, 'D')
+
+ let buf = RunVimInTerminal('-S '.filename, #{rows: 10})
+ call VerifyScreenDump(buf, 'Test_matchparen_sh_case_1', {})
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim
index 0b88fd1dba..988d60916c 100644
--- a/test/old/testdir/test_popup.vim
+++ b/test/old/testdir/test_popup.vim
@@ -1896,6 +1896,7 @@ func Test_pum_complete_with_special_characters()
return [#{word: "func ()\n\t\nend", abbr: "function ()",}, #{word: "foobar"}, #{word: "你好\n\t\n我好"}]
endfunc
set omnifunc=Omni_test
+ inoremap <F5> <Cmd>call complete(col('.'), [ "my\n\tmulti\nline", "my\n\t\tmulti\nline" ])<CR>
END
call writefile(lines, 'Xpreviewscript', 'D')
@@ -1954,6 +1955,14 @@ func Test_pum_complete_with_special_characters()
call TermWait(buf, 50)
call VerifyScreenDump(buf, 'Test_pum_with_special_characters_12', {})
+ call term_sendkeys(buf, "\<ESC>ggVGd")
+ call term_sendkeys(buf, ":filetype indent on\<CR>")
+ call term_sendkeys(buf, ":set nocompatible autoindent& shiftwidth& tabstop&\<CR>")
+ call term_sendkeys(buf, ":setlocal ft=lua\<CR>")
+ call term_sendkeys(buf, "S\<F5>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_with_special_characters_13', {})
+
call StopVimInTerminal(buf)
endfunc
diff --git a/test/old/testdir/test_registers.vim b/test/old/testdir/test_registers.vim
index 2557f2bcbd..02da2ac689 100644
--- a/test/old/testdir/test_registers.vim
+++ b/test/old/testdir/test_registers.vim
@@ -1037,4 +1037,54 @@ func Test_register_cursor_column_negative()
call StopVimInTerminal(buf)
endfunc
+" test '] mark generated by op_yank
+func Test_mark_from_yank()
+ new
+ " double quote object
+ call setline(1, 'test "yank" mark')
+ normal! yi"
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ normal! ya"
+ call assert_equal([0, 1, 13, 0], getpos("']"))
+ " single quote object
+ call setline(1, 'test ''yank'' mark')
+ normal! yi'
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ normal! ya'
+ call assert_equal([0, 1, 13, 0], getpos("']"))
+ " paren object
+ call setline(1, 'test (yank) mark')
+ call cursor(1, 9)
+ normal! yi(
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ call cursor(1, 9)
+ normal! ya(
+ call assert_equal([0, 1, 11, 0], getpos("']"))
+ " brace object
+ call setline(1, 'test {yank} mark')
+ call cursor(1, 9)
+ normal! yi{
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ call cursor(1, 9)
+ normal! ya{
+ call assert_equal([0, 1, 11, 0], getpos("']"))
+ " bracket object
+ call setline(1, 'test [yank] mark')
+ call cursor(1, 9)
+ normal! yi[
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ call cursor(1, 9)
+ normal! ya[
+ call assert_equal([0, 1, 11, 0], getpos("']"))
+ " block object
+ call setline(1, 'test <yank> mark')
+ call cursor(1, 9)
+ normal! yi<
+ call assert_equal([0, 1, 10, 0], getpos("']"))
+ call cursor(1, 9)
+ normal! ya<
+ call assert_equal([0, 1, 11, 0], getpos("']"))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_tagjump.vim b/test/old/testdir/test_tagjump.vim
index efc5e4cebe..74ae4a6c73 100644
--- a/test/old/testdir/test_tagjump.vim
+++ b/test/old/testdir/test_tagjump.vim
@@ -1696,4 +1696,21 @@ func Test_tag_guess_short()
set tags& cpoptions-=t
endfunc
+func Test_tag_excmd_with_nostartofline()
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "f\tXfile\tascii"],
+ \ 'Xtags', 'D')
+ call writefile(['f', 'foobar'], 'Xfile', 'D')
+
+ set nostartofline
+ new Xfile
+ setlocal tags=Xtags
+ normal! G$
+ " This used to cause heap-buffer-overflow
+ tag f
+
+ bwipe!
+ set startofline&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim
index 328ac502bf..f5a5970e24 100644
--- a/test/old/testdir/test_visual.vim
+++ b/test/old/testdir/test_visual.vim
@@ -1101,6 +1101,50 @@ func Test_exclusive_selection()
bw!
endfunc
+" Test for inclusive motion in visual mode with 'exclusive' selection
+func Test_inclusive_motion_selection_exclusive()
+ func s:compare_exclu_inclu(line, keys, expected_exclu)
+ let msg = "data: '" . a:line . "' operation: '" . a:keys . "'"
+ call setline(1, a:line)
+ set selection=exclusive
+ call feedkeys(a:keys, 'xt')
+ call assert_equal(a:expected_exclu, getpos('.'), msg)
+ let pos_ex = col('.')
+ set selection=inclusive
+ call feedkeys(a:keys, 'xt')
+ let pos_in = col('.')
+ call assert_equal(1, pos_ex - pos_in, msg)
+ endfunc
+
+ new
+ " Test 'e' motion
+ set selection=exclusive
+ call setline(1, 'eins zwei drei')
+ norm! ggvey
+ call assert_equal('eins', @")
+ call setline(1, 'abc(abc)abc')
+ norm! ggveeed
+ call assert_equal(')abc', getline(1))
+ call setline(1, 'abc(abc)abc')
+ norm! gg3lvey
+ call assert_equal('(abc', @")
+ call s:compare_exclu_inclu('abc(abc)abc', 'ggveee', [0, 1, 8, 0])
+ " Test 'f' motion
+ call s:compare_exclu_inclu('geschwindigkeit', 'ggvfefe', [0, 1, 14, 0])
+ call s:compare_exclu_inclu('loooooooooooong', 'ggv2fo2fo2fo', [0, 1, 8, 0])
+ " Test 't' motion
+ call s:compare_exclu_inclu('geschwindigkeit', 'ggv2te', [0, 1, 13, 0])
+ call s:compare_exclu_inclu('loooooooooooong', 'gglv2to2to2to', [0, 1, 6, 0])
+ " Test ';' motion
+ call s:compare_exclu_inclu('geschwindigkeit', 'ggvfi;;', [0, 1, 15, 0])
+ call s:compare_exclu_inclu('geschwindigkeit', 'ggvti;;', [0, 1, 14, 0])
+ call s:compare_exclu_inclu('loooooooooooong', 'ggv2fo;;', [0, 1, 6, 0])
+ call s:compare_exclu_inclu('loooooooooooong', 'ggvl2to;;', [0, 1, 6, 0])
+ " Clean up
+ set selection&
+ bw!
+endfunc
+
" Test for starting linewise visual with a count.
" This test needs to be run without any previous visual mode. Otherwise the
" count will use the count from the previous visual mode.
diff --git a/test/unit/multiqueue_spec.lua b/test/unit/multiqueue_spec.lua
index 931a5efa94..36dae403be 100644
--- a/test/unit/multiqueue_spec.lua
+++ b/test/unit/multiqueue_spec.lua
@@ -25,7 +25,7 @@ describe('multiqueue (multi-level event-queue)', function()
before_each(function()
child_call_once(function()
- parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL)
+ parent = multiqueue.multiqueue_new(ffi.NULL, ffi.NULL)
child1 = multiqueue.multiqueue_new_child(parent)
child2 = multiqueue.multiqueue_new_child(parent)
child3 = multiqueue.multiqueue_new_child(parent)