Compare commits

...

37 Commits

Author SHA1 Message Date
zeertzjq
0bafbe872b vim-patch:9.1.1732: filetype: .inc file detection can be improved (#35635)
Problem:  filetype: .inc file detection can be improved
Solution: Update filetype detection for Pascal and BitBake code
          (Martin Schwan).

Fix the detection of .inc files containing Pascal and BitBake code:

- the concatenated string, merged from three lines, only contains one
  beginning and the pattern "^" would not match as expected. Use a range()
  loop to iterate each line string individually. This way, the pattern "^"
  works for beginning of lines.

- improve BitBake include file detection by also matching forward-slashes
  "/" in variable names and assignment operators with a dot ".=" and "=.".
  Valid examples, which should match, are:

    PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"
    MACHINEOVERRIDES =. "qemuall:"
    BBPATH .= ":${LAYERDIR}"

- parse twenty instead of just three lines, to accommodate for potential
  comments at the beginning of files

closes: vim/vim#18202

9fd1a657d2

Co-authored-by: Martin Schwan <m.schwan@phytec.de>
2025-09-05 17:22:24 +08:00
Justin M. Keyes
9898327bd7 Merge #35621 from fredizzimo/refactor-mouse-spec
refactor(tests): don't compare the full screen in mouse_spec
2025-09-05 00:40:13 -04:00
zeertzjq
0bf4b7898c vim-patch:9.1.1731: Not using const qualifier for opchars (#35634)
Problem:  Not using const qualifier
Solution: Mark the opchars array const

closes: vim/vim#18196

63a02ca39a

Co-authored-by: Damien Lejay <damien@lejay.be>
2025-09-05 00:20:28 +00:00
Justin M. Keyes
7fcdb0541f Merge #35628 fix(progress): msg-id, memoryleak 2025-09-04 17:38:52 -04:00
Fred Sundvik
e6cdb76665 refactor(tests): don't compare the full screen in mouse_spec
Problem:
Adding multigrid tests to `mouse_spec` requires all tests to test both
the multigrid and non-multigrid screen state, which adds a lot of extra
code.

Solution:
Instead of testing the full screen state, only test substrings of it.
2025-09-05 00:12:55 +03:00
shadmansaleh
e5ba875a95 fix(progress): memory leak on progress-message with history=false 2025-09-04 21:53:49 +06:00
shadmansaleh
2415d8f424 fix(progress): message-id not sent to ui with history set to false 2025-09-04 20:37:05 +06:00
Justin M. Keyes
2b421d518f Merge #35462 from justinmk/doc2 2025-09-04 00:30:28 -04:00
Justin M. Keyes
2affb8373f docs: api events 2025-09-04 00:11:52 -04:00
Justin M. Keyes
9c3099f0cf docs: lsp, misc
- Problem: It's not clear for new plugin developers that `:help` uses
  a help-tags file for searching the docs, generated by `:helptags`.
  - Solution: Hint to the |:helptags| docs for regenerating the tags
    file for their freshly written documentation.

Co-authored-by: Yochem van Rosmalen <git@yochem.nl>
2025-09-03 23:03:51 -04:00
luukvbaal
de950f8272 fix(window): avoid referencing cmdline_win after it is closed #35617
Solution:  A window marked for ext_cmdline usage is still referenced
           after it is already closed and freed.
Solution:  Unset cmdline_win when the window is closed.
2025-09-03 16:28:23 -07:00
zeertzjq
17da1ad8f4 vim-patch:9.1.1730: filetype: vivado journal/log files are not recognized (#35613)
Problem:  filetype: vivado journal/log files are not recognized
Solution: Detect vivado*.{jou,log} as tcl filetype (Wu Zhenyu).

closes: vim/vim#18191

9a6cafdc1c

Also fix missing anchor in the pattern from the port of patch 9.1.1602.

Co-authored-by: Wu, Zhenyu <wuzhenyu@ustc.edu>
2025-09-04 07:16:47 +08:00
Fred Sundvik
f2536aa795 test: make it possible to test multiple screen string matches
Problem:
Currently it's only possible to test a single string match in the
screen, which makes it hard match multiple strings in the screen to
avoid having to compare the whole screen state.

It's also not possible to test if a string match is not found in the screen.

Solution:
Support an array of `any` matches and also support `none`, which does a
negative comparision.
2025-09-03 21:35:21 +03:00
Sean Dewar
bf5f7c1591 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.
2025-09-03 18:34:37 +01:00
zeertzjq
1f7d6c3876 Merge pull request #35612 from zeertzjq/vim-e0704a3
vim-patch: runtime file updates
2025-09-03 22:40:10 +08:00
zeertzjq
967d226f96 vim-patch:f165798: runtime(m4): update syntax script
This change does the following to the M4 syntax script:

- In M4 there are no "strings" in the usual sense. Instead, M4 has
  quotes, but the text inside a quoted region is rescanned just like
  outside, and quotes can be nested.
- The old m4String region was misleading and removed. A new m4Quoted
  region reflects proper quoting semantics.
- Removed a duplicate highlight rule.
- Fixed a typo in a highlight group name (m4builtin → m4Builtin).
- Added a reference link to the POSIX M4 specification.
- Removed outdated maintainer URL.

closes: vim/vim#18192

f165798184

Co-authored-by: Damien Lejay <damien@lejay.be>
2025-09-03 21:28:11 +08:00
zeertzjq
c4c9daf7e2 vim-patch:e0704a3: runtime(keymap): Add transliteration (buckwalter) arabic keymap
References:
- https://en.wikipedia.org/wiki/Buckwalter_transliteration
- http://qamus.org/transliteration.htm

closes: vim/vim#18186

e0704a3593

Co-authored-by: Rafael Ketsetsides <rketsetsides@gmail.com>
2025-09-03 21:27:24 +08:00
Luna Razzaghipour
f9ce939bf5 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).
2025-09-02 18:34:46 -07:00
zeertzjq
1ae09bf545 vim-patch:4c39d0c: runtime(doc): quote partial urls with a backtick (#35606)
closes: vim/vim#18194

4c39d0cc9b

Co-authored-by: Yochem van Rosmalen <git@yochem.nl>
2025-09-03 09:07:01 +08:00
Evgeni Chasnovski
431004dda2 fix: screenchar()/screenstring() with hidden floating windows #35560 2025-09-02 11:21:19 -07:00
Shadman
79bfeecdb4 feat(editor)!: insert-mode ctrl-r should work like paste #35477
Problem:
insert-mode ctrl-r input is treated like raw user input, which is almost
never useful. This means any newlines in the input are affected by
autoindent, etc., which is:
- slow
- usually breaks the formatting of the input

Solution:
- ctrl-r should be treated like a paste, not user-input.
- does not affect `<c-r>=`, so `<c-r>=@x` can still be used to get the
  old behavior.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2025-09-01 21:05:16 -07: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
github-actions[bot]
c6bfc203f1 docs: update version.c #35484
vim-patch:8.1.1083: MS-Windows: hang when opening a file on network share
vim-patch:8.1.1527: when moving popup window over the cmdline it is not redrawn
vim-patch:8.1.1550: when a popup has left padding text may be cut off
vim-patch:8.1.1562: popup window not always redrawn after popup_setoptions()
vim-patch:8.1.1575: callbacks may be garbage collected
vim-patch:8.1.1596: when resizing the screen may draw popup in wrong position
vim-patch:8.1.1602: popup window cannot overflow on the left or right
vim-patch:8.1.1615: crash when passing buffer number to popup_create()
vim-patch:8.1.1617: no test for popup window with mask and position fixed
vim-patch:8.1.1620: no test for popup window with border and mask
vim-patch:8.1.1622: wrong width if displaying a lot of lines in a popup window
vim-patch:8.1.1636: crash when popup has fitting scrollbar
vim-patch:8.1.1646: build failure
vim-patch:8.1.1649: Illegal memory access when closing popup window
vim-patch:8.1.1656: popup window width is wrong when using Tabs
vim-patch:8.1.1665: crash when popup window with mask is below the screen
vim-patch:8.1.1666: click in popup window scrollbar with border doesn't scroll
vim-patch:8.1.1676: "maxwidth" of popup window does not always work properly
vim-patch:8.1.1678: using popup_menu() does not scroll to show the selected line
vim-patch:8.1.1707: Coverity warns for possibly using a NULL pointer
vim-patch:8.1.1709: Coverity warns for possibly using a NULL pointer
vim-patch:8.1.1719: popup too wide when 'showbreak' is set
vim-patch:8.1.1733: the man ftplugin leaves an empty buffer behind
vim-patch:8.1.1753: use of popup window mask is inefficient
vim-patch:8.1.1754: build failure
vim-patch:8.1.1755: leaking memory when using a popup window mask
vim-patch:8.1.1768: man plugin changes setting in current window
vim-patch:8.1.1773: the preview popup window may be too far to the right
vim-patch:8.1.1778: not showing the popup window right border is confusing
vim-patch:8.1.1779: not showing the popup window right border is confusing
vim-patch:8.1.1786: double click in popup scrollbar starts selection
vim-patch:8.1.1789: cannot see file name of preview popup window
vim-patch:8.1.1814: a long title in a popup window overflows
vim-patch:8.1.1845: may use NULL pointer when running out of memory
vim-patch:8.1.1850: focus may remain in popup window
vim-patch:8.1.1874: modeless selection in popup window overlaps scrollbar
vim-patch:8.1.1902: cannot have an info popup without a border
vim-patch:8.1.1907: wrong position for info popup with scrollbar on the left
vim-patch:8.1.1917: non-current window is not redrawn when moving popup
vim-patch:8.1.1918: redrawing popups is inefficient
vim-patch:8.1.1929: no tests for text property popup window
vim-patch:8.1.1934: not enough tests for text property popup window
vim-patch:8.1.1936: not enough tests for text property popup window
vim-patch:8.1.1945: popup window "firstline" cannot be reset
vim-patch:8.1.1959: when using "firstline" in popup window text may jump
vim-patch:8.1.1963: popup window filter may be called recursively
vim-patch:8.1.1997: no redraw after a popup window filter is invoked
vim-patch:8.1.1998: redraw even when no popup window filter was invoked
vim-patch:8.1.2009: cursorline highlighting not updated in popup window
vim-patch:8.1.2032: scrollbar thumb wrong in popup window
vim-patch:8.1.2109: popup_getoptions() hangs with tab-local popup
vim-patch:8.1.2110: CTRL-C closes two popups instead of one
vim-patch:8.1.2114: when a popup is closed with CTRL-C the callback aborts
vim-patch:8.1.2164: stuck when using "j" in a popupwin with popup_filter_menu
vim-patch:8.1.2210: using negative offset for popup_create() does not work
vim-patch:8.1.2213: popup_textprop tests fail
vim-patch:8.1.2240: popup window width changes when scrolling
vim-patch:8.1.2277: terminal window is not updated when info popup changes
vim-patch:8.1.2286: using border highlight in popup window leaks memory
vim-patch:8.1.2287: using EndOfBuffer highlight in popup does not look good
vim-patch:8.1.2288: not using all space when popup with "topleft" flips to above
vim-patch:8.1.2300: redraw breaks going through list of popup windows
vim-patch:8.1.2307: positioning popup doesn't work for buffer-local textprop
vim-patch:8.1.2334: possible NULL pointer dereference in popup_locate()
vim-patch:8.1.2420: crash when calling popup_close() in win_execute()

vim-patch:8.2.0826: Vim9: crash in :defcompile
vim-patch:8.2.1207: Vim9: crash in expr test when run in the GUI
vim-patch:8.2.2018: Vim9: script variable not found from lambda

vim-patch:9.0.0133: virtual text after line moves to joined line
2025-08-31 19:02:30 -07:00
Riley Bruins
77e3efecee feat(lsp): support textDocument/onTypeFormatting (#34637)
Implements [on-type
formatting](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_onTypeFormatting)
using a `vim.on_key()` approach to listen to typed keys. It will listen
to keys on the *left hand side* of mappings. The `on_key` callback is
cleared when detaching the last on-type formatting client. This feature
is disabled by default.

Co-authored-by: Maria José Solano <majosolano99@gmail.com>
2025-08-31 14:09:12 -07:00
Michael Henry
f311c96973 fix(health): update advice for Python #35564
Problem: `:checkhealth` advice for Python is out-of-date.

Solution: Update the advice to point to `:help provider-python`.
2025-08-31 11:17:21 -07:00
77 changed files with 3076 additions and 1453 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

@@ -826,7 +826,8 @@ message window. Cmdline state is emitted as |ui-cmdline| events, which the UI
must handle.
["msg_show", kind, content, replace_last, history, append, msg_id] ~
Display a message to the user.
Display a message to the user. Update (replace) any existing message
matching `msg_id`.
kind
Name indicating the message kind:

View File

@@ -254,19 +254,6 @@ The idea is "versionless evolution", in the words of Rich Hickey:
- Relaxing a requirement should be a compatible change.
- Strengthening a promise should be a compatible change.
==============================================================================
Global events *api-global-events*
When a client invokes an API request as an async notification, it is not
possible for Nvim to send an error response. Instead, in case of error, the
following notification will be sent to the client:
*nvim_error_event*
nvim_error_event[{type}, {message}]
{type} is a numeric id as defined by `api_info().error_types`, and {message} is
a string with the error message.
==============================================================================
Buffer update events *api-buffer-updates*
@@ -553,6 +540,38 @@ Extmark positions changed by an edit will be restored on undo/redo. Creating
and deleting extmarks is not a buffer change, thus new undo states are not
created for extmark changes.
==============================================================================
Global Events *api-events*
nvim_error_event({type}, {msg}) *nvim_error_event*
Emitted on the client channel if an async API request responds with an
error.
Attributes: ~
|RPC| only
Parameters: ~
• {type} (`integer`) Error type id as defined by
`api_info().error_types`.
• {msg} (`string`) Error message.
nvim_ui_term_event({event}, {value}) *nvim_ui_term_event*
Emitted by the TUI client to signal when a host-terminal event occurred.
Supports these events:
• "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response
sequence to Nvim. The payload is the received response. Sets
|v:termresponse| and fires |TermResponse|.
Attributes: ~
|RPC| only
Since: 0.10.0
Parameters: ~
• {event} (`string`) Event name
• {value} (`any`) Event payload
==============================================================================
Global Functions *api-global*
@@ -1385,9 +1404,9 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
debugging and orchestration. (Note: Something is better than nothing!
Fields are optional, but at least set `name`.)
Can be called more than once; the caller should merge old info if
appropriate. Example: library first identifies the channel, then a plugin
using that library later identifies itself.
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.
Attributes: ~
|RPC| only
@@ -3324,8 +3343,7 @@ nvim_set_decoration_provider({ns_id}, {opts})
• on_win: called when starting to redraw a specific window. >
["win", winid, bufnr, toprow, botrow]
<
• on_line: called for each buffer line being redrawn. (The
interaction with fold lines is subject to change) >
• on_line: (deprecated, use on_range instead) >
["line", winid, bufnr, row]
<
• on_range: called for each buffer range being redrawn. Range
@@ -3661,22 +3679,6 @@ nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
• {name} (`string`)
• {value} (`any`)
nvim_ui_term_event({event}, {value}) *nvim_ui_term_event()*
Tells Nvim when a host-terminal event occurred.
Supports these events:
• "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response
sequence to Nvim. The payload is the received response. Sets
|v:termresponse| and fires |TermResponse|.
Attributes: ~
|RPC| only
Since: 0.10.0
Parameters: ~
• {event} (`string`) Event name
• {value} (`any`) Event payload
nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
Attributes: ~

View File

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

View File

@@ -142,13 +142,16 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>*
typing CTRL-R and the second character '"' will be displayed
to indicate that you are expected to enter the name of a
register.
The text is inserted as if you typed it, but mappings and
abbreviations are not used. Command-line completion through
'wildchar' is not triggered though. And characters that end
the command line are inserted literally (<Esc>, <CR>, <NL>,
<C-C>). A <BS> or CTRL-W could still end the command line
though, and remaining characters will then be interpreted in
another mode, which might not be what you intended.
When used with named or clipboard registers (A-Z,a-z,0-9,+)
text is inserted literally like pasting with "p". For other
registers, the text is inserted as if you typed it, but
mappings and abbreviations are not used. Command-line
completion through 'wildchar' is not triggered though. And
characters that end the command line are inserted literally
(<Esc>, <CR>, <NL>, <C-C>). A <BS> or CTRL-W could still end
the command line though, and remaining characters will then be
interpreted in another mode, which might not be what you
intended.
Special registers:
'"' the unnamed register, containing the text of
the last delete or yank
@@ -176,7 +179,9 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>*
sure the expression evaluates to an empty
string. E.g.: >
<C-R><C-R>=setcmdpos(2)[-1]<CR>
< See |registers| about registers.
< You can use this to insert a register as
typed with CTRL-R =@reg.
See |registers| about registers.
Implementation detail: When using the |expression| register
and invoking setcmdpos(), this sets the position before
inserting the resulting string. Use CTRL-R CTRL-R to set the

View File

@@ -451,6 +451,8 @@ Use existing common {verb} names (actions) if possible:
a handler. |dev-name-events|
- open: Opens something (a buffer, window, …)
- 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)
- start: Spin up a long-lived process. Prefer "enable" except when
"start" is obviously more appropriate.
@@ -461,7 +463,7 @@ Do NOT use these deprecated verbs:
- contains: Prefer "has".
- disable: Prefer `enable(enable: boolean)`.
- exit: Prefer "cancel" (or "stop" if appropriate).
- is_disabled: Prefer `is_enabled()`.
- is_disabled: Prefer `!is_enabled()`.
- list: Redundant with "get"
- notify: Redundant with "print", "echo"
- show: Redundant with "print", "echo"

View File

@@ -101,13 +101,15 @@ CTRL-N Find next keyword (see |i_CTRL-N|).
CTRL-P Find previous keyword (see |i_CTRL-P|).
CTRL-R {register} *i_CTRL-R*
Insert the contents of a register. Between typing CTRL-R and
Insert the contents of a register. Between typing CTRL-R and
the second character, '"' will be displayed to indicate that
you are expected to enter the name of a register.
The text is inserted as if you typed it, but mappings and
you are expected to enter the name of a register. When used
with named or clipboard registers (A-Z,a-z,0-9,+) text is
inserted literally like pasting with "p". For other registers,
the text is inserted as if you typed it, but mappings and
abbreviations are not used. If you have options like
'textwidth', 'formatoptions', or 'autoindent' set, this will
influence what will be inserted. This is different from what
influence what will be inserted. This is different from what
happens with the "p" command and pasting with the mouse.
Special registers:
'"' the unnamed register, containing the text of
@@ -131,6 +133,8 @@ CTRL-R {register} *i_CTRL-R*
special keys. E.g., you can use this to move
the cursor up:
CTRL-R ="\<Up>"
you can use this to insert a register as
typed with CTRL-R =@reg.
Use CTRL-R CTRL-R to insert text literally.
When the result is a |List| the items are used
as lines. They can have line breaks inside

View File

@@ -2229,6 +2229,47 @@ is_enabled({filter}) *vim.lsp.inlay_hint.is_enabled()*
==============================================================================
Lua module: vim.lsp.inline_completion *lsp-inline_completion*
This module provides the LSP "inline completion" feature, for completing
multiline text (e.g., whole methods) instead of just a word or line, which may
result in "syntactically or semantically incorrect" code. Unlike regular
completion, this is typically presented as overlay text instead of a menu of
completion candidates.
LSP spec:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
To try it out, here is a quickstart example using Copilot: *lsp-copilot*
1. Install Copilot: >sh
npm install --global @github/copilot-language-server
<
2. Define a config, (or copy `lsp/copilot.lua` from
https://github.com/neovim/nvim-lspconfig): >lua
vim.lsp.config('copilot', {
cmd = { 'copilot-language-server', '--stdio', },
root_markers = { '.git' },
})
<
3. Activate the config: >lua
vim.lsp.enable('copilot')
<
4. Sign in to Copilot, or use the `:LspCopilotSignIn` command from
https://github.com/neovim/nvim-lspconfig
5. Enable inline completion: >lua
vim.lsp.inline_completion.enable()
<
6. Set a keymap for `vim.lsp.inline_completion.get()` and invoke the keymap.
*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 +2287,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 +2306,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`.
@@ -2377,6 +2422,33 @@ set_level({level}) *vim.lsp.log.set_level()*
• {level} (`string|integer`) One of |vim.log.levels|
==============================================================================
Lua module: vim.lsp.on_type_formatting *lsp-on_type_formatting*
enable({enable}, {filter}) *vim.lsp.on_type_formatting.enable()*
Enables/disables on-type formatting globally or for the {filter}ed scope.
The following are some practical usage examples: >lua
-- Enable for all clients
vim.lsp.on_type_formatting.enable()
-- Enable for a specific client
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client_id = args.data.client_id
local client = assert(vim.lsp.get_client_by_id(client_id))
if client.name == 'rust-analyzer' then
vim.lsp.on_type_formatting.enable(true, { client_id = client_id })
end
end,
})
<
Parameters: ~
• {enable} (`boolean?`) true/nil to enable, false to disable.
• {filter} (`table?`) Optional filters |kwargs|:
• {client_id} (`integer?`) Client ID, or `nil` for all.
==============================================================================
Lua module: vim.lsp.rpc *lsp-rpc*

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
(commands, options, mappings, autocommands), which are covered in the
This guide introduces the basics of everyday Lua usage for configuring and
controlling 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*

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

@@ -0,0 +1,313 @@
*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*
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, { 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 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

@@ -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

@@ -848,25 +848,32 @@ prompt.
==============================================================================
4. PROGRESS MESSAGE *progress-message*
Nvim can emit progress-message, which are a special kind of |ui-messages|
used to report the state of long-running tasks.
Plugins and core Nvim features can emit "progress" |ui-messages| to report the
state of long-running tasks.
Progress messages are created or updated using |nvim_echo()| with `kind='progress'`
and the related options. Each message has a unique `msg_id`. A subsequent
message with the same `msg_id` replaces the older one.
Create or update a progress-message by calling |nvim_echo()| with
`kind='progress'`. Each message has a unique message-id, returned by
`nvim_echo`. You can specify the id when calling `nvim_echo` to update an
existing progress-message.
Events: ~
• msg_show |ui-messages| event is fired for ext-ui upon creation/update of a
progress-message
• Updating or creating a progress message also triggers the |Progress| autocommand.
Example: >
local id = vim.api.nvim_echo({{'searching...'}}, true,
{kind='progress', status='running', percent=10, title="term"})
vim.api.nvim_echo({{'searching'}}, true,
{id=id, kind='progress', status='running', percent=50, title="term"})
vim.api.nvim_echo({{'done'}}, true,
{id=id, kind='progress', status='success', percent=100, title="term"})
Example: >lua
local progress = {
kind = 'progress',
status = 'running',
percent = 10,
title = 'term',
}
progress.id = vim.api.nvim_echo({ { 'searching...' } }, true, progress)
progress.percent = 50
vim.api.nvim_echo({ { 'searching' } }, true, progress)
progress.status = 'success'
progress.percent = 100
vim.api.nvim_echo({ { 'done' } }, true, progress)
<
See also: |nvim_echo()| |ui-messages| |Progress|

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

@@ -68,7 +68,9 @@ DIAGNOSTICS
EDITOR
• todo
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
pasting instead of like user input. Improves performance, avoids broken
formatting. To get the old behavior you can use `<C-R>=@x`.
EVENTS
@@ -202,7 +204,7 @@ EVENTS
HIGHLIGHTS
• |hl-DiffTextAdd| highlights added text within a changed line.
• |hl-StderrMsg| |hl-StdoutMsg|
|hl-OkMsg| |hl-StderrMsg| |hl-StdoutMsg|
• |hl-SnippetTabstopActive| highlights the currently active tabstop.
LSP
@@ -242,9 +244,13 @@ LSP
• |vim.lsp.buf.signature_help()| supports "noActiveParameterSupport".
• Support for `textDocument/inlineCompletion` |lsp-inline_completion|
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
See |lsp-inline_completion| for quickstart instructions.
• Support for `textDocument/onTypeFormatting`: |lsp-on_type_formatting|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_onTypeFormatting
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.
@@ -286,6 +292,7 @@ PERFORMANCE
support for nested braces and follows LSP 3.17 specification with
additional constraints for improved correctness and resistance to
backtracking edge cases.
- |i_CTRL-R| inserts named/clipboard registers literally, 10x speedup.
PLUGINS

View File

@@ -649,11 +649,11 @@ ask you where to write the file (there must be a writable directory in
'runtimepath' for this).
The plugin has a default place where to look for spell files, on the Vim ftp
server. The protocol used is SSL (https://) for security. If you want to use
another location or another protocol, set the g:spellfile_URL variable to the
directory that holds the spell files. You can use http:// or ftp://, but you
are taking a security risk then. The |netrw| plugin is used for getting the
file, look there for the specific syntax of the URL. Example: >
server. The protocol used is TLS (`https://`) for security. If you want to
use another location or another protocol, set the g:spellfile_URL variable to
the directory that holds the spell files. You can use `http://` or `ftp://`,
but you are taking a security risk then. The |netrw| plugin is used for
getting the file, look there for the specific syntax of the URL. Example: >
let g:spellfile_URL = 'https://ftp.nluug.nl/vim/runtime/spell'
You may need to escape special characters.

View File

@@ -5263,8 +5263,12 @@ 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.
*hl-WarningMsg*
WarningMsg Warning messages.
*hl-ErrorMsg*
ErrorMsg Error messages on the command line.
ErrorMsg Error messages.
*hl-StderrMsg*
StderrMsg Messages in stderr from shell commands.
*hl-StdoutMsg*
@@ -5405,8 +5409,6 @@ Title Titles for output from ":set all", ":autocmd" etc.
Visual Visual mode selection.
*hl-VisualNOS*
VisualNOS Visual mode selection when vim is "Not Owning the Selection".
*hl-WarningMsg*
WarningMsg Warning messages.
*hl-Whitespace*
Whitespace "nbsp", "space", "tab", "multispace", "lead" and "trail"
in 'listchars'.

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

@@ -136,10 +136,10 @@ might want to try the manual way of downloading the file.
Accessing files over the internet works with the netrw plugin. Currently URLs
with these formats are recognized:
ftp:// uses ftp
rcp:// uses rcp
scp:// uses scp
http:// uses wget (reading only)
`ftp://` uses ftp
`rcp://` uses rcp
`scp://` uses scp
`http://` uses wget (reading only)
Vim doesn't do the communication itself, it relies on the mentioned programs
to be available on your computer. On most Unix systems "ftp" and "rcp" will
@@ -147,7 +147,7 @@ be present. "scp" and "wget" might need to be installed.
Vim detects these URLs for each command that starts editing a new file, also
with ":edit" and ":split", for example. Write commands also work, except for
http://.
`http://`.
For more information, also about passwords, see |netrw|.

View File

@@ -316,6 +316,9 @@ Commands:
Editor:
- |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal
commands.
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
pasting instead of like user input. Improves performance, avoids broken
formatting. To get the old behavior you can use `<C-R>=@x`.
Events (autocommands):
- Fixed inconsistent behavior in execution of nested autocommands #23368

View File

@@ -0,0 +1,75 @@
" Vim Keymap file for Arabic using Buckwalter Transliteration
" see
" http://qamus.org/transliteration.htm
" https://en.wikipedia.org/wiki/Buckwalter_transliteration
"
" Created By: Rafael Ketsetsides <rketsetsides@gmail.com>
" Last Update: 2025 Sep 02
let b:keymap_name = "arabic_buckwalter"
loadkeymap
A <char-0x0627> " ALEF
b <char-0x0628> " BEH
p <char-0x0629> " TEH MARBUTA
t <char-0x062a> " TEH
v <char-0x062b> " THEH
j <char-0x062c> " JEEM
H <char-0x062d> " HAH
x <char-0x062e> " KHAH
d <char-0x062f> " DAL
* <char-0x0630> " THAL
r <char-0x0631> " REH
z <char-0x0632> " ZAIN
s <char-0x0633> " SEEN
$ <char-0x0634> " SHEEN
S <char-0x0635> " SAD
D <char-0x0636> " DAD
T <char-0x0637> " TAH
Z <char-0x0638> " ZAH
E <char-0x0639> " AIN
g <char-0x063a> " GHAIN
_ <char-0x0640> " TATWEEL
f <char-0x0641> " FEH
q <char-0x0642> " QAF
k <char-0x0643> " KAF
l <char-0x0644> " LAM
m <char-0x0645> " MEEM
n <char-0x0646> " NOON
h <char-0x0647> " HEH
w <char-0x0648> " WAW
Y <char-0x0649> " ALEF MAKSURA
y <char-0x064a> " YEH
' <char-0x0621> " HAMZA
| <char-0x0622> " ALEF WITH MADDA ABOVE
> <char-0x0623> " ALEF WITH HAMZA ABOVE
& <char-0x0624> " WAW WITH HAMZA ABOVE
< <char-0x0625> " ALEF WITH HAMZA BELOW
} <char-0x0626> " YEH WITH HAMZA ABOVE
F <char-0x064b> " Tanween -- FATHATAN
N <char-0x064c> " Tanween -- DAMMATAN
K <char-0x064d> " Tanween -- KASRATAN
a <char-0x064e> " Tanween -- FATHA
u <char-0x064f> " Tanween -- DAMMA
i <char-0x0650> " Tanween -- KASRA
~ <char-0x0651> " Tanween -- SHADDA
o <char-0x0652> " Tanween -- SUKUN
; <char-0x061b> " Arabic Semicolon
, <char-0x060c> " Arabic Comma
? <char-0x061f> " Arabic Question Mark
{ <char-0x0671> " ALEF WASLA
P <char-0x067E> " PEH
J <char-0x0686> " TCHEH
V <char-0x06A4> " VEH
G <char-0x06AF> " GAF
0 <char-0x06F0> " Arabic-Indic 0
1 <char-0x06F1> " Arabic-Indic 1
2 <char-0x06F2> " Arabic-Indic 2
3 <char-0x06F3> " Arabic-Indic 3
4 <char-0x06F4> " Arabic-Indic 4
5 <char-0x06F5> " Arabic-Indic 5
6 <char-0x06F6> " Arabic-Indic 6
7 <char-0x06F7> " Arabic-Indic 7
8 <char-0x06F8> " Arabic-Indic 8
9 <char-0x06F9> " Arabic-Indic 9

View File

@@ -2157,8 +2157,7 @@ function vim.api.nvim_set_current_win(window) end
--- ```
--- ["win", winid, bufnr, toprow, botrow]
--- ```
--- - on_line: called for each buffer line being redrawn.
--- (The interaction with fold lines is subject to change)
--- - on_line: (deprecated, use on_range instead)
--- ```
--- ["line", winid, bufnr, row]
--- ```

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'),
@@ -2346,7 +2348,7 @@ local pattern = {
},
['require'] = {
['%-requirements%.txt$'] = 'requirements',
['requirements%-.*%.txt$'] = 'requirements',
['^requirements%-.*%.txt$'] = 'requirements',
['^requirements/.*%.txt$'] = 'requirements',
['^requires/.*%.txt$'] = 'requirements',
},
@@ -2528,6 +2530,8 @@ local pattern = {
[',v$'] = 'rcs',
['^svn%-commit.*%.tmp$'] = 'svn',
['%.swift%.gyb$'] = 'swiftgyb',
['^vivado.*%.jou$'] = 'tcl',
['^vivado.*%.log$'] = 'tcl',
['termcap'] = starsetf(function(_path, _bufnr)
return require('vim.filetype.detect').printcap('term')
end),

View File

@@ -844,28 +844,30 @@ function M.inc(path, bufnr)
if vim.g.filetype_inc then
return vim.g.filetype_inc
end
local lines = table.concat(getlines(bufnr, 1, 3))
if lines:lower():find('perlscript') then
return 'aspperl'
elseif lines:find('<%%') then
return 'aspvbs'
elseif lines:find('<%?') then
return 'php'
for _, line in ipairs(getlines(bufnr, 1, 20)) do
if line:lower():find('perlscript') then
return 'aspperl'
elseif line:find('<%%') then
return 'aspvbs'
elseif line:find('<%?') then
return 'php'
-- Pascal supports // comments but they're vary rarely used for file
-- headers so assume POV-Ray
elseif findany(lines, { '^%s{', '^%s%(%*' }) or matchregex(lines, pascal_keywords) then
return 'pascal'
elseif findany(lines, { '^%s*inherit ', '^%s*require ', '^%s*%u[%w_:${}]*%s+%??[?:+]?= ' }) then
return 'bitbake'
else
local syntax = M.asm_syntax(path, bufnr)
if not syntax or syntax == '' then
return 'pov'
end
return syntax, function(b)
vim.b[b].asmsyntax = syntax
elseif findany(line, { '^%s{', '^%s%(%*' }) or matchregex(line, pascal_keywords) then
return 'pascal'
elseif
findany(line, { '^%s*inherit ', '^%s*require ', '^%s*%u[%w_:${}/]*%s+%??[?:+.]?=.? ' })
then
return 'bitbake'
end
end
local syntax = M.asm_syntax(path, bufnr)
if not syntax or syntax == '' then
return 'pov'
end
return syntax, function(b)
vim.b[b].asmsyntax = syntax
end
end
--- @type vim.filetype.mapfn

View File

@@ -19,6 +19,7 @@ local lsp = vim._defer_require('vim.lsp', {
inline_completion = ..., --- @module 'vim.lsp.inline_completion'
linked_editing_range = ..., --- @module 'vim.lsp.linked_editing_range'
log = ..., --- @module 'vim.lsp.log'
on_type_formatting = ..., --- @module 'vim.lsp.on_type_formatting'
protocol = ..., --- @module 'vim.lsp.protocol'
rpc = ..., --- @module 'vim.lsp.rpc'
semantic_tokens = ..., --- @module 'vim.lsp.semantic_tokens'

View File

@@ -211,6 +211,9 @@ local all_clients = {}
---
--- @field _enabled_capabilities table<vim.lsp.capability.Name, boolean?>
---
--- Whether on-type formatting is enabled for this client.
--- @field _otf_enabled boolean?
---
--- Track this so that we can escalate automatically if we've already tried a
--- graceful shutdown
--- @field private _graceful_shutdown_failed true?

View File

@@ -1,3 +1,35 @@
--- @brief
--- This module provides the LSP "inline completion" feature, for completing multiline text (e.g.,
--- whole methods) instead of just a word or line, which may result in "syntactically or
--- semantically incorrect" code. Unlike regular completion, this is typically presented as overlay
--- text instead of a menu of completion candidates.
---
--- LSP spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
---
--- To try it out, here is a quickstart example using Copilot: [lsp-copilot]()
---
--- 1. Install Copilot:
--- ```sh
--- npm install --global @github/copilot-language-server
--- ```
--- 2. Define a config, (or copy `lsp/copilot.lua` from https://github.com/neovim/nvim-lspconfig):
--- ```lua
--- vim.lsp.config('copilot', {
--- cmd = { 'copilot-language-server', '--stdio', },
--- root_markers = { '.git' },
--- })
--- ```
--- 3. Activate the config:
--- ```lua
--- vim.lsp.enable('copilot')
--- ```
--- 4. Sign in to Copilot, or use the `:LspCopilotSignIn` command from https://github.com/neovim/nvim-lspconfig
--- 5. Enable inline completion:
--- ```lua
--- vim.lsp.inline_completion.enable()
--- ```
--- 6. Set a keymap for `vim.lsp.inline_completion.get()` and invoke the keymap.
local util = require('vim.lsp.util')
local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
@@ -11,11 +43,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 +57,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 +178,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 +313,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 +331,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 +344,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 +408,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 +423,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 +452,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

@@ -0,0 +1,261 @@
local api = vim.api
local lsp = vim.lsp
local util = lsp.util
local method = lsp.protocol.Methods.textDocument_onTypeFormatting
local schedule = vim.schedule
local current_buf = api.nvim_get_current_buf
local get_mode = api.nvim_get_mode
local ns = api.nvim_create_namespace('nvim.lsp.on_type_formatting')
local augroup = api.nvim_create_augroup('nvim.lsp.on_type_formatting', {})
local M = {}
--- @alias vim.lsp.on_type_formatting.BufTriggers table<string, table<integer, vim.lsp.Client>>
--- A map from bufnr -> trigger character -> client ID -> client
--- @type table<integer, vim.lsp.on_type_formatting.BufTriggers>
local buf_handles = {}
--- |lsp-handler| for the `textDocument/onTypeFormatting` method.
---
--- @param err? lsp.ResponseError
--- @param result? lsp.TextEdit[]
--- @param ctx lsp.HandlerContext
local function on_type_formatting(err, result, ctx)
if err then
lsp.log.error('on_type_formatting', err)
return
end
local bufnr = assert(ctx.bufnr)
-- A `null` result is equivalent to an empty `TextEdit[]` result; no work should be done.
if not result or not api.nvim_buf_is_loaded(bufnr) or util.buf_versions[bufnr] ~= ctx.version then
return
end
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end
---@param bufnr integer
---@param typed string
---@param triggered_clients vim.lsp.Client[]
---@param idx integer?
---@param client vim.lsp.Client?
local function format_iter(bufnr, typed, triggered_clients, idx, client)
if not idx or not client then
return
end
---@type lsp.DocumentOnTypeFormattingParams
local params = vim.tbl_extend(
'keep',
util.make_formatting_params(),
util.make_position_params(0, client.offset_encoding),
{ ch = typed }
)
client:request(method, params, function(...)
on_type_formatting(...)
format_iter(bufnr, typed, triggered_clients, next(triggered_clients, idx))
end, bufnr)
end
---@param typed string
local function on_key(_, typed)
local mode = get_mode()
if mode.blocking or mode.mode ~= 'i' then
return
end
local bufnr = current_buf()
local buf_handle = buf_handles[bufnr]
if not buf_handle then
return
end
-- LSP expects '\n' for formatting on newline
if typed == '\r' then
typed = '\n'
end
local triggered_clients = buf_handle[typed]
if not triggered_clients then
return
end
-- Schedule the formatting to occur *after* the LSP is aware of the inserted character
schedule(function()
format_iter(bufnr, typed, triggered_clients, next(triggered_clients))
end)
end
--- @param client vim.lsp.Client
--- @param bufnr integer
local function detach(client, bufnr)
local buf_handle = buf_handles[bufnr]
if not buf_handle then
return
end
local client_id = client.id
-- Remove this client from its associated trigger characters
for trigger_char, attached_clients in pairs(buf_handle) do
attached_clients[client_id] = nil
-- Remove the trigger character if we detached its last client.
if not next(attached_clients) then
buf_handle[trigger_char] = nil
end
end
-- Remove the buf handle and its autocmds if we removed its last client.
if not next(buf_handle) then
buf_handles[bufnr] = nil
api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
-- Remove the on_key callback if we removed the last buf handle.
if not next(buf_handles) then
vim.on_key(nil, ns)
end
end
end
--- @param client vim.lsp.Client
--- @param bufnr integer
local function attach(client, bufnr)
if not client:supports_method(method, bufnr) then
return
end
local client_id = client.id
---@type lsp.DocumentOnTypeFormattingOptions
local otf_capabilities =
assert(vim.tbl_get(client.server_capabilities, 'documentOnTypeFormattingProvider'))
-- Set on_key callback, clearing first in case it was already registered.
vim.on_key(nil, ns)
vim.on_key(on_key, ns)
-- Populate the buf handle data. We cannot use defaulttable here because then an empty table will
-- be created for each unique keystroke
local buf_handle = buf_handles[bufnr] or {}
buf_handles[bufnr] = buf_handle
local trigger = buf_handle[otf_capabilities.firstTriggerCharacter] or {}
buf_handle[otf_capabilities.firstTriggerCharacter] = trigger
trigger[client_id] = client
for _, char in ipairs(otf_capabilities.moreTriggerCharacter or {}) do
trigger = buf_handle[char] or {}
buf_handle[char] = trigger
trigger[client_id] = client
end
api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
api.nvim_create_autocmd('LspDetach', {
buffer = bufnr,
desc = 'Detach on-type formatting module when the client detaches',
group = augroup,
callback = function(args)
local detached_client = assert(lsp.get_client_by_id(args.data.client_id))
detach(detached_client, bufnr)
end,
})
end
api.nvim_create_autocmd('LspAttach', {
desc = 'Enable on-type formatting for all buffers with individually-enabled clients.',
callback = function(ev)
local buf = ev.buf
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled then
attach(client, buf)
end
end,
})
---@param enable boolean
---@param client vim.lsp.Client
local function toggle_for_client(enable, client)
local handler = enable and attach or detach
-- Toggle for buffers already attached.
for bufnr, _ in pairs(client.attached_buffers) do
handler(client, bufnr)
end
client._otf_enabled = enable
end
---@param enable boolean
local function toggle_globally(enable)
-- Toggle for clients that have already attached.
local clients = lsp.get_clients({ method = method })
for _, client in ipairs(clients) do
toggle_for_client(enable, client)
end
-- If disabling, only clear the attachment autocmd. If enabling, create it as well.
local group = api.nvim_create_augroup('nvim.lsp.on_type_formatting', { clear = true })
if enable then
api.nvim_create_autocmd('LspAttach', {
group = group,
desc = 'Enable on-type formatting for ALL clients by default.',
callback = function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled ~= false then
attach(client, ev.buf)
end
end,
})
end
end
--- Optional filters |kwargs|:
--- @inlinedoc
--- @class vim.lsp.on_type_formatting.enable.Filter
--- @field client_id integer? Client ID, or `nil` for all.
--- Enables/disables on-type formatting globally or for the {filter}ed scope. The following are some
--- practical usage examples:
---
--- ```lua
--- -- Enable for all clients
--- vim.lsp.on_type_formatting.enable()
---
--- -- Enable for a specific client
--- vim.api.nvim_create_autocmd('LspAttach', {
--- callback = function(args)
--- local client_id = args.data.client_id
--- local client = assert(vim.lsp.get_client_by_id(client_id))
--- if client.name == 'rust-analyzer' then
--- vim.lsp.on_type_formatting.enable(true, { client_id = client_id })
--- end
--- end,
--- })
--- ```
---
--- @param enable? boolean true/nil to enable, false to disable.
--- @param filter vim.lsp.on_type_formatting.enable.Filter?
function M.enable(enable, filter)
vim.validate('enable', enable, 'boolean', true)
vim.validate('filter', filter, 'table', true)
enable = enable ~= false
filter = filter or {}
if filter.client_id then
local client =
assert(lsp.get_client_by_id(filter.client_id), 'Client not found for id ' .. filter.client_id)
toggle_for_client(enable, client)
else
toggle_globally(enable)
end
end
return M

View File

@@ -572,6 +572,9 @@ function protocol.make_client_capabilities()
linkedEditingRange = {
dynamicRegistration = false,
},
onTypeFormatting = {
dynamicRegistration = false,
},
},
workspace = {
symbol = {

View File

@@ -732,10 +732,9 @@ local function python()
local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": '
.. pynvim_exe
local advice = {
'Use that Python version to reinstall "pynvim" and optionally "neovim".',
'Use that Python version to uninstall any "pynvim" or "neovim", e.g.:',
pynvim_exe .. ' -m pip uninstall pynvim neovim',
pynvim_exe .. ' -m pip install pynvim',
pynvim_exe .. ' -m pip install neovim # only if needed by third-party software',
'Then see :help provider-python for "pynvim" installation steps.',
}
health.error(message, advice)
end
@@ -761,7 +760,7 @@ local function python()
if is_bad_response(current) then
health.error(
'pynvim is not installed.\nError: ' .. current,
'Run in shell: ' .. python_exe .. ' -m pip install pynvim'
'See :help provider-python for "pynvim" installation steps.'
)
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

@@ -1,9 +1,8 @@
" Vim syntax file
" Language: M4
" Language: M4
" Maintainer: Claudio Fleiner (claudio@fleiner.com)
" URL: http://www.fleiner.com/vim/syntax/m4.vim
" (outdated)
" Last Change: 2022 Jun 12
" 2025 Sep 2 by Vim project: fix a few syntax issues #18192
" This file will highlight user function calls if they use only
" capital letters and have at least one argument (i.e. the '('
@@ -18,6 +17,23 @@ if !exists("main_syntax")
let main_syntax='m4'
endif
" Reference: The Open Group Base Specifications, M4
" https://pubs.opengroup.org/onlinepubs/9799919799/
" Quoting in M4:
" Quotes are nestable;
" The delimiters can be redefined with changequote(); here we only handle
" the default pair: ` ... ';
" Quoted text in M4 is rescanned, not treated as a literal string;
" Therefore the region is marked transparent so contained items retain
" their normal highlighting.
syn region m4Quoted
\ matchgroup=m4QuoteDelim
\ start=+`+
\ end=+'+
\ contains=@m4Top
\ transparent
" define the m4 syntax
syn match m4Variable contained "\$\d\+"
syn match m4Special contained "$[@*#]"
@@ -30,28 +46,26 @@ syn region m4Paren matchgroup=m4Delimiter start="(" end=")" contained contain
syn region m4Command matchgroup=m4Function start="\<\(m4_\)\=\(define\|defn\|pushdef\)(" end=")" contains=@m4Top
syn region m4Command matchgroup=m4Preproc start="\<\(m4_\)\=\(include\|sinclude\)("he=e-1 end=")" contains=@m4Top
syn region m4Command matchgroup=m4Statement start="\<\(m4_\)\=\(syscmd\|esyscmd\|ifdef\|ifelse\|indir\|builtin\|shift\|errprint\|m4exit\|changecom\|changequote\|changeword\|m4wrap\|debugfile\|divert\|undivert\)("he=e-1 end=")" contains=@m4Top
syn region m4Command matchgroup=m4builtin start="\<\(m4_\)\=\(len\|index\|regexp\|substr\|translit\|patsubst\|format\|incr\|decr\|eval\|maketemp\)("he=e-1 end=")" contains=@m4Top
syn region m4Command matchgroup=m4Builtin start="\<\(m4_\)\=\(len\|index\|regexp\|substr\|translit\|patsubst\|format\|incr\|decr\|eval\|maketemp\)("he=e-1 end=")" contains=@m4Top
syn keyword m4Statement divert undivert
syn region m4Command matchgroup=m4Type start="\<\(m4_\)\=\(undefine\|popdef\)("he=e-1 end=")" contains=@m4Top
syn region m4Function matchgroup=m4Type start="\<[_A-Z][_A-Z0-9]*("he=e-1 end=")" contains=@m4Top
syn region m4String start="`" end="'" contains=SpellErrors
syn cluster m4Top contains=m4Comment,m4Constants,m4Special,m4Variable,m4String,m4Paren,m4Command,m4Statement,m4Function
syn cluster m4Top contains=m4Comment,m4Constants,m4Special,m4Variable,m4Paren,m4Command,m4Statement,m4Function,m4Quoted
" Define the default highlighting.
" Only when an item doesn't have highlighting yet
hi def link m4Delimiter Delimiter
hi def link m4Comment Comment
hi def link m4Function Function
hi def link m4Keyword Keyword
hi def link m4Special Special
hi def link m4String String
hi def link m4Statement Statement
hi def link m4Preproc PreProc
hi def link m4Type Type
hi def link m4Special Special
hi def link m4Variable Special
hi def link m4Constants Constant
hi def link m4Builtin Statement
hi def link m4QuoteDelim Delimiter
hi def link m4Delimiter Delimiter
hi def link m4Comment Comment
hi def link m4Function Function
hi def link m4Keyword Keyword
hi def link m4Special Special
hi def link m4Statement Statement
hi def link m4Preproc PreProc
hi def link m4Type Type
hi def link m4Variable Special
hi def link m4Constants Constant
hi def link m4Builtin Statement
let b:current_syntax = "m4"

View File

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

View File

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

View File

@@ -395,6 +395,7 @@ output:write([[
#include "nvim/api/buffer.h"
#include "nvim/api/command.h"
#include "nvim/api/deprecated.h"
#include "nvim/api/events.h"
#include "nvim/api/extmark.h"
#include "nvim/api/options.h"
#include "nvim/api/tabpage.h"

View File

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

View File

@@ -1,5 +1,8 @@
#!/usr/bin/env -S nvim -l
--- Generates Nvim :help docs from Lua/C docstrings
--- Generates Nvim :help docs from Lua/C docstrings.
---
--- Usage:
--- make doc
---
--- The generated :help text for each function is formatted as follows:
--- - Max width of 78 columns (`TEXT_WIDTH`).
@@ -106,6 +109,7 @@ local config = {
filename = 'api.txt',
section_order = {
-- Sections at the top, in a specific order:
'events.c',
'vim.c',
'vimscript.c',
@@ -126,11 +130,22 @@ local config = {
['vim.c'] = 'Global',
},
section_fmt = function(name)
if name == 'Events' then
return 'Global Events'
end
return name .. ' Functions'
end,
helptag_fmt = function(name)
return fmt('api-%s', name:lower())
end,
fn_helptag_fmt = function(fun)
local name = fun.name
if vim.endswith(name, '_event') then
return name
end
return fn_helptag_fmt_common(fun)
end,
},
lua = {
filename = 'lua.txt',
@@ -286,6 +301,7 @@ local config = {
'inline_completion.lua',
'linked_editing_range.lua',
'log.lua',
'on_type_formatting.lua',
'rpc.lua',
'semantic_tokens.lua',
'tagfunc.lua',

View File

@@ -409,13 +409,18 @@ name, etc.
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
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
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
commands has finished. The work is done by `update_screen()`, which calls
`win_update()` for every window, which calls `win_line()` for every line.

76
src/nvim/api/events.c Normal file
View File

@@ -0,0 +1,76 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
#include "nvim/api/events.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
#include "nvim/assert_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/channel.h"
#include "nvim/channel_defs.h"
#include "nvim/eval.h"
#include "nvim/globals.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
/// Emitted on the client channel if an async API request responds with an error.
///
/// @param channel_id
/// @param type Error type id as defined by `api_info().error_types`.
/// @param msg Error message.
void nvim_error_event(uint64_t channel_id, Integer type, String msg)
FUNC_API_REMOTE_ONLY
{
// TODO(bfredl): consider printing message to user, as will be relevant
// if we fork nvim processes as async workers
ELOG("async error on channel %" PRId64 ": %s", channel_id, msg.size ? msg.data : "");
}
/// Emitted by the TUI client to signal when a host-terminal event occurred.
///
/// Supports these events:
///
/// - "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response sequence to Nvim.
/// The payload is the received response. Sets |v:termresponse| and fires
/// |TermResponse|.
///
/// @param channel_id
/// @param event Event name
/// @param value Event payload
/// @param[out] err Error details, if any.
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
{
if (strequal("termresponse", event.data)) {
if (value.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, "termresponse must be a string");
return;
}
const String termresponse = value.data.string;
set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
MAXSIZE_TEMP_DICT(data, 1);
PUT_C(data, "sequence", value);
apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
&DICT_OBJ(data));
}
}

8
src/nvim/api/events.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdint.h> // IWYU pragma: keep
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "api/events.h.generated.h"

View File

@@ -1033,8 +1033,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// ```
/// ["win", winid, bufnr, toprow, botrow]
/// ```
/// - on_line: called for each buffer line being redrawn.
/// (The interaction with fold lines is subject to change)
/// - on_line: (deprecated, use on_range instead)
/// ```
/// ["line", winid, bufnr, row]
/// ```

View File

@@ -567,37 +567,6 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
ui->pum_pos = true;
}
/// Tells Nvim when a host-terminal event occurred.
///
/// Supports these events:
///
/// - "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response sequence to Nvim.
/// The payload is the received response. Sets |v:termresponse| and fires
/// |TermResponse|.
///
/// @param channel_id
/// @param event Event name
/// @param value Event payload
/// @param[out] err Error details, if any.
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
{
if (strequal("termresponse", event.data)) {
if (value.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, "termresponse must be a string");
return;
}
const String termresponse = value.data.string;
set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
MAXSIZE_TEMP_DICT(data, 1);
PUT_C(data, "sequence", value);
apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
&DICT_OBJ(data));
}
}
static void flush_event(RemoteUI *ui)
{
if (ui->cur_event) {

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;
}
@@ -1557,7 +1558,7 @@ ArrayOf(Object, 2) nvim_get_api_info(uint64_t channel_id, Arena *arena)
/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
/// `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.
///
/// @param channel_id
@@ -2221,15 +2222,6 @@ DictAs(eval_statusline_ret) nvim_eval_statusline(String str, Dict(eval_statuslin
return result;
}
/// @nodoc
void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
FUNC_API_REMOTE_ONLY
{
// TODO(bfredl): consider printing message to user, as will be relevant
// if we fork nvim processes as async workers
ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : "");
}
/// EXPERIMENTAL: this API may change in the future.
///
/// Sets info for the completion item at the given index. If the info text was shown in a window,

View File

@@ -2854,6 +2854,8 @@ static void ins_reg(void)
vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else {
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
if (literally == Ctrl_O || literally == Ctrl_P) {
// Append the command to the redo buffer.
AppendCharToRedobuff(Ctrl_R);
@@ -2862,7 +2864,11 @@ static void ins_reg(void)
do_put(regname, NULL, BACKWARD, 1,
(literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
} else if (insert_reg(regname, NULL, literally) == FAIL) {
} else if (reg->y_size > 1 && is_literal_register(regname)) {
AppendCharToRedobuff(Ctrl_R);
AppendCharToRedobuff(regname);
do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND);
} else if (insert_reg(regname, NULL, !!literally) == FAIL) {
vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else if (stop_insert_mode) {

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

@@ -17,6 +17,8 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_TERM] = "TermCursor",
[HLF_AT] = "NonText",
[HLF_D] = "Directory",
[HLF_OK] = "OkMsg",
[HLF_W] = "WarningMsg",
[HLF_E] = "ErrorMsg",
[HLF_SE] = "StderrMsg",
[HLF_SO] = "StdoutMsg",
@@ -39,7 +41,6 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_V] = "Visual",
[HLF_VNC] = "VisualNC",
[HLF_VSP] = "VertSplit",
[HLF_W] = "WarningMsg",
[HLF_WM] = "WildMenu",
[HLF_FL] = "Folded",
[HLF_FC] = "FoldColumn",

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

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

View File

@@ -292,26 +292,73 @@ 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 Progress-message data
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();
msg_clr_eos();
bool need_clear = false;
bool hl_msg_updated = false;
msg_ext_history = history;
if (kind != NULL) {
msg_ext_set_kind(kind);
}
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) {
@@ -322,13 +369,16 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
msg_id_next = id.data.integer + 1;
}
}
msg_ext_id = id;
// 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_updated = true;
hl_msg = formated_message;
}
}
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
@@ -341,12 +391,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);
}
@@ -355,6 +399,10 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
is_multihl = false;
no_wait_return--;
msg_end();
if (hl_msg_updated && !(history && kv_size(hl_msg))) {
hl_msg_free(hl_msg);
}
return id;
}
@@ -1120,7 +1168,6 @@ static void msg_hist_add_multihl(MsgID msg_id, HlMessage msg, bool temp, Message
msg_hist_last = entry;
msg_ext_history = true;
msg_ext_id = msg_id;
msg_hist_clear(msg_hist_max);
}
@@ -1265,7 +1312,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

@@ -99,7 +99,7 @@ static const char e_search_pattern_and_expression_register_may_not_contain_two_o
/// The names of operators.
/// IMPORTANT: Index must correspond with defines in ops.h!!!
/// The third field indicates whether the operator always works on lines.
static char opchars[][3] = {
static const char opchars[][3] = {
{ NUL, NUL, 0 }, // OP_NOP
{ 'd', NUL, OPF_CHANGE }, // OP_DELETE
{ 'y', NUL, 0 }, // OP_YANK

View File

@@ -152,7 +152,7 @@ static inline int op_reg_index(const int regname)
static inline bool is_literal_register(const int regname)
FUNC_ATTR_CONST
{
return regname == '*' || regname == '+';
return regname == '*' || regname == '+' || ASCII_ISALNUM(regname);
}
EXTERN LuaRef repeat_luaref INIT( = LUA_NOREF); ///< LuaRef for "."

View File

@@ -39,6 +39,10 @@
# include "nvim/fileio.h"
#endif
#ifdef __APPLE__
# include <mach/task.h>
#endif
#ifdef HAVE__NSGETENVIRON
# include <crt_externs.h>
#endif
@@ -367,6 +371,18 @@ int64_t os_get_pid(void)
#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.
///
/// @param hostname Buffer to store the hostname.

View File

@@ -335,7 +335,8 @@ ScreenGrid *ui_comp_get_grid_at_coord(int row, int col)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
ScreenGrid *grid = &wp->w_grid_alloc;
if (row >= grid->comp_row && row < grid->comp_row + grid->rows
&& col >= grid->comp_col && col < grid->comp_col + grid->cols) {
&& col >= grid->comp_col && col < grid->comp_col + grid->cols
&& !wp->w_config.hide) {
return grid;
}
}

View File

@@ -110,7 +110,7 @@ static const int included_patches[] = {
2374,
2373,
2372,
// 2371,
2371,
2370,
2369,
2368,
@@ -1222,7 +1222,7 @@ static const int included_patches[] = {
1262,
1261,
1260,
// 1259,
1259,
1258,
1257,
1256,
@@ -1345,7 +1345,7 @@ static const int included_patches[] = {
1139,
1138,
1137,
// 1136,
1136,
1135,
1134,
1133,

View File

@@ -3138,6 +3138,10 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp)
if (win == win_tp->tp_curwin) {
win_tp->tp_curwin = wp;
}
// Avoid executing cmdline_win logic after it is closed.
if (win == cmdline_win) {
cmdline_win = NULL;
}
return wp;
}
@@ -3412,7 +3416,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 (global_stl_height() == 0 && wp->w_status_height == 0) {
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);
}
}

View File

@@ -83,6 +83,36 @@ describe('insert-mode', function()
{5:-- INSERT --} |
]])
end)
it('inserts named/clipboard registers literally', function()
local screen = Screen.new(50, 6)
-- regular text without special charecter command
command('let @a = "test"')
feed('i<C-R>a<ESC>')
screen:expect([[
tes^t |
{1:~ }|*4
|
]])
-- text with backspace character gets written literally by default
command('let @a = "test\\<C-H>"')
feed('cc<C-R>a<ESC>')
screen:expect([[
test{18:^^H} |
{1:~ }|*4
|
]])
-- =@reg<CR> can be used to get effect of keypress
command('let @a = "test\\<C-H>"')
feed('cc<C-R>=@a<CR><ESC>')
screen:expect([[
te^s |
{1:~ }|*4
|
]])
end)
end)
describe('Ctrl-O', function()

View File

@@ -93,7 +93,7 @@ describe('luaeval()', function()
-- Not checked: funcrefs converted to NIL. To be altered to something more
-- meaningful later.
it('correctly evaluates scalars', function()
it('scalars', function()
-- Also test method call (->) syntax
eq(1, fn.luaeval('1'))
eq(0, eval('"1"->luaeval()->type()'))
@@ -114,7 +114,7 @@ describe('luaeval()', function()
eq(NIL, fn.luaeval('nil'))
end)
it('correctly evaluates containers', function()
it('containers', function()
eq({}, fn.luaeval('{}'))
eq(3, eval('type(luaeval("{}"))'))
@@ -145,7 +145,7 @@ describe('luaeval()', function()
)
end)
it('correctly passes scalars as argument', function()
it('passes scalars as argument', function()
eq(1, fn.luaeval('_A', 1))
eq(1.5, fn.luaeval('_A', 1.5))
eq('', fn.luaeval('_A', ''))
@@ -155,7 +155,7 @@ describe('luaeval()', function()
eq(false, fn.luaeval('_A', false))
end)
it('correctly passes containers as argument', function()
it('passes containers as argument', function()
eq({}, fn.luaeval('_A', {}))
eq({ test = 1 }, fn.luaeval('_A', { test = 1 }))
eq({ 4, 2 }, fn.luaeval('_A', { 4, 2 }))
@@ -187,7 +187,7 @@ describe('luaeval()', function()
]=]):format(expr or '"_A"', argexpr):gsub('\n', '')))
end
it('correctly passes special dictionaries', function()
it('passes special dictionaries', function()
eq({ 0, '\000\n\000' }, luaevalarg(sp('string', '["\\n", "\\n"]')))
eq({ 0, true }, luaevalarg(sp('boolean', 1)))
eq({ 0, false }, luaevalarg(sp('boolean', 0)))
@@ -195,7 +195,7 @@ describe('luaeval()', function()
eq({ 0, { [''] = '' } }, luaevalarg(mapsp(sp('string', '[""]'), '""')))
end)
it('issues an error in some cases', function()
it('failure modes', function()
eq(
'Vim(call):E5100: Cannot convert given Lua table: table should contain either only integer keys or only string keys',
exc_exec('call luaeval("{1, foo=2}")')
@@ -205,7 +205,7 @@ describe('luaeval()', function()
startswith('Vim(call):E5108: Lua: [string "luaeval()"]:', exc_exec('call luaeval("(nil)()")'))
end)
it('should handle sending lua functions to viml', function()
it('handles sending lua functions to viml', function()
eq(
true,
exec_lua [[
@@ -294,7 +294,7 @@ describe('luaeval()', function()
matches(': dead function\n', api.nvim_get_vvar('errmsg'))
end)
it('should handle passing functions around', function()
it('can pass functions around', function()
command [[
function VimCanCallLuaCallbacks(Concat, Cb)
let message = a:Concat("Hello Vim", "I'm Lua")
@@ -317,7 +317,7 @@ describe('luaeval()', function()
)
end)
it('should handle funcrefs', function()
it('handles funcrefs', function()
command [[
function VimCanCallLuaCallbacks(Concat, Cb)
let message = a:Concat("Hello Vim", "I'm Lua")
@@ -340,7 +340,7 @@ describe('luaeval()', function()
)
end)
it('should work with metatables using __call', function()
it('works with metatables using __call', function()
eq(
1,
exec_lua [[
@@ -362,7 +362,7 @@ describe('luaeval()', function()
)
end)
it('should handle being called from a timer once.', function()
it('handles being called from a timer once.', function()
eq(
3,
exec_lua [[
@@ -381,7 +381,7 @@ describe('luaeval()', function()
)
end)
it('should call functions once with __call metamethod', function()
it('calls functions once with __call metamethod', function()
eq(
true,
exec_lua [[
@@ -398,7 +398,7 @@ describe('luaeval()', function()
)
end)
it('should work with lists using __call', function()
it('works with lists using __call', function()
eq(
3,
exec_lua [[
@@ -429,7 +429,7 @@ describe('luaeval()', function()
)
end)
it('should not work with tables not using __call', function()
it('fails with tables not using __call', function()
eq(
{ false, 'Vim:E921: Invalid callback argument' },
exec_lua [[
@@ -441,7 +441,7 @@ describe('luaeval()', function()
)
end)
it('correctly converts containers with type_idx', function()
it('converts containers with type_idx', function()
eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))'))
eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]]))
eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]]))
@@ -463,7 +463,7 @@ describe('luaeval()', function()
eq({}, fn.luaeval('{[vim.type_idx]=vim.types.dictionary}'))
end)
it('correctly converts self-containing containers', function()
it('converts self-containing containers', function()
api.nvim_set_var('l', {})
eval('add(l, l)')
eq(true, eval('luaeval("_A == _A[1]", l)'))
@@ -479,7 +479,7 @@ describe('luaeval()', function()
eq(true, eval('luaeval("_A ~= _A.d", {"d": d})'))
end)
it('errors out correctly when doing incorrect things in lua', function()
it('fails when doing incorrect things in lua', function()
-- Conversion errors
eq(
'Vim(call):E5108: Lua: [string "luaeval()"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
@@ -531,7 +531,7 @@ describe('v:lua', function()
]])
end)
it('works in expressions', function()
it('in expressions', function()
eq(7, eval('v:lua.foo(3,4,v:null)'))
eq(true, exec_lua([[return _G.val == vim.NIL]]))
eq(NIL, eval('v:lua.mymod.noisy("eval")'))
@@ -545,7 +545,7 @@ describe('v:lua', function()
)
end)
it('works when called as a method', function()
it('when called as a method', function()
eq(123, eval('110->v:lua.foo(13)'))
eq(true, exec_lua([[return _G.val == nil]]))
@@ -562,7 +562,7 @@ describe('v:lua', function()
)
end)
it('works in :call', function()
it('in :call', function()
command(":call v:lua.mymod.noisy('command')")
eq('hey command', api.nvim_get_current_line())
eq(
@@ -571,7 +571,7 @@ describe('v:lua', function()
)
end)
it('works in func options', function()
it('in func options', function()
local screen = Screen.new(60, 8)
api.nvim_set_option_value('omnifunc', 'v:lua.mymod.omni', {})
feed('isome st<c-x><c-o>')
@@ -599,7 +599,7 @@ describe('v:lua', function()
eq(9004, eval("0 ? v:lua.require'bar'.doit() : v:lua.require'baz-quux'.doit()"))
end)
it('throw errors for invalid use', function()
it('fails for invalid usage', function()
eq(
[[Vim(let):E15: Invalid expression: "v:lua.func"]],
pcall_err(command, 'let g:Func = v:lua.func')
@@ -639,7 +639,7 @@ describe('v:lua', function()
eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, 'v:lua.()'))
end)
describe('invalid use in fold text', function()
describe('invalid usage in fold text', function()
before_each(function()
feed('ifoo<CR>bar<Esc>')
command('1,2fold')

View File

@@ -390,6 +390,13 @@ describe('vim.ui_attach', function()
9 bufname( {12: } |
Excommand:call bufadd^( |
]])
-- No crash after _cmdline_offset window is closed #35584.
exec_lua(function()
vim.ui_detach(_G.ns)
vim.api.nvim_win_close(_G.win, true)
end)
feed('<Esc>:<Tab>')
n.assert_alive()
end)
end)

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

@@ -0,0 +1,174 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local t_lsp = require('test.functional.plugin.lsp.testutil')
local retry = t.retry
local eq = t.eq
local dedent = t.dedent
local exec_lua = n.exec_lua
local insert = n.insert
local feed = n.feed
local clear_notrace = t_lsp.clear_notrace
local create_server_definition = t_lsp.create_server_definition
describe('vim.lsp.on_type_formatting', function()
local text = dedent([[
int main() {
int hi
}]])
before_each(function()
clear_notrace()
exec_lua(create_server_definition)
exec_lua(function()
_G.server = _G._create_server({
capabilities = {
documentOnTypeFormattingProvider = {
firstTriggerCharacter = '=',
},
},
handlers = {
---@param params lsp.DocumentOnTypeFormattingParams
---@param callback fun(err?: lsp.ResponseError, result?: lsp.TextEdit[])
['textDocument/onTypeFormatting'] = function(_, params, callback)
callback(nil, {
{
newText = ';',
range = {
start = params.position,
['end'] = params.position,
},
},
})
end,
},
})
_G.server_id = vim.lsp.start({
name = 'dummy',
cmd = _G.server.cmd,
})
vim.lsp.on_type_formatting.enable(true, { client_id = _G.server_id })
end)
insert(text)
end)
it('enables formatting on type', function()
exec_lua(function()
local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_cursor(win, { 2, 0 })
end)
feed('A = 5')
retry(nil, 100, function()
eq(
{
'int main() {',
' int hi = 5;',
'}',
},
exec_lua(function()
return vim.api.nvim_buf_get_lines(0, 0, -1, false)
end)
)
end)
end)
it('works with multiple clients', function()
exec_lua(function()
vim.lsp.on_type_formatting.enable(true)
_G.server2 = _G._create_server({
capabilities = {
documentOnTypeFormattingProvider = {
firstTriggerCharacter = '.',
moreTriggerCharacter = { '=' },
},
},
handlers = {
---@param params lsp.DocumentOnTypeFormattingParams
---@param callback fun(err?: lsp.ResponseError, result?: lsp.TextEdit[])
['textDocument/onTypeFormatting'] = function(_, params, callback)
callback(nil, {
{
newText = ';',
range = {
start = params.position,
['end'] = params.position,
},
},
})
end,
},
})
vim.lsp.start({
name = 'dummy2',
cmd = _G.server2.cmd,
})
local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_cursor(win, { 2, 0 })
end)
feed('A =')
retry(nil, 100, function()
eq(
{
'int main() {',
' int hi =;;',
'}',
},
exec_lua(function()
return vim.api.nvim_buf_get_lines(0, 0, -1, false)
end)
)
end)
end)
it('can be disabled', function()
exec_lua(function()
vim.lsp.on_type_formatting.enable(false, { client_id = _G.server_id })
local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_cursor(win, { 2, 0 })
end)
feed('A = 5')
eq(
{
'int main() {',
' int hi = 5',
'}',
},
exec_lua(function()
return vim.api.nvim_buf_get_lines(0, 0, -1, false)
end)
)
end)
it('attaches to new buffers', function()
exec_lua(function()
local buf = vim.api.nvim_create_buf(true, false)
vim.api.nvim_set_current_buf(buf)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, {
'int main() {',
' int hi',
'}',
})
local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_cursor(win, { 2, 0 })
vim.lsp.buf_attach_client(buf, _G.server_id)
end)
feed('A = 5')
retry(nil, 100, function()
eq(
{
'int main() {',
' int hi = 5;',
'}',
},
exec_lua(function()
return vim.api.nvim_buf_get_lines(0, 0, -1, false)
end)
)
end)
end)
end)

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)
@@ -3183,7 +3185,7 @@ describe('progress-message', function()
setup_autocmd()
end)
it('can be sent by nvim_echo', function()
it('emitted by nvim_echo', function()
local id = api.nvim_echo(
{ { 'test-message' } },
true,
@@ -3197,14 +3199,18 @@ 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',
},
},
})
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 (fail)' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 35, status = 'failed' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 9, 'ErrorMsg' },
{ ': ' },
{ ' 35% ', 19, 'WarningMsg' },
{ 'test-message (fail)' },
},
history = true,
id = 3,
kind = 'progress',
},
},
})
-- cancel status
api.nvim_echo(
{ { 'test-message (cancel)' } },
true,
{ kind = 'progress', title = 'TestSuit', percent = 30, status = 'cancel' }
)
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 19, 'WarningMsg' },
{ ': ' },
{ ' 30% ', 19, 'WarningMsg' },
{ 'test-message (cancel)' },
},
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,44 @@ describe('progress-message', function()
screen:expect([[
^ |
{1:~ }|*3
TestSuit: test-message...10% |
{6:TestSuit}: {19: 10% }test-message |
]])
end)
it('works with history off', function()
api.nvim_echo({ { 'test-message' } }, false, {
kind = 'progress',
title = 'TestSuit',
percent = 10,
status = 'running',
})
screen:expect({
grid = [[
^ |
{1:~ }|*4
]],
messages = {
{
content = {
{ 'TestSuit', 6, 'MoreMsg' },
{ ': ' },
{ ' 10% ', 19, 'WarningMsg' },
{ 'test-message' },
},
id = 1,
kind = 'progress',
},
},
})
assert_progress_autocmd({
text = { 'test-message' },
percent = 10,
status = 'running',
title = 'TestSuit',
id = 1,
data = {},
}, 'progress autocmd receives progress messages')
end)
end)

File diff suppressed because it is too large Load Diff

View File

@@ -362,6 +362,7 @@ local expect_keys = {
condition = true,
mouse_enabled = true,
any = true,
none = true,
mode = true,
unchanged = true,
intermediate = true,
@@ -408,7 +409,13 @@ end
--- following chars are magic characters
--- ( ) . % + - * ? [ ^ $
--- and must be escaped with a preceding % for a literal match.
--- @field any? string
--- @field any? string|table<string>
---
--- Lua pattern string expected to not match a screen line. NB: the
--- following chars are magic characters
--- ( ) . % + - * ? [ ^ $
--- and must be escaped with a preceding % for a literal match.
--- @field none? string|table<string>
---
--- Expected mode as signaled by "mode_change" event
--- @field mode? string
@@ -492,7 +499,7 @@ function Screen:expect(expected, attr_ids, ...)
grid = expected.grid
attr_ids = expected.attr_ids
condition = expected.condition
assert(expected.any == nil or grid == nil)
assert((expected.any == nil and expected.none == nil) or grid == nil)
elseif type(expected) == 'string' then
grid = expected
expected = {}
@@ -541,22 +548,55 @@ function Screen:expect(expected, attr_ids, ...)
local actual_rows
if expected.any or grid then
actual_rows = self:render(not expected.any, attr_state)
actual_rows = self:render(not (expected.any or expected.none), attr_state)
end
if expected.any then
-- Search for `any` anywhere in the screen lines.
local any_or_none = function(screen_str, value, is_any)
if value then
local v = value
if type(v) == 'string' then
v = { v }
end
local msg
if is_any then
msg = 'Expected (anywhere): "'
else
msg = 'Expected (nowhere): "'
end
for _, v2 in ipairs(v) do
local test = screen_str:find(v2)
if is_any then
test = not test
end
-- Search for `any` anywhere in the screen lines.
if test then
return (
'Failed to match any screen lines.\n'
.. msg
.. v2
.. '"\n'
.. 'Actual:\n |'
.. table.concat(actual_rows, '\n |')
.. '\n\n'
)
end
end
end
return nil
end
if expected.any or expected.none then
local actual_screen_str = table.concat(actual_rows, '\n')
if not actual_screen_str:find(expected.any) then
return (
'Failed to match any screen lines.\n'
.. 'Expected (anywhere): "'
.. expected.any
.. '"\n'
.. 'Actual:\n |'
.. table.concat(actual_rows, '\n |')
.. '\n\n'
)
if expected.any then
local res = any_or_none(actual_screen_str, expected.any, true)
if res then
return res
end
end
if expected.none then
local res = any_or_none(actual_screen_str, expected.none, false)
if res then
return res
end
end
end

View File

@@ -504,6 +504,20 @@ describe('global statusline', function()
{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)

View File

@@ -76,7 +76,39 @@ describe('screenchar() and family respect floating windows', function()
describe('with ext_multigrid', function()
with_ext_multigrid(true)
end)
describe('without ext_multigrid', function()
with_ext_multigrid(false)
end)
describe('hidden windows', function()
before_each(function()
clear()
Screen.new(40, 7, {})
api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa', 'aaa' })
end)
local assert_screen_funcs = function()
eq('a', fn.screenstring(1, 1))
eq(97, fn.screenchar(1, 1))
eq({ 97 }, fn.screenchars(1, 1))
eq(fn.screenattr(2, 1), fn.screenattr(1, 1))
end
it('manual', function()
local bufnr = api.nvim_create_buf(false, true)
api.nvim_buf_set_lines(bufnr, 0, -1, true, { 'bb' })
local win_opts = { relative = 'editor', row = 0, col = 0, height = 1, width = 2, hide = true }
api.nvim_open_win(bufnr, false, win_opts)
assert_screen_funcs()
end)
it('from ui2', function()
n.exec_lua('require("vim._extui").enable({ enable = true })')
command('echo "foo"')
assert_screen_funcs()
end)
end)
end)

View File

@@ -2090,7 +2090,7 @@ func Test_Cmdline()
let g:log = []
let @r = 'abc'
call feedkeys(":0\<C-R>r1\<C-R>\<C-O>r2\<C-R>\<C-R>r3\<Esc>", 'xt')
call feedkeys(":0\<C-R>=@r\<CR>1\<C-R>\<C-O>r2\<C-R>\<C-R>r3\<Esc>", 'xt')
call assert_equal([
\ '0',
\ '0a',

View File

@@ -905,7 +905,7 @@ func Test_cmdline_paste()
" Test for pasting register containing CTRL-H using CTRL-R and CTRL-R CTRL-R
let @a = "xy\<C-H>z"
call feedkeys(":\"\<C-R>a\<CR>", 'xt')
call feedkeys(":\"\<C-R>=@a\<CR>\<CR>", 'xt')
call assert_equal('"xz', @:)
call feedkeys(":\"\<C-R>\<C-R>a\<CR>", 'xt')
call assert_equal("\"xy\<C-H>z", @:)

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'],
@@ -820,7 +820,8 @@ func s:GetFilenameChecks() abort
\ 'tal': ['file.tal'],
\ 'taskdata': ['pending.data', 'completed.data', 'undo.data'],
\ 'taskedit': ['file.task'],
\ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc', '.tclsh-history', '.xsctcmdhistory', '.xsdbcmdhistory'],
\ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc', '.tclsh-history',
\ '.xsctcmdhistory', '.xsdbcmdhistory', 'vivado.jou', 'vivado.log'],
\ 'teal': ['file.tl'],
\ 'templ': ['file.templ'],
\ 'template': ['file.tmpl'],
@@ -928,7 +929,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'],
@@ -2659,6 +2660,21 @@ func Test_inc_file()
call assert_equal('bitbake', &filetype)
bwipe!
call writefile(['PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"'], 'Xfile.inc')
split Xfile.inc
call assert_equal('bitbake', &filetype)
bwipe!
call writefile(['MACHINEOVERRIDES =. "qemuall:"'], 'Xfile.inc')
split Xfile.inc
call assert_equal('bitbake', &filetype)
bwipe!
call writefile(['BBPATH .= ":${LAYERDIR}"'], 'Xfile.inc')
split Xfile.inc
call assert_equal('bitbake', &filetype)
bwipe!
" asm
call writefile(['asmsyntax=foo'], 'Xfile.inc')
split Xfile.inc

View File

@@ -1523,7 +1523,7 @@ func Test_complete_reginsert()
exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>"
call assert_equal('a123', getline(5))
let @r = "\<C-P>\<C-P>"
exe "normal GCa\<C-P>\<C-R>r"
exe "normal GCa\<C-P>\<C-R>=@r\<CR>"
call assert_equal('a12', getline(5))
exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>"
call assert_equal('a1234x', getline(5))