Compare commits

..

12 Commits

Author SHA1 Message Date
Justin M. Keyes
3d13807b8d docs: backport (#35675)
docs: lsp, misc
2025-09-08 02:49:21 +00:00
Evgeni Chasnovski
7875a39ba0 docs: adjust *lua-plugin-init* wording #35600
Problem: Unnecessarily anecdotal wording.

Solution: Adjust wording while delivering the same message.
(cherry picked from commit 0c49167490)
2025-09-08 01:33:56 +00:00
skewb1k
3237f634fa fix(cjson): fix strbuf_set_length incorrectness #35565
`strbuf_set_length` was incorrectly updating
length with `+=` instead of assignment.

Also syncs minor updates with upstream.

(cherry picked from commit ad22d0ace9)
2025-09-07 23:17:31 +00:00
Sean Dewar
8c311386c3 fix(window): don't add a hsep when out of room if global stl is off
Problem: a horizontal separator may be added to a window that doesn't need one
if there is no room when moving a different window.

Solution: only restore a hsep in winframe_restore when the global statusline is
enabled.

(cherry picked from commit bf5f7c1591)
2025-09-03 18:03:35 +00:00
Luna Razzaghipour
4c8486e1f2 perf: scheduler priority clamping on macOS #35488
Problem:
All of Nvim’s threads are clamped to the Default QoS class. This means
that Nvim is forced to compete for CPU time with compilers and other
batch work, and is even prioritized beneath user-initiated work in GUI
apps like e.g. file imports. This significantly harms responsiveness.

Solution:
Tell the kernel that the Nvim process takes part in rendering a UI.

Implementation:
Remove the process-wide QoS clamp. This doesn’t directly do anything to
the main thread, but rather has the side-effect of letting the main
thread run at its actual QoS (User Interactive QoS).

(cherry picked from commit f9ce939bf5)
2025-09-03 02:01:50 +00:00
Mike
d31953d532 fix(diagnostics): extend conversion support from/to quickfix format (#34006)
Use uppercase to check severity
Mark quickfix items as valid when converting from diagnostics
Support nr/code attribute between formats

(cherry picked from commit e4a100a1e1)
2025-09-02 15:47:15 +00:00
Justin M. Keyes
c5262c4ca8 Merge pull request #35598 from neovim/backport-29073-to-release-0.11
docs: add guide for developing Lua plugins
2025-09-01 22:10:02 -04:00
Justin M. Keyes
dd8e3d7aa5 docs: Lua plugin development guide
(cherry picked from commit a5e7ccc329)
2025-09-01 21:56:58 -04:00
Marc Jakobi
a7491e1457 docs: Lua plugin development guide
(cherry picked from commit 28ab656122)
2025-09-01 23:53:57 +00:00
Justin M. Keyes
1063aff643 ci(release): link to release notes #35585
fix #35580
2025-09-01 11:30:51 -04:00
Wise Man
e415fae42e 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-09-01 11:17:37 -04:00
Justin M. Keyes
7b099d2b2b version bump 2025-08-31 15:29:56 -04:00
23 changed files with 461 additions and 66 deletions

View File

@@ -1,9 +1,17 @@
# This script enables Developer Command Prompt # This script enables Developer Command Prompt
# See https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-powershell # 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 ($env:BUILD_ARCH -eq "arm64") {
if ($installationPath -and (Test-Path "$installationPath\Common7\Tools\vsdevcmd.bat")) { $arch = "arm64"
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | ForEach-Object { $installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.arm64 -property installationPath
$name, $value = $_ -split '=', 2 } else {
"$name=$value" >> $env:GITHUB_ENV $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} ${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 ## Install
### Windows ### Windows
#### Zip #### Zip
1. Download **nvim-win64.zip** 1. Download **nvim-win64.zip** (or **nvim-win-arm64.zip** for ARM)
2. Extract the zip 2. Extract the zip
3. Run `nvim.exe` on your CLI of choice 3. Run `nvim.exe` in your terminal
#### MSI #### MSI
1. Download **nvim-win64.msi** 1. Download **nvim-win64.msi** (or **nvim-win-arm64.msi** for ARM)
2. Run the MSI 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). 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: windows:
needs: setup 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
# Perform a full checkout #13471 # Perform a full checkout #13471
fetch-depth: 0 fetch-depth: 0
- run: .github/scripts/env.ps1 - 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 - name: Build deps
run: | run: |
cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build .deps cmake --build .deps
- name: build package - name: Build package
run: | run: |
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build build --target package cmake --build build --target package
- uses: actions/upload-artifact@v4 - name: Upload artifact
uses: actions/upload-artifact@v4
with: with:
name: nvim-win64 name: nvim-win-${{ matrix.arch }}
path: | path: |
build/nvim-win64.msi build/${{ matrix.archive_name }}.zip
build/nvim-win64.zip build/${{ matrix.archive_name }}.msi
retention-days: 1 retention-days: 1
publish: publish:
@@ -196,10 +213,11 @@ jobs:
- name: Publish release - name: Publish release
env: env:
NVIM_VERSION: ${{ needs.linux.outputs.version }} NVIM_VERSION: ${{ needs.linux.outputs.version }}
NVIM_COMMIT: ${{ github.sha }}
DEBUG: api DEBUG: api
run: | run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md" envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
if [ "$TAG_NAME" != "nightly" ]; then 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 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

@@ -141,8 +141,8 @@ endif()
# version string, else they are combined with the result of `git describe`. # version string, else they are combined with the result of `git describe`.
set(NVIM_VERSION_MAJOR 0) set(NVIM_VERSION_MAJOR 0)
set(NVIM_VERSION_MINOR 11) set(NVIM_VERSION_MINOR 11)
set(NVIM_VERSION_PATCH 4) set(NVIM_VERSION_PATCH 5)
set(NVIM_VERSION_PRERELEASE "") # for package maintainers set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level # API level
set(NVIM_API_LEVEL 13) # Bump this after any API/stdlib change. set(NVIM_API_LEVEL 13) # Bump this after any API/stdlib change.

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

View File

@@ -1319,9 +1319,9 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
debugging and orchestration. (Note: Something is better than nothing! debugging and orchestration. (Note: Something is better than nothing!
Fields are optional, but at least set `name`.) Fields are optional, but at least set `name`.)
Can be called more than once; the caller should merge old info if Can be called more than once; caller should merge old info if appropriate.
appropriate. Example: library first identifies the channel, then a plugin Example: a library first identifies the channel, then a plugin using that
using that library later identifies itself. library later identifies itself.
Attributes: ~ Attributes: ~
|RPC| only |RPC| only

View File

@@ -1143,23 +1143,23 @@ first word). You can use the "']" or "`]" command after the put command to
move the cursor to the end of the inserted text, or use "'[" or "`[" to move move the cursor to the end of the inserted text, or use "'[" or "`[" to move
the cursor to the start. the cursor to the start.
*put-Visual-mode* *v_p* *v_P* *put-Visual-mode*
When using a put command like |p| or |P| in Visual mode, Vim will try to When using a put command like |p| or |P| in Visual mode, Vim will try to
replace the selected text with the contents of the register. Whether this replace the selected text with the contents of the register. How this
works well depends on the type of selection and the type of the text in the works depends on the type of selection and the text. With blockwise selection
register. With blockwise selection it also depends on the size of the block it also depends on the size of the block and whether the corners are on an
and whether the corners are on an existing character. (Implementation detail: existing character. (Implementation detail: it actually works by first
it actually works by first putting the register after the selection and then putting the register after the selection and then deleting the selection.)
deleting the selection.) *v_p*
With |p| the previously selected text is put in the unnamed register (and |p| in Visual mode puts text and sets the default register (unnamed,
possibly the selection and/or clipboard). This is useful if you want to put selection, or clipboard) to the previously-selected text. Useful if you want
that text somewhere else. But you cannot repeat the same change. to put that text somewhere else. But you cannot repeat the same change.
With |P| the unnamed register is not changed (and neither the selection or *v_P*
clipboard), you can repeat the same change. But the deleted text cannot be |P| in Visual mode puts text without setting the default register. You can
used. If you do need it you can use |p| with another register. E.g., yank repeat the change, but the deleted text cannot be used. If you do need it you
the text to copy, Visually select the text to replace and use "0p . You can can use |p| with another register. E.g., yank the text to copy, Visually
repeat this as many times as you like, and the unnamed register will be select the text to replace and use "0p . You can repeat this as many times as
changed each time. you like, and the unnamed register will be changed each time.
*blockwise-put* *blockwise-put*
When a register contains text from one line (characterwise), using a When a register contains text from one line (characterwise), using a
blockwise Visual selection, putting that register will paste that text blockwise Visual selection, putting that register will paste that text

View File

@@ -446,6 +446,8 @@ Use existing common {verb} names (actions) if possible:
- is_enabled: Checks if functionality is enabled. - is_enabled: Checks if functionality is enabled.
- open: Opens something (a buffer, window, …) - open: Opens something (a buffer, window, …)
- parse: Parses something into a structured form - parse: Parses something into a structured form
- request: Calls a remote (network, RPC) operation.
- send: Writes data or a message to a channel.
- set: Sets a thing (or group of things) - set: Sets a thing (or group of things)
- start: Spin up a long-lived process. Prefer "enable" except when - start: Spin up a long-lived process. Prefer "enable" except when
"start" is obviously more appropriate. "start" is obviously more appropriate.
@@ -456,7 +458,7 @@ Do NOT use these deprecated verbs:
- contains: Prefer "has". - contains: Prefer "has".
- disable: Prefer `enable(enable: boolean)`. - disable: Prefer `enable(enable: boolean)`.
- exit: Prefer "cancel" (or "stop" if appropriate). - exit: Prefer "cancel" (or "stop" if appropriate).
- is_disabled: Prefer `is_enabled()`. - is_disabled: Prefer `!is_enabled()`.
- list: Redundant with "get" - list: Redundant with "get"
- notify: Redundant with "print", "echo" - notify: Redundant with "print", "echo"
- show: Redundant with "print", "echo" - show: Redundant with "print", "echo"

View File

@@ -10,19 +10,18 @@
============================================================================== ==============================================================================
Introduction *lua-guide* Introduction *lua-guide*
This guide will go through the basics of using Lua in Nvim. It is not meant This guide introduces the basics of everyday Lua usage for configuring and
to be a comprehensive encyclopedia of all available features, nor will it controlling Nvim. It assumes some familiarity with the (non-Lua) basics of
detail all intricacies. Think of it as a survival kit -- the bare minimum Nvim (commands, options, mappings, autocommands), which are covered in the
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
(commands, options, mappings, autocommands), which are covered in the
|user-manual|. |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* Some words on the API *lua-guide-api*

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

@@ -0,0 +1,316 @@
*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 manifest or "registration" step.
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`, or 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)
vim.keymap.set('v', '<Plug>(SayHello)', function()
print('Hello from visual mode')
end)
<
In the user's config:
>lua
vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
<
==============================================================================
Initialization *lua-plugin-init*
Strictly separated configuration and smart initialization allow your plugin to
work out of the box. Common approaches 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'|.
On the other hand, a single `setup(opts)` that combines configuration and
initialization may be useful in specific cases:
- Customizing complex initialization, where there is a significant risk of
misconfiguration.
- Requiring users to opt in for plugin functionality that should not be
initialized automatically.
Keep in mind that this approach requires users to call `setup` in order to
use your plugin, even if the default configuration is enough for them.
Consider carefully whether your plugin benefits from combined `setup()` pattern
before adopting it.
NOTE: A well designed plugin has minimal impact on startup time. See also
|lua-plugin-lazy|.
==============================================================================
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, { 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*
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 discouraged 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|. The
help-tags (the right-aligned "search keywords" in the help documents) are
regenerated using the |:helptags| command.
DOCUMENTATION TOOLS
- panvimdoc https://github.com/kdheepak/panvimdoc
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:

View File

@@ -2620,7 +2620,9 @@ function M.toqflist(diagnostics)
end_lnum = v.end_lnum and (v.end_lnum + 1) or nil, end_lnum = v.end_lnum and (v.end_lnum + 1) or nil,
end_col = v.end_col and (v.end_col + 1) or nil, end_col = v.end_col and (v.end_col + 1) or nil,
text = v.message, text = v.message,
nr = tonumber(v.code),
type = errlist_type_map[v.severity] or 'E', type = errlist_type_map[v.severity] or 'E',
valid = 1,
} }
table.insert(list, item) table.insert(list, item)
end end
@@ -2652,7 +2654,8 @@ function M.fromqflist(list)
local col = math.max(0, item.col - 1) local col = math.max(0, item.col - 1)
local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum
local end_col = item.end_col > 0 and (item.end_col - 1) or col local end_col = item.end_col > 0 and (item.end_col - 1) or col
local severity = item.type ~= '' and M.severity[item.type] or M.severity.ERROR local code = item.nr > 0 and item.nr or nil
local severity = item.type ~= '' and M.severity[item.type:upper()] or M.severity.ERROR
diagnostics[#diagnostics + 1] = { diagnostics[#diagnostics + 1] = {
bufnr = item.bufnr, bufnr = item.bufnr,
lnum = lnum, lnum = lnum,
@@ -2661,6 +2664,7 @@ function M.fromqflist(list)
end_col = end_col, end_col = end_col,
severity = severity, severity = severity,
message = item.text, message = item.text,
code = code,
} }
end end
end end

View File

@@ -4,8 +4,8 @@
# changelog header # changelog header
header = """ header = """
# Changelog\n # Changelog\n
For notable changes, see runtime/doc/news.txt (or `:help news` in Nvim).\n Following is a list of commits (fixes/features only) in this release.\n
Following is a list of fixes/features commits.\n See `:help news` in Nvim for release notes.\n
""" """
# template for the changelog body # template for the changelog body
# https://github.com/Keats/tera # https://github.com/Keats/tera

View File

@@ -97,5 +97,5 @@ echo "
Next steps: Next steps:
- Run tests/CI (version_spec.lua)! - Run tests/CI (version_spec.lua)!
- Push the tag: - Push the tag:
git push --follow-tags git push v${__VERSION}
- Update website: index.html" - Update website: index.html"

2
src/cjson/fpconv.c vendored
View File

@@ -130,7 +130,7 @@ double fpconv_strtod(const char *nptr, char **endptr)
/* Duplicate number into buffer */ /* Duplicate number into buffer */
if (buflen >= FPCONV_G_FMT_BUFSIZE) { if (buflen >= FPCONV_G_FMT_BUFSIZE) {
/* Handle unusually large numbers */ /* Handle unusually large numbers */
buf = malloc(buflen + 1); buf = (char *)malloc(buflen + 1);
if (!buf) { if (!buf) {
fprintf(stderr, "Out of memory"); fprintf(stderr, "Out of memory");
abort(); abort();

7
src/cjson/strbuf.c vendored
View File

@@ -39,7 +39,7 @@ static void die(const char *fmt, ...)
va_end(arg); va_end(arg);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
exit(-1); abort();
} }
void strbuf_init(strbuf_t *s, size_t len) void strbuf_init(strbuf_t *s, size_t len)
@@ -51,7 +51,7 @@ void strbuf_init(strbuf_t *s, size_t len)
else else
size = len + 1; /* \0 terminator */ size = len + 1; /* \0 terminator */
if (size < len) if (size < len)
die("Overflow, len %zu", len); die("Overflow, len: %zu", len);
s->buf = NULL; s->buf = NULL;
s->size = size; s->size = size;
s->length = 0; s->length = 0;
@@ -132,7 +132,7 @@ static size_t calculate_new_size(strbuf_t *s, size_t len)
/* Ensure there is room for optional NULL termination */ /* Ensure there is room for optional NULL termination */
reqsize = len + 1; reqsize = len + 1;
if (reqsize < len) if (reqsize < len)
die("Overflow, len %zu", len); die("Overflow, len: %zu", len);
/* If the user has requested to shrink the buffer, do it exactly */ /* If the user has requested to shrink the buffer, do it exactly */
if (s->size > reqsize) if (s->size > reqsize)
@@ -194,5 +194,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
} }
} }
/* vi:ai et sw=4 ts=4: /* vi:ai et sw=4 ts=4:
*/ */

2
src/cjson/strbuf.h vendored
View File

@@ -103,7 +103,7 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
static inline void strbuf_set_length(strbuf_t *s, int len) static inline void strbuf_set_length(strbuf_t *s, int len)
{ {
s->length += len; s->length = len;
} }
static inline void strbuf_extend_length(strbuf_t *s, size_t len) static inline void strbuf_extend_length(strbuf_t *s, size_t len)

View File

@@ -72,6 +72,8 @@ local new_layout = {
['gui.txt'] = true, ['gui.txt'] = true,
['intro.txt'] = true, ['intro.txt'] = true,
['lua.txt'] = true, ['lua.txt'] = true,
['lua-guide.txt'] = true,
['lua-plugin.txt'] = true,
['luaref.txt'] = true, ['luaref.txt'] = true,
['news.txt'] = true, ['news.txt'] = true,
['news-0.9.txt'] = true, ['news-0.9.txt'] = true,

View File

@@ -409,13 +409,18 @@ name, etc.
All the global variables are declared in `globals.h`. All the global variables are declared in `globals.h`.
### The main loop ### The main event-loop
The main loop is implemented in state_enter. The basic idea is that Vim waits The main loop is implemented in state_enter. The basic idea is that Vim waits
for the user to type a character and processes it until another character is for the user to type a character and processes it until another character is
needed. Thus there are several places where Vim waits for a character to be needed. Thus there are several places where Vim waits for a character to be
typed. The `vgetc()` function is used for this. It also handles mapping. typed. The `vgetc()` function is used for this. It also handles mapping.
What we consider the "Nvim event loop" is actually a wrapper around `uv_run` to
handle both the `fast_events` queue and possibly (a suitable subset of) deferred
events. Therefore "raw" `vim.uv.run()` is often not enough to "yield" from Lua
plugins; instead they can call `vim.wait(0)`.
Updating the screen is mostly postponed until a command or a sequence of Updating the screen is mostly postponed until a command or a sequence of
commands has finished. The work is done by `update_screen()`, which calls commands has finished. The work is done by `update_screen()`, which calls
`win_update()` for every window, which calls `win_line()` for every line. `win_update()` for every window, which calls `win_line()` for every line.

View File

@@ -1489,7 +1489,7 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set /// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
/// `name`.) /// `name`.)
/// ///
/// Can be called more than once; the caller should merge old info if appropriate. Example: library /// Can be called more than once; caller should merge old info if appropriate. Example: a library
/// first identifies the channel, then a plugin using that library later identifies itself. /// first identifies the channel, then a plugin using that library later identifies itself.
/// ///
/// @param channel_id /// @param channel_id

View File

@@ -190,6 +190,7 @@ static bool event_teardown(void)
/// Needed for unit tests. /// Needed for unit tests.
void early_init(mparm_T *paramp) void early_init(mparm_T *paramp)
{ {
os_hint_priority();
estack_init(); estack_init();
cmdline_init(); cmdline_init();
eval_init(); // init global variables eval_init(); // init global variables

View File

@@ -39,6 +39,10 @@
# include "nvim/fileio.h" # include "nvim/fileio.h"
#endif #endif
#ifdef __APPLE__
# include <mach/task.h>
#endif
#ifdef HAVE__NSGETENVIRON #ifdef HAVE__NSGETENVIRON
# include <crt_externs.h> # include <crt_externs.h>
#endif #endif
@@ -352,6 +356,18 @@ int64_t os_get_pid(void)
#endif #endif
} }
/// Signals to the OS that Nvim is an application for "interactive work"
/// which should be prioritized similar to a GUI app.
void os_hint_priority(void)
{
#ifdef __APPLE__
// By default, processes have the TASK_UNSPECIFIED "role", which means all of its threads are
// clamped to Default QoS. Setting the role to TASK_DEFAULT_APPLICATION removes this clamp.
integer_t policy = TASK_DEFAULT_APPLICATION;
task_policy_set(mach_task_self(), TASK_CATEGORY_POLICY, &policy, 1);
#endif
}
/// Gets the hostname of the current machine. /// Gets the hostname of the current machine.
/// ///
/// @param hostname Buffer to store the hostname. /// @param hostname Buffer to store the hostname.

View File

@@ -3359,7 +3359,7 @@ void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr)
if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
if (global_stl_height() == 0 && wp->w_status_height == 0) { if (global_stl_height() == 0 && wp->w_status_height == 0) {
frame_add_statusline(frp->fr_prev); frame_add_statusline(frp->fr_prev);
} else if (wp->w_hsep_height == 0) { } else if (global_stl_height() > 0 && wp->w_hsep_height == 0) {
frame_add_hsep(frp->fr_prev); frame_add_hsep(frp->fr_prev);
} }
} }

View File

@@ -504,6 +504,20 @@ describe('global statusline', function()
{3:[No Name] 0,0-1 All}| {3:[No Name] 0,0-1 All}|
| |
]]) ]])
-- Shouldn't gain a hsep if the global statusline is turned off.
command('set laststatus=2')
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L'))
command('mode')
screen:expect([[
|
{1:~ }|*5
{2:[No Name] 0,0-1 All}|
^ |
{1:~ }|*6
{3:[No Name] 0,0-1 All}|
|
]])
end) end)
end) end)