Compare commits

...

14 Commits

Author SHA1 Message Date
marvim
c381eb052b docs: update version.c 2025-09-02 03:25:30 +00:00
zeertzjq
b4d21f141c vim-patch:71149df: runtime(java): Dismiss "g:markdown_fenced_languages" for Java buffers (#35596)
No support is provided or planned for language recognition
in code snippets of documentation comments.  Requesting to
load arbitrary syntax plugins with the aid of the concerned
variable is therefore wasteful in general and erroneous when
paired languages ":syn-include" one another without taking
steps to manage circularity.

related: vim/vim#17308
related: vim/vim#17220
closes: vim/vim#18172

71149dfec5

Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
2025-09-02 08:28:07 +08:00
Justin M. Keyes
4f374bf938 Merge #29073 docs: Lua plugin guide 2025-09-01 19:53:13 -04:00
zeertzjq
c4c69c5012 vim-patch:9.1.1724: Compiler warning about ununitialized variable in ex_docmd. (#35595)
Problem:  Compiler warning about ununitialized variable in ex_docmd.
Solution: Initialize result variable (mityu)

Silence uninitialized variable warning produced by clang 21.1.0

closes: vim/vim#18182

5f5a1c5876

Co-authored-by: mityu <mityu.mail@gmail.com>
2025-09-02 07:46:08 +08:00
Justin M. Keyes
a5e7ccc329 docs: Lua plugin development guide 2025-09-01 19:42:45 -04:00
Marc Jakobi
28ab656122 docs: Lua plugin development guide 2025-09-01 18:42:02 -04:00
Shadman
8a12a01466 feat(progress): better default format + history sync #35533
Problem:
The default progress message doesn't account for
message-status. Also, the title and percent sections don't get written
to history. And progress percent is hard to find with variable length messages.

Solution:
Apply highlighting on Title based on status. And sync the formated msg
in history too. Also updates the default progress message format to
{title}: {percent}% msg
2025-09-01 15:13:21 -07:00
Yochem van Rosmalen
4cda52a5d1 docs(treesitter): fix language-injection url #35592 2025-09-01 14:08:08 -07:00
Justin M. Keyes
d8a8825679 feat(lua): vim.wait() returns callback results #35588
Problem:
The callback passed to `vim.wait` cannot return results directly, it
must set upvalues or globals.

    local rv1, rv2, rv3
    local ok = vim.wait(200, function()
      rv1, rv2, rv3 = 'a', 42, { ok = { 'yes' } }
      return true
    end)

Solution:
Let the callback return values after the first "status" result.

    local ok, rv1, rv2, rv3 = vim.wait(200, function()
      return true, 'a', 42, { ok = { 'yes' } }
    end)
2025-09-01 13:26:46 -07:00
Yi Ming
6888f65be1 feat(lsp): vim.lsp.inline_completion on_accept #35507 2025-09-01 08:46:29 -07:00
Volodymyr Chernetskyi
06df337617 vim-patch:9.1.1718: filetype: kubectl config file is not recognized #35583
Problem:  filetype: kubectl config file is not recognized
Solution: Detect .kube/kubectl as yaml filetype
          (Volodymyr Chernetskyi).

References:
- https://kubernetes.io/docs/reference/kubectl/kuberc/

closes: vim/vim#18169

6cd6857cbe
2025-09-01 08:16:43 -07:00
Volodymyr Chernetskyi
8fc1db043a vim-patch:9.1.1717: filetype: AWS cli alias file is not recognized #35581
Problem:  filetype: AWS cli alias file is not recognized
Solution: Detect .aws/cli/alias as confini filetype
          (Volodymyr Chernetskyi).

References:
- https://docs.aws.amazon.com/cli/v1/userguide/cli-usage-alias.html

related: vim/vim#18169

be0589f1d2
2025-09-01 08:16:34 -07:00
Justin M. Keyes
d4f789fd78 ci(release): link to release notes #35585
fix #35580
2025-09-01 08:12:11 -07:00
Wise Man
3c601d02dc ci: Windows arm64 packages #35345
Problem:
Neovim binaries are not provided for Windows ARM64.
GitHub Actions now offer native CI runners for Windows on ARM devices
(windows-11-arm), enabling automated builds and testing.

Solution:
- Modified CMake packaging to include packaging windows on arm binaries.
- Modified env script to install and initialize vs setup for arm64 arch. 

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-08-31 22:35:48 -07:00
30 changed files with 889 additions and 233 deletions

View File

@@ -1,9 +1,17 @@
# This script enables Developer Command Prompt
# See https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-powershell
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if ($installationPath -and (Test-Path "$installationPath\Common7\Tools\vsdevcmd.bat")) {
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | ForEach-Object {
$name, $value = $_ -split '=', 2
"$name=$value" >> $env:GITHUB_ENV
}
if ($env:BUILD_ARCH -eq "arm64") {
$arch = "arm64"
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.arm64 -property installationPath
} else {
$arch = "x64"
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
}
if ($installationPath) {
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=$arch -no_logo && set" |
ForEach-Object {
$name, $value = $_ -split '=', 2
"$name=$value" >> $env:GITHUB_ENV
}
}

View File

@@ -2,21 +2,26 @@
${NVIM_VERSION}
```
## Release notes
- [Changelog](https://github.com/neovim/neovim/commit/${NVIM_COMMIT}) (fixes + features)
- [News](./runtime/doc/news.txt) (`:help news` in Nvim)
## Install
### Windows
#### Zip
1. Download **nvim-win64.zip**
1. Download **nvim-win64.zip** (or **nvim-win-arm64.zip** for ARM)
2. Extract the zip
3. Run `nvim.exe` on your CLI of choice
3. Run `nvim.exe` in your terminal
#### MSI
1. Download **nvim-win64.msi**
1. Download **nvim-win64.msi** (or **nvim-win-arm64.msi** for ARM)
2. Run the MSI
3. Run `nvim.exe` on your CLI of choice
3. Run `nvim.exe` in your terminal
Note: On Windows "Server" you may need to [install vcruntime140.dll](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170).

View File

@@ -132,27 +132,44 @@ jobs:
windows:
needs: setup
runs-on: windows-2022
strategy:
matrix:
include:
- runner: windows-2022
arch: x86_64
archive_name: nvim-win64
- runner: windows-11-arm
arch: arm64
archive_name: nvim-win-arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
with:
# Perform a full checkout #13471
fetch-depth: 0
- run: .github/scripts/env.ps1
env:
BUILD_ARCH: ${{ matrix.arch }}
- name: Install Wix
run: |
Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" -OutFile "wix314-binaries.zip"
Expand-Archive -Path "wix314-binaries.zip" -DestinationPath "C:/wix"
echo "C:\wix" >> $env:GITHUB_PATH
- name: Build deps
run: |
cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build .deps
- name: build package
- name: Build package
run: |
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build build --target package
- uses: actions/upload-artifact@v4
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: nvim-win64
name: nvim-win-${{ matrix.arch }}
path: |
build/nvim-win64.msi
build/nvim-win64.zip
build/${{ matrix.archive_name }}.zip
build/${{ matrix.archive_name }}.msi
retention-days: 1
publish:
@@ -196,10 +213,11 @@ jobs:
- name: Publish release
env:
NVIM_VERSION: ${{ needs.linux.outputs.version }}
NVIM_COMMIT: ${{ github.sha }}
DEBUG: api
run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
if [ "$TAG_NAME" != "nightly" ]; then
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/*
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win-x86_64/* nvim-win-arm64/*
fi
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/*
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win-x86_64/* nvim-win-arm64/*

View File

@@ -1,4 +1,4 @@
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|ARM64|aarch64)$")
set(CMAKE_SYSTEM_PROCESSOR arm64)
endif()
@@ -27,9 +27,13 @@ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
if(WIN32)
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(CPACK_PACKAGE_FILE_NAME "nvim-win-arm64")
else()
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
endif()
set(CPACK_GENERATOR ZIP WIX)
# WIX
# CPACK_WIX_UPGRADE_GUID should be set, but should never change.
# CPACK_WIX_PRODUCT_GUID should not be set (leave as default to auto-generate).

View File

@@ -2229,6 +2229,16 @@ is_enabled({filter}) *vim.lsp.inlay_hint.is_enabled()*
==============================================================================
Lua module: vim.lsp.inline_completion *lsp-inline_completion*
*vim.lsp.inline_completion.Item*
Fields: ~
• {client_id} (`integer`) Client ID
• {insert_text} (`string|lsp.StringValue`) The text to be inserted, can
be a snippet.
• {range}? (`vim.Range`) Which range it be applied.
• {command}? (`lsp.Command`) Corresponding server command.
enable({enable}, {filter}) *vim.lsp.inline_completion.enable()*
Enables or disables inline completion for the {filter}ed scope, inline
completion will automatically be refreshed when you are in insert mode.
@@ -2246,9 +2256,9 @@ enable({enable}, {filter}) *vim.lsp.inline_completion.enable()*
for all.
get({opts}) *vim.lsp.inline_completion.get()*
Apply the currently displayed completion candidate to the buffer.
Accept the currently displayed completion candidate to the buffer.
It returns false when no candidate can be applied, so you can use the
It returns false when no candidate can be accepted, so you can use the
return value to implement a fallback: >lua
vim.keymap.set('i', '<Tab>', function()
if not vim.lsp.inline_completion.get() then
@@ -2265,6 +2275,10 @@ get({opts}) *vim.lsp.inline_completion.get()*
• {opts} (`table?`) A table with the following fields:
• {bufnr}? (`integer`, default: 0) Buffer handle, or 0 for
current.
• {on_accept}? (`fun(item: vim.lsp.inline_completion.Item)`)
Accept handler, called with the accepted item. If not
provided, the default handler is used, which applies changes
to the buffer based on the completion item.
Return: ~
(`boolean`) `true` if a completion was applied, else `false`.

View File

@@ -10,19 +10,18 @@
==============================================================================
Introduction *lua-guide*
This guide will go through the basics of using Lua in Nvim. It is not meant
to be a comprehensive encyclopedia of all available features, nor will it
detail all intricacies. Think of it as a survival kit -- the bare minimum
needed to know to comfortably get started on using Lua in Nvim.
An important thing to note is that this isn't a guide to the Lua language
itself. Rather, this is a guide on how to configure and modify Nvim through
the Lua language and the functions we provide to help with this. Take a look
at |luaref| and |lua-concepts| if you'd like to learn more about Lua itself.
Similarly, this guide assumes some familiarity with the basics of Nvim
This guide introduces the basics of everyday usage of Lua to configure and
operate Nvim. It assumes some familiarity with the (non-Lua) basics of Nvim
(commands, options, mappings, autocommands), which are covered in the
|user-manual|.
This is not a comprehensive encyclopedia of all available features. Think of
it as a survival kit: the bare minimum needed to comfortably get started on
using Lua in Nvim.
See |lua-plugin| for guidance on developing Lua plugins.
See |luaref| and |lua-concepts| for details on the Lua programming language.
------------------------------------------------------------------------------
Some words on the API *lua-guide-api*

309
runtime/doc/lua-plugin.txt Normal file
View File

@@ -0,0 +1,309 @@
*lua-plugin.txt* Nvim
NVIM REFERENCE MANUAL
Guide to developing Lua plugins for Nvim
Type |gO| to see the table of contents.
==============================================================================
Introduction *lua-plugin*
This document provides guidance for developing Nvim (Lua) plugins:
See |lua-guide| for guidance on using Lua to configure and operate Nvim.
See |luaref| and |lua-concepts| for details on the Lua programming language.
==============================================================================
Creating your first plugin *lua-plugin-new*
Any Vimscript or Lua code file that lives in the right directory,
automatically is a "plugin". There's no maniest or "registration" required.
You can try it right now:
1. Visit your config directory: >
:exe 'edit' stdpath('config')
<
2. Create a `plugin/foo.lua` file in there.
3. Add something to it, like: >lua
vim.print('Hello World')
<
4. Start `nvim` and notice that it prints "Hello World" in the messages area.
Check `:messages` if you don't see it.
Besides `plugin/foo.lua`, which is always run at startup, you can define Lua
modules in the `lua/` directory. Those modules aren't loaded until your
`plugin/foo.lua`, the user, calls `require(…)`.
==============================================================================
Type safety *lua-plugin-type-safety*
Lua, as a dynamically typed language, is great for configuration. It provides
virtually immediate feedback.
But for larger projects, this can be a double-edged sword, leaving your plugin
susceptible to unexpected bugs at the wrong time.
You can leverage LuaCATS or "emmylua" annotations https://luals.github.io/wiki/annotations/
along with lua-language-server ("LuaLS") https://luals.github.io/ to catch
potential bugs in your CI before your plugin's users do. The Nvim codebase
uses these annotations extensively.
TOOLS
- lua-typecheck-action https://github.com/marketplace/actions/lua-typecheck-action
- lua-language-server https://luals.github.io
==============================================================================
Keymaps *lua-plugin-keymaps*
Avoid creating excessive keymaps automatically. Doing so can conflict with
user |mapping|s.
NOTE: An example for uncontroversial keymaps are buffer-local |mapping|s for
specific file types or floating windows, or <Plug> mappings.
A common approach to allow keymap configuration is to define a declarative DSL
https://en.wikipedia.org/wiki/Domain-specific_language via a `setup` function.
However, doing so means that
- You will have to implement and document it yourself.
- Users will likely face inconsistencies if another plugin has a slightly
different DSL.
- |init.lua| scripts that call such a `setup` function may throw an error if
the plugin is not installed or disabled.
As an alternative, you can provide |<Plug>| mappings to allow users to define
their own keymaps with |vim.keymap.set()|.
- This requires one line of code in user configs.
- Even if your plugin is not installed or disabled, creating the keymap won't
throw an error.
Another option is to simply expose a Lua function or |user-commands|.
Some benefits of |<Plug>| mappings are that you can
- Enforce options like `expr = true`.
- Use |vim.keymap|'s built-in mode handling to expose functionality only for
specific |map-modes|.
- Handle different |map-modes| differently with a single mapping, without
adding mode checks to the underlying implementation.
- Detect user-defined mappings through |hasmapto()| before creating defaults.
Some benefits of exposing a Lua function are:
- Extensibility, if the function takes an options table as an argument.
- A cleaner UX, if there are many options and enumerating all combinations
of options would result in a lot of |<Plug>| mappings.
NOTE: If your function takes an options table, users may still benefit
from |<Plug>| mappings for the most common combinations.
KEYMAP EXAMPLE
In your plugin:
>lua
vim.keymap.set('n', '<Plug>(SayHello)', function()
print('Hello from normal mode')
end, { noremap = true })
vim.keymap.set('v', '<Plug>(SayHello)', function()
print('Hello from visual mode')
end, { noremap = true })
<
In the user's config:
>lua
vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
<
==============================================================================
Initialization *lua-plugin-init*
Newcomers to Lua plugin development will often put all initialization logic in
a single `setup` function, which takes a table of options.
If you do this, users will be forced to call this function in order to use
your plugin, even if they are happy with the default configuration.
Strictly separated configuration and smart initialization allow your plugin to
work out of the box.
NOTE: A well designed plugin has minimal impact on startup time. See also
|lua-plugin-lazy|.
Common approaches to a strictly separated configuration are:
- A Lua function, e.g. `setup(opts)` or `configure(opts)`, which only overrides the
default configuration and does not contain any initialization logic.
- A Vimscript compatible table (e.g. in the |vim.g| or |vim.b| namespace) that your
plugin reads from and validates at initialization time.
See also |lua-vim-variables|.
Typically, automatic initialization logic is done in a |plugin| or |ftplugin|
script. See also |'runtimepath'|.
==============================================================================
Lazy loading *lua-plugin-lazy*
Some users like to micro-manage "lazy loading" of plugins by explicitly
configuring which commands and key mappings load the plugin.
Your plugin should not depend on every user micro-managing their configuration
in such a way. Nvim has a mechanism for every plugin to do its own implicit
lazy-loading (in Vimscript it's called |autoload|), via `autoload/`
(Vimscript) and `lua/` (Lua). Plugin authors can provide "lazy loading" by
providing a `plugin/<name>.lua` file which defines their commands and
keymappings. This file should be small, and should not eagerly `require()` the
rest of your plugin. Commands and mappings should do the `require()`.
Guidance:
- Plugins should arrange their "lazy" behavior once, instead of expecting every user to micromanage it.
- Keep `plugin/<name>.lua` small, avoid eagerly calling `require()` on modules
until a command or mapping is actually used.
------------------------------------------------------------------------------
Defer require() calls *lua-plugin-defer-require*
`plugin/<name>.lua` scripts (|plugin|) are eagerly run at startup; this is
intentional, so that plugins can setup the (minimal) commands and keymappings
that users will use to invoke the plugin. This also means these "plugin/"
files should NOT eagerly `require` Lua modules.
For example, instead of:
>lua
local foo = require('foo')
vim.api.nvim_create_user_command('MyCommand', function()
foo.do_something()
end, { -- ... })
<
which calls `require('foo')` as soon as the module is loaded, you can
lazy-load it by moving the `require` into the command's implementation:
>lua
vim.api.nvim_create_user_command('MyCommand', function()
local foo = require('foo')
foo.do_something()
end, {
-- ...
})
<
Likewise, if a plugin uses a Lua module as an entrypoint, it should
defer `require` calls too.
NOTE: For a Vimscript alternative to `require`, see |autoload|.
NOTE: If you are worried about eagerly creating user commands, autocommands or
keymaps at startup: Plugin managers that provide abstractions for lazy-loading
plugins on such events do the same amount of work. There is no performance
benefit for users to define lazy-loading entrypoints in their configuration
instead of plugins defining it in `plugin/<name>.lua`.
NOTE: You can use |--startuptime| to |profile| the impact a plugin has on
startup time.
------------------------------------------------------------------------------
Filetype-specific functionality *lua-plugin-filetype*
Consider making use of 'filetype' for any functionality that is specific to
a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
script.
FILETYPE EXAMPLE
A plugin tailored to Rust development might have initialization in
`ftplugin/rust.lua`:
>lua
if not vim.g.loaded_my_rust_plugin then
-- Initialize
end
-- NOTE: Using `vim.g.loaded_` prevents the plugin from initializing twice
-- and allows users to prevent plugins from loading
-- (in both Lua and Vimscript).
vim.g.loaded_my_rust_plugin = true
local bufnr = vim.api.nvim_get_current_buf()
-- do something specific to this buffer,
-- e.g. add a |<Plug>| mapping or create a command
vim.keymap.set('n', '<Plug>(MyPluginBufferAction)', function()
print('Hello')
end, { noremap = true, buffer = bufnr, })
<
==============================================================================
Configuration *lua-plugin-config*
Once you have merged the default configuration with the user's config, you
should validate configs.
Validations could include:
- Correct types, see |vim.validate()|
- Unknown fields in the user config (e.g. due to typos).
This can be tricky to implement, and may be better suited for a |health|
check, to reduce overhead.
==============================================================================
Troubleshooting *lua-plugin-troubleshooting*
While developing a plugin, you can use the |:restart| command to see the
result of code changes in your plugin.
HEALTH
Nvim's "health" framework gives plugins a simple way to report status checks
to users. See |health-dev| for an example.
Basically, this just means your plugin will have a `lua/{plugin}/health.lua`
file. |:checkhealth| will automatically find this file when it runs.
Some things to validate:
- User configuration
- Proper initialization
- Presence of Lua dependencies (e.g. other plugins)
- Presence of external dependencies
MINIMAL CONFIG TEMPLATE
It can be useful to provide a template for a minimal configuration, along with
a guide on how to use it to reproduce issues.
==============================================================================
Versioning and releases *lua-plugin-versioning*
Consider:
- Use |vim.deprecate()| or a `---@deprecate` annotation when you need to
communicate a (future) breaking change or discourged practice.
- Using SemVer https://semver.org/ tags and releases to properly communicate
bug fixes, new features, and breaking changes.
- Automating versioning and releases in CI.
- Publishing to luarocks https://luarocks.org, especially if your plugin
has dependencies or components that need to be built; or if it could be a
dependency for another plugin.
FURTHER READING
- Luarocks ❤️ Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin
VERSIONING TOOLS
- luarocks-tag-release
https://github.com/marketplace/actions/luarocks-tag-release
- release-please-action
https://github.com/marketplace/actions/release-please-action
- semantic-release
https://github.com/semantic-release/semantic-release
==============================================================================
Documentation *lua-plugin-doc*
Provide vimdoc (see |help-writing|), so that users can read your plugin's
documentation in Nvim, by entering `:h {plugin}` in |command-mode|.
DOCUMENTATION TOOLS
- panvimdoc https://github.com/kdheepak/panvimdoc
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:

View File

@@ -843,32 +843,29 @@ vim.ui_detach({ns}) *vim.ui_detach()*
• {ns} (`integer`) Namespace ID
vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
Wait for {time} in milliseconds until {callback} returns `true`.
Waits up to `time` milliseconds, until `callback` returns `true`
(success). Executes `callback` immediately, then at intervals of
approximately `interval` milliseconds (default 200). Returns all
`callback` results on success.
Executes {callback} immediately and at approximately {interval}
milliseconds (default 200). Nvim still processes other events during this
time.
Cannot be called while in an |api-fast| event.
Nvim processes other events while waiting. Cannot be called during an
|api-fast| event.
Examples: >lua
---
-- Wait for 100 ms, allowing other events to process
vim.wait(100, function() end)
-- Wait for 100 ms, allowing other events to process.
vim.wait(100)
---
-- Wait for 100 ms or until global variable set.
vim.wait(100, function() return vim.g.waiting_for_var end)
-- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
vim.wait(1000, function() return vim.g.foo end, 500)
---
-- Wait for 1 second or until global variable set, checking every ~500 ms
vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
-- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
local ok, rv1, rv2, rv3 = vim.wait(100, function()
return vim.g.foo, 'a', 42, { ok = { 'yes' } }
end)
---
-- Schedule a function to set a value in 100ms
-- Schedule a function to set a value in 100ms. This would wait 10s if blocked, but actually
-- only waits 100ms because `vim.wait` processes other events while waiting.
vim.defer_fn(function() vim.g.timer_result = true end, 100)
-- Would wait ten seconds if results blocked. Actually only waits 100 ms
if vim.wait(10000, function() return vim.g.timer_result end) then
print('Only waiting a little bit of time!')
end
@@ -886,10 +883,10 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
Return (multiple): ~
(`boolean`)
(`-1|-2?`)
• If {callback} returns `true` during the {time}: `true, nil`
If {callback} never returns `true` during the {time}: `false, -1`
If {callback} is interrupted during the {time}: `false, -2`
If {callback} errors, the error is raised.
• If callback returns `true` before timeout: `true, nil, ...`
On timeout: `false, -1`
On interrupt: `false, -2`
On error: the error is raised.
==============================================================================

View File

@@ -186,7 +186,7 @@ The following new APIs or features were added.
Additionally |TSNode:range()| now takes an optional {include_bytes} argument.
• Treesitter injection queries now use the format described at
https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection .
https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection .
Support for the previous format will be removed in a future release.
• Added |nvim_get_hl()| for getting highlight group definitions in a format

View File

@@ -204,6 +204,7 @@ HIGHLIGHTS
• |hl-DiffTextAdd| highlights added text within a changed line.
• |hl-StderrMsg| |hl-StdoutMsg|
• |hl-SnippetTabstopActive| highlights the currently active tabstop.
• |hl-OkMsg| highlights success messages.
LSP
@@ -247,6 +248,7 @@ LSP
LUA
• |vim.wait()| returns the callback results.
• Lua type annotations for `vim.uv`.
• |vim.hl.range()| now allows multiple timed highlights.
• |vim.tbl_extend()| and |vim.tbl_deep_extend()| now accept a function behavior argument.

View File

@@ -5263,6 +5263,8 @@ EndOfBuffer Filler lines (~) after the last line in the buffer.
By default, this is highlighted like |hl-NonText|.
*hl-TermCursor*
TermCursor Cursor in a focused terminal.
*hl-OkMsg*
OkMsg Success messages on the command line.
*hl-ErrorMsg*
ErrorMsg Error messages on the command line.
*hl-StderrMsg*

View File

@@ -551,7 +551,7 @@ TREESITTER LANGUAGE INJECTIONS *treesitter-language-injections*
<
Note the following information is adapted from:
https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection
Some source files contain code written in multiple different languages.
Examples include:

View File

@@ -179,34 +179,30 @@ function vim.iconv(str, from, to, opts) end
--- @param fn fun()
function vim.schedule(fn) end
--- Wait for {time} in milliseconds until {callback} returns `true`.
--- Waits up to `time` milliseconds, until `callback` returns `true` (success). Executes
--- `callback` immediately, then at intervals of approximately `interval` milliseconds (default
--- 200). Returns all `callback` results on success.
---
--- Executes {callback} immediately and at approximately {interval}
--- milliseconds (default 200). Nvim still processes other events during
--- this time.
---
--- Cannot be called while in an |api-fast| event.
--- Nvim processes other events while waiting.
--- Cannot be called during an |api-fast| event.
---
--- Examples:
---
--- ```lua
--- ---
--- -- Wait for 100 ms, allowing other events to process
--- vim.wait(100, function() end)
--- -- Wait for 100 ms, allowing other events to process.
--- vim.wait(100)
---
--- ---
--- -- Wait for 100 ms or until global variable set.
--- vim.wait(100, function() return vim.g.waiting_for_var end)
--- -- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
--- vim.wait(1000, function() return vim.g.foo end, 500)
---
--- ---
--- -- Wait for 1 second or until global variable set, checking every ~500 ms
--- vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
--- -- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
--- local ok, rv1, rv2, rv3 = vim.wait(100, function()
--- return vim.g.foo, 'a', 42, { ok = { 'yes' } }
--- end)
---
--- ---
--- -- Schedule a function to set a value in 100ms
--- -- Schedule a function to set a value in 100ms. This would wait 10s if blocked, but actually
--- -- only waits 100ms because `vim.wait` processes other events while waiting.
--- vim.defer_fn(function() vim.g.timer_result = true end, 100)
---
--- -- Would wait ten seconds if results blocked. Actually only waits 100 ms
--- if vim.wait(10000, function() return vim.g.timer_result end) then
--- print('Only waiting a little bit of time!')
--- end
@@ -216,11 +212,11 @@ function vim.schedule(fn) end
--- @param callback? fun(): boolean Optional callback. Waits until {callback} returns true
--- @param interval? integer (Approximate) number of milliseconds to wait between polls
--- @param fast_only? boolean If true, only |api-fast| events will be processed.
--- @return boolean, nil|-1|-2
--- - If {callback} returns `true` during the {time}: `true, nil`
--- - If {callback} never returns `true` during the {time}: `false, -1`
--- - If {callback} is interrupted during the {time}: `false, -2`
--- - If {callback} errors, the error is raised.
--- @return boolean, nil|-1|-2, ...
--- - If callback returns `true` before timeout: `true, nil, ...`
--- - On timeout: `false, -1`
--- - On interrupt: `false, -2`
--- - On error: the error is raised.
function vim.wait(time, callback, interval, fast_only) end
--- Subscribe to |ui-events|, similar to |nvim_ui_attach()| but receive events in a Lua callback.

View File

@@ -2236,6 +2236,7 @@ local pattern = {
},
['/%.'] = {
['/%.aws/credentials$'] = 'confini',
['/%.aws/cli/alias$'] = 'confini',
['/%.gitconfig%.d/'] = starsetf('gitconfig'),
['/%.gnupg/gpg%.conf$'] = 'gpg',
['/%.gnupg/options$'] = 'gpg',
@@ -2244,6 +2245,7 @@ local pattern = {
['/%.pinforc$'] = 'pinfo',
['/%.cargo/credentials$'] = 'toml',
['/%.init/.*%.override$'] = 'upstart',
['/%.kube/kuberc$'] = 'yaml',
},
['calendar/'] = {
['/%.calendar/'] = starsetf('calendar'),

View File

@@ -11,11 +11,11 @@ local M = {}
local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
---@class (private) vim.lsp.inline_completion.CurrentItem
---@field index integer The index among all items form all clients.
---@class vim.lsp.inline_completion.Item
---@field _index integer The index among all items form all clients.
---@field client_id integer Client ID
---@field insert_text string|lsp.StringValue The text to be inserted, can be a snippet.
---@field filter_text? string
---@field _filter_text? string
---@field range? vim.Range Which range it be applied.
---@field command? lsp.Command Corresponding server command.
@@ -25,7 +25,7 @@ local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
---@class (private) vim.lsp.inline_completion.Completor : vim.lsp.Capability
---@field active table<integer, vim.lsp.inline_completion.Completor?>
---@field timer? uv.uv_timer_t Timer for debouncing automatic requests
---@field current? vim.lsp.inline_completion.CurrentItem Currently selected item
---@field current? vim.lsp.inline_completion.Item Currently selected item
---@field client_state table<integer, vim.lsp.inline_completion.ClientState>
local Completor = {
name = 'inline_completion',
@@ -146,11 +146,11 @@ function Completor:select(index, show_index)
local client = assert(vim.lsp.get_client_by_id(client_id))
local range = item.range and vim.range.lsp(self.bufnr, item.range, client.offset_encoding)
self.current = {
index = index,
_index = index,
client_id = client_id,
insert_text = item.insertText,
range = range,
filter_text = item.filterText,
_filter_text = item.filterText,
command = item.command,
}
@@ -281,19 +281,14 @@ function Completor:abort()
self.current = nil
end
--- Apply the current completion item to the buffer.
--- Accept the current completion item to the buffer.
---
---@package
function Completor:apply()
local current = self.current
self:abort()
if not current then
return
end
local insert_text = current.insert_text
---@param item vim.lsp.inline_completion.Item
function Completor:accept(item)
local insert_text = item.insert_text
if type(insert_text) == 'string' then
local range = current.range
local range = item.range
if range then
local lines = vim.split(insert_text, '\n')
api.nvim_buf_set_text(
@@ -304,7 +299,7 @@ function Completor:apply()
range.end_.col,
lines
)
local pos = range.start:to_cursor()
local pos = item.range.start:to_cursor()
api.nvim_win_set_cursor(vim.fn.bufwinid(self.bufnr), {
pos[1] + #lines - 1,
(#lines == 1 and pos[2] or 0) + #lines[#lines],
@@ -317,9 +312,9 @@ function Completor:apply()
end
-- Execute the command *after* inserting this completion.
if current.command then
local client = assert(vim.lsp.get_client_by_id(current.client_id))
client:exec_cmd(current.command, { bufnr = self.bufnr })
if item.command then
local client = assert(vim.lsp.get_client_by_id(item.client_id))
client:exec_cmd(item.command, { bufnr = self.bufnr })
end
end
@@ -381,7 +376,7 @@ function M.select(opts)
end
local n = completor:count_items()
local index = current.index + count
local index = current._index + count
if wrap then
index = (index - 1) % n + 1
else
@@ -396,10 +391,15 @@ end
--- Buffer handle, or 0 for current.
--- (default: 0)
---@field bufnr? integer
--- Apply the currently displayed completion candidate to the buffer.
---
--- It returns false when no candidate can be applied,
--- Accept handler, called with the accepted item.
--- If not provided, the default handler is used,
--- which applies changes to the buffer based on the completion item.
---@field on_accept? fun(item: vim.lsp.inline_completion.Item)
--- Accept the currently displayed completion candidate to the buffer.
---
--- It returns false when no candidate can be accepted,
--- so you can use the return value to implement a fallback:
---
--- ```lua
@@ -420,11 +420,23 @@ function M.get(opts)
opts = opts or {}
local bufnr = vim._resolve_bufnr(opts.bufnr)
local on_accept = opts.on_accept
local completor = Completor.active[bufnr]
if completor and completor.current then
-- Schedule apply to allow `get()` can be mapped with `<expr>`.
vim.schedule(function()
completor:apply()
local item = completor.current
completor:abort()
if not item then
return
end
if on_accept then
on_accept(item)
else
completor:accept(item)
end
end)
return true
end

View File

@@ -988,7 +988,7 @@ end
---@private
--- Extract injections according to:
--- https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
--- https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection
---@param match table<integer,TSNode[]>
---@param metadata vim.treesitter.query.TSMetadata
---@return string?, boolean, Range6[]

View File

@@ -3,7 +3,7 @@
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
" Former Maintainer: Claudio Fleiner <claudio@fleiner.com>
" Repository: https://github.com/zzzyxwvut/java-vim.git
" Last Change: 2025 Aug 07
" Last Change: 2025 Aug 30
" Please check ":help java.vim" for comments on some of the options
" available.
@@ -447,6 +447,11 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
unlet g:html_syntax_folding
endif
if !empty(get(g:, 'markdown_fenced_languages', []))
let s:markdown_fenced_languages_copy = g:markdown_fenced_languages
unlet g:markdown_fenced_languages
endif
syntax include @javaMarkdown syntax/markdown.vim
try
@@ -464,6 +469,11 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
finally
unlet! b:current_syntax
if exists("s:markdown_fenced_languages_copy")
let g:markdown_fenced_languages = s:markdown_fenced_languages_copy
unlet s:markdown_fenced_languages_copy
endif
if exists("s:html_syntax_folding_copy")
let g:html_syntax_folding = s:html_syntax_folding_copy
unlet s:html_syntax_folding_copy

View File

@@ -796,6 +796,7 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
}
bool is_progress = strequal(kind, "progress");
bool needs_clear = !history;
VALIDATE(is_progress
|| (opts->status.size == 0 && opts->title.size == 0 && opts->percent == 0
@@ -822,7 +823,7 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
MessageData msg_data = { .title = opts->title, .status = opts->status,
.percent = opts->percent, .data = opts->data };
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data);
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data, &needs_clear);
if (opts->verbose) {
verbose_leave();
@@ -833,8 +834,8 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
do_autocmd_progress(id, hl_msg, &msg_data);
}
if (history) {
// history takes ownership
if (!needs_clear) {
// history takes ownership of `hl_msg`
return id;
}

View File

@@ -7360,7 +7360,7 @@ ssize_t find_cmdline_var(const char *src, size_t *usedlen)
char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump,
const char **errormsg, int *escaped, bool empty_is_error)
{
char *result;
char *result = "";
char *resultbuf = NULL;
size_t resultlen;
int valid = VALID_HEAD | VALID_PATH; // Assume valid result.
@@ -7576,7 +7576,6 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum
default:
// should not happen
*errormsg = "";
result = ""; // avoid gcc warning
break;
}

View File

@@ -84,6 +84,7 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_BFOOTER] = "FloatFooter",
[HLF_TS] = "StatusLineTerm",
[HLF_TSNC] = "StatusLineTermNC",
[HLF_OK] = "OkMsg",
});
EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.

View File

@@ -132,6 +132,7 @@ typedef enum {
HLF_TSNC, ///< status line for non-current terminal window
HLF_SE, ///< stderr messages (from shell)
HLF_SO, ///< stdout messages (from shell)
HLF_OK, ///< OK message
HLF_COUNT, ///< MUST be the last one
} hlf_T;

View File

@@ -375,6 +375,7 @@ static const char *highlight_init_light[] = {
"MoreMsg guifg=NvimDarkCyan ctermfg=6",
"NonText guifg=NvimLightGrey4",
"NormalFloat guibg=NvimLightGrey1",
"OkMsg guifg=NvimDarkGreen ctermfg=2",
"Pmenu guibg=NvimLightGrey3 cterm=reverse",
"PmenuThumb guibg=NvimLightGrey4",
"Question guifg=NvimDarkCyan ctermfg=6",
@@ -459,6 +460,7 @@ static const char *highlight_init_dark[] = {
"MoreMsg guifg=NvimLightCyan ctermfg=14",
"NonText guifg=NvimDarkGrey4",
"NormalFloat guibg=NvimDarkGrey1",
"OkMsg guifg=NvimLightGreen ctermfg=10",
"Pmenu guibg=NvimDarkGrey3 cterm=reverse",
"PmenuThumb guibg=NvimDarkGrey4",
"Question guifg=NvimLightCyan ctermfg=14",

View File

@@ -175,11 +175,18 @@ int nlua_pcall(lua_State *lstate, int nargs, int nresults)
lua_getfield(lstate, -1, "traceback");
lua_remove(lstate, -2);
lua_insert(lstate, -2 - nargs);
int pre_top = lua_gettop(lstate);
int status = lua_pcall(lstate, nargs, nresults, -2 - nargs);
if (status) {
lua_remove(lstate, -2);
} else {
lua_remove(lstate, -1 - nresults);
if (nresults == LUA_MULTRET) {
int new_top = lua_gettop(lstate);
int actual_nres = new_top - pre_top + nargs + 1;
lua_remove(lstate, -1 - actual_nres);
} else {
lua_remove(lstate, -1 - nresults);
}
}
return status;
}
@@ -415,16 +422,28 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
xfree(tw);
}
static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result)
static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result,
int *nresults)
{
int top = lua_gettop(lstate);
lua_pushvalue(lstate, 2);
*status = nlua_pcall(lstate, 0, 1);
*status = nlua_pcall(lstate, 0, LUA_MULTRET);
if (*status) {
return true; // break on error, but keep error on stack
}
*callback_result = lua_toboolean(lstate, -1);
lua_pop(lstate, 1);
return *callback_result; // break if true
*nresults = lua_gettop(lstate) - top;
if (*nresults == 0) {
*callback_result = false;
return false;
}
*callback_result = lua_toboolean(lstate, top + 1);
if (!*callback_result) {
lua_settop(lstate, top);
return false;
}
lua_remove(lstate, top + 1);
(*nresults)--;
return true; // break if true
}
/// "vim.wait(timeout, condition[, interval])" function
@@ -454,8 +473,7 @@ static int nlua_wait(lua_State *lstate)
}
if (!is_function) {
lua_pushliteral(lstate,
"vim.wait: if passed, condition must be a function");
lua_pushliteral(lstate, "vim.wait: callback must be callable");
return lua_error(lstate);
}
}
@@ -488,6 +506,7 @@ static int nlua_wait(lua_State *lstate)
int pcall_status = 0;
bool callback_result = false;
int nresults = 0;
// Flush screen updates before blocking.
ui_flush();
@@ -497,7 +516,8 @@ static int nlua_wait(lua_State *lstate)
(int)timeout,
got_int || (is_function ? nlua_wait_condition(lstate,
&pcall_status,
&callback_result)
&callback_result,
&nresults)
: false));
// Stop dummy timer
@@ -508,18 +528,26 @@ static int nlua_wait(lua_State *lstate)
return lua_error(lstate);
} else if (callback_result) {
lua_pushboolean(lstate, 1);
lua_pushnil(lstate);
if (nresults == 0) {
lua_pushnil(lstate);
nresults = 1;
} else {
lua_insert(lstate, -1 - nresults);
}
return nresults + 1;
} else if (got_int) {
got_int = false;
vgetc();
lua_pushboolean(lstate, 0);
lua_pushinteger(lstate, -2);
return 2;
} else {
lua_pushboolean(lstate, 0);
lua_pushinteger(lstate, -1);
return 2;
}
return 2;
abort();
}
static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
@@ -966,7 +994,8 @@ static void nlua_print_event(void **argv)
HlMessage msg = KV_INITIAL_VALUE;
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
kv_push(msg, chunk);
msg_multihl(INTEGER_OBJ(0), msg, "lua_print", true, false, NULL);
bool needs_clear = false;
msg_multihl(INTEGER_OBJ(0), msg, "lua_print", true, false, NULL, &needs_clear);
}
/// Print as a Vim message

View File

@@ -292,14 +292,61 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_
// Avoid starting a new message for each chunk and adding message to history in msg_keep().
static bool is_multihl = false;
/// Format a progress message, adding title and percent if given.
///
/// @param hl_msg Message chunks
/// @param msg_data Additional data for progress messages
static HlMessage format_progress_message(HlMessage hl_msg, MessageData *msg_data)
{
HlMessage updated_msg = KV_INITIAL_VALUE;
// progress messages are special. displayed as "title: percent% msg"
if (msg_data->title.size != 0) {
// this block draws the "title:" before the progress-message
int hl_id = 0;
if (msg_data->status.data == NULL) {
hl_id = 0;
} else if (strequal(msg_data->status.data, "success")) {
hl_id = syn_check_group("OkMsg", STRLEN_LITERAL("OkMsg"));
} else if (strequal(msg_data->status.data, "failed")) {
hl_id = syn_check_group("ErrorMsg", STRLEN_LITERAL("ErrorMsg"));
} else if (strequal(msg_data->status.data, "running")) {
hl_id = syn_check_group("MoreMsg", STRLEN_LITERAL("MoreMsg"));
} else if (strequal(msg_data->status.data, "cancel")) {
hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
}
kv_push(updated_msg,
((HlMessageChunk){ .text = copy_string(msg_data->title, NULL), .hl_id = hl_id }));
kv_push(updated_msg, ((HlMessageChunk){ .text = cstr_to_string(": "), .hl_id = 0 }));
}
if (msg_data->percent > 0) {
char percent_buf[10];
vim_snprintf(percent_buf, sizeof(percent_buf), "%3ld%% ", (long)msg_data->percent);
String percent = cstr_to_string(percent_buf);
int hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
kv_push(updated_msg, ((HlMessageChunk){ .text = percent, .hl_id = hl_id }));
}
if (kv_size(updated_msg) != 0) {
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
kv_push(updated_msg,
((HlMessageChunk){ .text = copy_string(kv_A(hl_msg, i).text, NULL),
.hl_id = kv_A(hl_msg, i).hl_id }));
}
return updated_msg;
} else {
return hl_msg;
}
}
/// Print message chunks, each with their own highlight ID.
///
/// @param hl_msg Message chunks
/// @param kind Message kind (can be NULL to avoid setting kind)
/// @param history Whether to add message to history
/// @param err Whether to print message as an error
/// @param msg_data Additional data for progress messages
MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bool err,
MessageData *msg_data)
MessageData *msg_data, bool *needs_msg_clear)
{
no_wait_return++;
msg_start();
@@ -311,7 +358,6 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
}
is_multihl = true;
msg_ext_skip_flush = true;
bool is_progress = strequal(kind, "progress");
// provide a new id if not given
if (id.type == kObjectTypeNil) {
@@ -323,12 +369,13 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
}
}
// progress message are special displayed as "title: msg...percent%"
if (is_progress && msg_data && msg_data->title.size != 0) {
// this block draws the "title:" before the progress-message
String title = cstr_as_string(concat_str(msg_data->title.data, ": "));
msg_multiline(title, 0, true, false, &need_clear);
api_free_string(title);
// progress message are special displayed as "title: percent% msg"
if (strequal(kind, "progress") && msg_data) {
HlMessage formated_message = format_progress_message(hl_msg, msg_data);
if (formated_message.items != hl_msg.items) {
*needs_msg_clear = true;
hl_msg = formated_message;
}
}
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
@@ -341,12 +388,6 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
}
if (is_progress && msg_data && msg_data->percent > 0) {
// this block draws the "...percent%" before the progress-message
char percent_buf[10];
vim_snprintf(percent_buf, sizeof(percent_buf), "...%ld%%", (long)msg_data->percent);
msg_multiline(cstr_as_string(percent_buf), 0, true, false, &need_clear);
}
if (history && kv_size(hl_msg)) {
msg_hist_add_multihl(id, hl_msg, false, msg_data);
}
@@ -1265,7 +1306,8 @@ void ex_messages(exarg_T *eap)
}
if (redirecting() || !ui_has(kUIMessages)) {
msg_silent += ui_has(kUIMessages);
msg_multihl(INTEGER_OBJ(0), p->msg, p->kind, false, false, NULL);
bool needs_clear = false;
msg_multihl(INTEGER_OBJ(0), p->msg, p->kind, false, false, NULL, &needs_clear);
msg_silent -= ui_has(kUIMessages);
}
}

View File

@@ -61,7 +61,7 @@ static const int included_patches[] = {
2423,
2422,
2421,
// 2420,
2420,
2419,
// 2418,
2417,
@@ -147,7 +147,7 @@ static const int included_patches[] = {
2337,
2336,
2335,
// 2334,
2334,
2333,
2332,
2331,
@@ -174,14 +174,14 @@ static const int included_patches[] = {
2310,
2309,
2308,
// 2307,
2307,
2306,
2305,
2304,
2303,
2302,
2301,
// 2300,
2300,
2299,
2298,
2297,
@@ -193,9 +193,9 @@ static const int included_patches[] = {
2291,
2290,
2289,
// 2288,
// 2287,
// 2286,
2288,
2287,
2286,
2285,
2284,
2283,
@@ -204,7 +204,7 @@ static const int included_patches[] = {
2280,
// 2279,
2278,
// 2277,
2277,
2276,
2275,
2274,
@@ -241,7 +241,7 @@ static const int included_patches[] = {
2243,
2242,
// 2241,
// 2240,
2240,
2239,
2238,
2237,
@@ -268,10 +268,10 @@ static const int included_patches[] = {
2216,
2215,
2214,
// 2213,
2213,
2212,
2211,
// 2210,
2210,
2209,
// 2208,
2207,
@@ -317,7 +317,7 @@ static const int included_patches[] = {
2167,
2166,
2165,
// 2164,
2164,
2163,
2162,
2161,
@@ -367,12 +367,12 @@ static const int included_patches[] = {
2117,
2116,
2115,
// 2114,
2114,
2113,
2112,
2111,
// 2110,
// 2109,
2110,
2109,
2108,
2107,
2106,
@@ -449,7 +449,7 @@ static const int included_patches[] = {
2035,
2034,
2033,
// 2032,
2032,
2031,
2030,
2029,
@@ -472,7 +472,7 @@ static const int included_patches[] = {
2012,
2011,
2010,
// 2009,
2009,
2008,
2007,
2006,
@@ -483,8 +483,8 @@ static const int included_patches[] = {
2001,
2000,
// 1999,
// 1998,
// 1997,
1998,
1997,
// 1996,
1995,
1994,
@@ -518,11 +518,11 @@ static const int included_patches[] = {
1966,
1965,
// 1964,
// 1963,
1963,
1962,
1961,
1960,
// 1959,
1959,
1958,
// 1957,
1956,
@@ -536,7 +536,7 @@ static const int included_patches[] = {
1948,
1947,
1946,
// 1945,
1945,
// 1944,
// 1943,
1942,
@@ -545,14 +545,14 @@ static const int included_patches[] = {
// 1939,
1938,
1937,
// 1936,
1936,
1935,
// 1934,
1934,
1933,
1932,
1931,
1930,
// 1929,
1929,
// 1928,
1927,
1926,
@@ -563,8 +563,8 @@ static const int included_patches[] = {
1921,
// 1920,
// 1919,
// 1918,
// 1917,
1918,
1917,
1916,
1915,
1914,
@@ -574,12 +574,12 @@ static const int included_patches[] = {
1910,
1909,
// 1908,
// 1907,
1907,
// 1906,
// 1905,
// 1904,
1903,
// 1902,
1902,
1901,
1900,
1899,
@@ -607,7 +607,7 @@ static const int included_patches[] = {
1877,
1876,
1875,
// 1874,
1874,
1873,
1872,
1871,
@@ -631,12 +631,12 @@ static const int included_patches[] = {
1853,
1852,
// 1851,
// 1850,
1850,
1849,
1848,
1847,
1846,
// 1845,
1845,
1844,
1843,
1842,
@@ -667,7 +667,7 @@ static const int included_patches[] = {
1817,
1816,
1815,
// 1814,
1814,
// 1813,
1812,
// 1811,
@@ -692,28 +692,28 @@ static const int included_patches[] = {
1792,
1791,
1790,
// 1789,
1789,
1788,
// 1787,
// 1786,
1786,
1785,
// 1784,
1783,
1782,
1781,
1780,
// 1779,
// 1778,
1779,
1778,
1777,
1776,
1775,
1774,
// 1773,
1773,
1772,
1771,
// 1770,
1769,
// 1768,
1768,
1767,
1766,
1765,
@@ -726,10 +726,10 @@ static const int included_patches[] = {
1758,
1757,
1756,
// 1755,
// 1754,
// 1753,
// 1752,
1755,
1754,
1753,
1752,
// 1751,
1750,
1749,
@@ -748,7 +748,7 @@ static const int included_patches[] = {
1736,
1735,
1734,
// 1733,
1733,
1732,
1731,
1730,
@@ -762,7 +762,7 @@ static const int included_patches[] = {
1722,
1721,
1720,
// 1719,
1719,
// 1718,
1717,
1716,
@@ -772,9 +772,9 @@ static const int included_patches[] = {
1712,
1711,
1710,
// 1709,
1709,
1708,
// 1707,
1707,
1706,
1705,
1704,
@@ -803,9 +803,9 @@ static const int included_patches[] = {
1681,
1680,
1679,
// 1678,
1678,
1677,
// 1676,
1676,
1675,
1674,
// 1673,
@@ -815,8 +815,8 @@ static const int included_patches[] = {
1669,
1668,
1667,
// 1666,
// 1665,
1666,
1665,
1664,
1663,
1662,
@@ -825,17 +825,17 @@ static const int included_patches[] = {
// 1659,
1658,
1657,
// 1656,
1656,
1655,
1654,
1653,
1652,
1651,
1650,
// 1649,
1649,
1648,
1647,
// 1646,
1646,
1645,
1644,
1643,
@@ -845,7 +845,7 @@ static const int included_patches[] = {
1639,
1638,
1637,
// 1636,
1636,
1635,
1634,
1633,
@@ -859,14 +859,14 @@ static const int included_patches[] = {
1625,
1624,
1623,
// 1622,
1622,
1621,
// 1620,
1620,
1619,
1618,
// 1617,
1617,
1616,
// 1615,
1615,
1614,
1613,
// 1612,
@@ -879,13 +879,13 @@ static const int included_patches[] = {
1605,
1604,
1603,
// 1602,
1602,
1601,
// 1600,
1599,
1598,
// 1597,
// 1596,
1596,
1595,
1594,
1593,
@@ -919,7 +919,7 @@ static const int included_patches[] = {
1565,
1564,
1563,
// 1562,
1562,
// 1561,
// 1560,
// 1559,
@@ -931,7 +931,7 @@ static const int included_patches[] = {
// 1553,
1552,
1551,
// 1550,
1550,
1549,
// 1548,
1547,
@@ -954,7 +954,7 @@ static const int included_patches[] = {
1530,
1529,
1528,
// 1527,
1527,
1526,
// 1525,
1524,
@@ -1398,7 +1398,7 @@ static const int included_patches[] = {
1086,
1085,
1084,
// 1083,
1083,
1082,
1081,
1080,

View File

@@ -2096,6 +2096,31 @@ stack traceback:
exec_lua [[vim.wait(100, function() return true end)]]
end)
it('returns all (multiple) callback results', function()
eq({ true, false }, exec_lua [[return { vim.wait(200, function() return true, false end) }]])
eq(
{ true, 'a', 42, { ok = { 'yes' } } },
exec_lua [[
local ok, rv1, rv2, rv3 = vim.wait(200, function()
return true, 'a', 42, { ok = { 'yes' } }
end)
return { ok, rv1, rv2, rv3 }
]]
)
end)
it('does not return callback results on timeout', function()
eq(
{ false, -1 },
exec_lua [[
return { vim.wait(1, function()
return false, 'a', 42, { ok = { 'yes' } }
end) }
]]
)
end)
it('waits the expected time if false', function()
eq(
{ time = true, wait_result = { false, -1 } },
@@ -2184,38 +2209,36 @@ stack traceback:
eq({ false, '[string "<nvim>"]:1: As Expected' }, { result[1], remove_trace(result[2]) })
end)
it('if callback is passed, it must be a function', function()
it('callback must be a function', function()
eq(
{ false, 'vim.wait: if passed, condition must be a function' },
exec_lua [[
return {pcall(function() vim.wait(1000, 13) end)}
]]
{ false, 'vim.wait: callback must be callable' },
exec_lua [[return {pcall(function() vim.wait(1000, 13) end)}]]
)
end)
it('allows waiting with no callback, explicit', function()
it('waits if callback arg is nil', function()
eq(
true,
exec_lua [[
local start_time = vim.uv.hrtime()
vim.wait(50, nil)
vim.wait(50, nil) -- select('#', ...) == 1
return vim.uv.hrtime() - start_time > 25000
]]
)
end)
it('allows waiting with no callback, implicit', function()
it('waits if callback arg is omitted', function()
eq(
true,
exec_lua [[
local start_time = vim.uv.hrtime()
vim.wait(50)
vim.wait(50) -- select('#', ...) == 0
return vim.uv.hrtime() - start_time > 25000
]]
)
end)
it('calls callbacks exactly once if they return true immediately', function()
it('invokes callback exactly once if it returns true immediately', function()
eq(
true,
exec_lua [[

View File

@@ -4,6 +4,7 @@ local t_lsp = require('test.functional.plugin.lsp.testutil')
local Screen = require('test.functional.ui.screen')
local dedent = t.dedent
local eq = t.eq
local api = n.api
local exec_lua = n.exec_lua
@@ -183,6 +184,59 @@ describe('vim.lsp.inline_completion', function()
feed('<Esc>')
screen:expect({ grid = grid_applied_candidates })
end)
it('accepts on_accept callback', function()
feed('i')
screen:expect({ grid = grid_with_candidates })
local result = exec_lua(function()
---@type vim.lsp.inline_completion.Item
local result
vim.lsp.inline_completion.get({
on_accept = function(item)
result = item
end,
})
vim.wait(1000, function()
return result ~= nil
end) -- Wait for async callback.
return result
end)
feed('<Esc>')
screen:expect({ grid = grid_without_candidates })
eq({
_index = 1,
client_id = 1,
command = {
command = 'dummy',
title = 'Completion Accepted',
},
insert_text = dedent([[
function fibonacci(n) {
if (n <= 0) return 0;
if (n === 1) return 1;
let a = 0, b = 1, c;
for (let i = 2; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}]]),
range = {
end_ = {
buf = 1,
col = 20,
row = 0,
},
start = {
buf = 1,
col = 0,
row = 0,
},
},
}, result)
end)
end)
describe('select()', function()

View File

@@ -262,7 +262,7 @@ describe('ui/cursor', function()
m.attr = { background = Screen.colors.DarkGray }
end
if m.id_lm then
m.id_lm = 75
m.id_lm = 76
m.attr_lm = {}
end
end

View File

@@ -3145,6 +3145,7 @@ describe('progress-message', function()
local function setup_autocmd(pattern)
exec_lua(function()
local grp = vim.api.nvim_create_augroup('ProgressListener', { clear = true })
_G.progress_autocmd_result = nil
vim.api.nvim_create_autocmd('Progress', {
pattern = pattern,
group = grp,
@@ -3171,6 +3172,7 @@ describe('progress-message', function()
screen:add_extra_attr_ids {
[100] = { undercurl = true, special = Screen.colors.Red },
[101] = { foreground = Screen.colors.Magenta1, bold = true },
[102] = { foreground = Screen.colors.NvimDarkGreen },
}
else
screen = Screen.new(40, 5)
@@ -3192,19 +3194,23 @@ describe('progress-message', function()
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
^ |
{1:~ }|*4
]],
messages = {
{
content = { { 'testsuit: test-message...10%' } },
content = {
{ 'testsuit', 6, 'MoreMsg' },
{ ': ' },
{ ' 10% ', 19, 'WarningMsg' },
{ 'test-message' },
},
history = true,
id = 1,
kind = 'progress',
},
},
})
assert_progress_autocmd({
text = { 'test-message' },
percent = 10,
@@ -3227,7 +3233,12 @@ describe('progress-message', function()
]],
messages = {
{
content = { { 'TestSuit: test-message-updated...50%' } },
content = {
{ 'TestSuit', 6, 'MoreMsg' },
{ ': ' },
{ ' 50% ', 19, 'WarningMsg' },
{ 'test-message-updated' },
},
history = true,
id = 1,
kind = 'progress',
@@ -3244,6 +3255,105 @@ describe('progress-message', function()
data = {},
}, 'Progress autocmd receives progress update')
-- success status
api.nvim_echo(
{ { 'test-message (success)' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 100, status = 'success' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 102, 'OkMsg' },
{ ': ' },
{ '100% ', 19, 'WarningMsg' },
{ 'test-message (success)' },
},
history = true,
id = 2,
kind = 'progress',
},
},
})
-- failed status
api.nvim_echo(
{ { 'test-message (success)' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 35, status = 'failed' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 9, 'ErrorMsg' },
{ ': ' },
{ ' 35% ', 19, 'WarningMsg' },
{ 'test-message (success)' },
},
history = true,
id = 3,
kind = 'progress',
},
},
})
-- cancel status
api.nvim_echo(
{ { 'test-message (success)' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'cancel' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 19, 'WarningMsg' },
{ ': ' },
{ ' 30% ', 19, 'WarningMsg' },
{ 'test-message (success)' },
},
history = true,
id = 4,
kind = 'progress',
},
},
})
-- without title and percent
api.nvim_echo(
{ { 'test-message (no-tile or percent)' } },
true,
{ kind = 'progress', status = 'cancel' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = { { 'test-message (no-tile or percent)' } },
history = true,
id = 5,
kind = 'progress',
},
},
})
-- progress event can filter by title
setup_autocmd('Special Title')
api.nvim_echo(
@@ -3284,7 +3394,12 @@ describe('progress-message', function()
]],
messages = {
{
content = { { 'TestSuit: test-message...10%' } },
content = {
{ 'TestSuit', 6, 'MoreMsg' },
{ ': ' },
{ ' 10% ', 19, 'WarningMsg' },
{ 'test-message' },
},
history = true,
id = 1,
kind = 'progress',
@@ -3373,23 +3488,29 @@ describe('progress-message', function()
true,
{ kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' }
)
eq('test-message 10', exec_capture('messages'))
eq('TestSuit: 10% test-message 10', exec_capture('messages'))
api.nvim_echo(
{ { 'test-message 20' } },
true,
{ id = id, kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' }
)
eq('test-message 10\ntest-message 20', exec_capture('messages'))
eq('TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20', exec_capture('messages'))
api.nvim_echo({ { 'middle msg' } }, true, {})
eq('test-message 10\ntest-message 20\nmiddle msg', exec_capture('messages'))
eq(
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg',
exec_capture('messages')
)
api.nvim_echo(
{ { 'test-message 30' } },
true,
{ id = id, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' }
)
eq('test-message 10\ntest-message 20\nmiddle msg\ntest-message 30', exec_capture('messages'))
eq(
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30',
exec_capture('messages')
)
api.nvim_echo(
{ { 'test-message 50' } },
@@ -3397,7 +3518,7 @@ describe('progress-message', function()
{ id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' }
)
eq(
'test-message 10\ntest-message 20\nmiddle msg\ntest-message 30\ntest-message 50',
'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30\nTestSuit: 50% test-message 50',
exec_capture('messages')
)
end)
@@ -3476,7 +3597,12 @@ describe('progress-message', function()
]],
messages = {
{
content = { { 'TestSuit: supports str-id...30%' } },
content = {
{ 'TestSuit', 6, 'MoreMsg' },
{ ': ' },
{ ' 30% ', 19, 'WarningMsg' },
{ 'supports str-id' },
},
history = true,
id = 'str-id',
kind = 'progress',
@@ -3511,7 +3637,7 @@ describe('progress-message', function()
screen:expect([[
^ |
{1:~ }|*3
TestSuit: test-message...10% |
{6:TestSuit}: {19: 10% }test-message |
]])
end)
end)

View File

@@ -192,7 +192,7 @@ func s:GetFilenameChecks() abort
\ 'conaryrecipe': ['file.recipe'],
\ 'conf': ['auto.master', 'file.conf', 'texdoc.cnf', '.x11vncrc', '.chktexrc', '.ripgreprc', 'ripgreprc', 'file.ctags'],
\ 'config': ['/etc/hostname.file', 'any/etc/hostname.file', 'configure.in', 'configure.ac', 'file.at', 'aclocal.m4'],
\ 'confini': ['pacman.conf', 'paru.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'file.nmconnection',
\ 'confini': ['pacman.conf', 'paru.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'any/.aws/cli/alias', 'file.nmconnection',
\ 'any/.gnuradio/grc.conf', 'any/gnuradio/config.conf', 'any/gnuradio/conf.d/modtool.conf'],
\ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'],
\ 'cook': ['file.cook'],
@@ -928,7 +928,7 @@ func s:GetFilenameChecks() abort
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
\ 'yaml': ['file.yaml', 'file.yml', 'file.eyaml', 'file.kyaml', 'file.kyml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock',
\ '/home/user/.kube/config', '.condarc', 'condarc', 'pixi.lock'],
\ '/home/user/.kube/config', '/home/user/.kube/kuberc', '.condarc', 'condarc', 'pixi.lock'],
\ 'yang': ['file.yang'],
\ 'yuck': ['file.yuck'],
\ 'z8a': ['file.z8a'],