Compare commits

...

57 Commits

Author SHA1 Message Date
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
Jan Edmund Lazo
582e0714b5 vim-patch:8.1.1752: resizing hashtable is inefficient (#35563)
Problem:    Resizing hashtable is inefficient.
Solution:   Avoid resizing when the final size is predictable.

7b73d7ebf7

Co-authored-by: Bram Moolenaar <Bram@vim.org>
2025-08-31 05:29:43 +00:00
zeertzjq
6a330f893b fix(lua): report error in Lua Funcref callback properly (#35555) 2025-08-31 06:44:23 +08:00
glepnir
1b3abfa688 docs(lsp): mention lsp/after/ in faq #35534 2025-08-30 10:31:16 -07:00
Volodymyr Chernetskyi
c333d64663 vim-patch:9.1.1709: filetype: kyaml files are not recognized
Problem:  filetype: kyaml files are not recognized
Solution: Detect *.kyml files as yaml filetype
          (Volodymyr Chernetskyi)

References:
- https://kubernetes.io/blog/2025/08/27/kubernetes-v1-34-release/#alpha-support-for-kyaml-a-kubernetes-dialect-of-yaml

closes: vim/vim#18158

d5c89cc6bb

Co-authored-by: Volodymyr Chernetskyi <19735328+chernetskyi@users.noreply.github.com>
2025-08-30 11:58:00 +02:00
Jan Edmund Lazo
b3d29f396d vim-patch:8.1.1259: crash when exiting early (#35552)
Problem:    Crash when exiting early. (Ralf Schandl)
Solution:   Only pop/push the title when it was set. (closes vim/vim#4334)

e5c83286bb

Co-authored-by: Bram Moolenaar <Bram@vim.org>
2025-08-30 13:48:24 +08:00
Jan Edmund Lazo
0c0ef489f9 vim-patch:8.1.1136: decoding of mouse click escape sequence is not tested (#35551)
Problem:    Decoding of mouse click escape sequence is not tested.
Solution:   Add a test for xterm and SGR using low-level input.  Make
            low-level input execution with feedkeys() work.

905dd905de

Co-authored-by: Bram Moolenaar <Bram@vim.org>
2025-08-30 05:39:43 +00:00
zeertzjq
4edeaaa6e2 vim-patch:partial:9.1.1708: tests: various tests can be improved (#35548)
Problem:  tests: various tests can be improved
Solution: Use string interpolation to concatenate strings in
          test_winfixbuf, check for specific errors in assert_fails()
          (Yegappan Lakshmanan)

closes: vim/vim#18151

97ea879b9b

Cherry-pick Test_file_perm.vim changes from patch 9.0.{0363,0611}.
Skip Test_colornames_assignment_and_unassignment().

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
2025-08-30 08:55:48 +08:00
zeertzjq
6eebf30a39 vim-patch:7239d95: runtime(vim): Update base syntax, match :defer command argument (#35547)
closes: vim/vim#18159

7239d95bf2

Co-authored-by: Doug Kearns <dougkearns@gmail.com>
2025-08-30 08:15:01 +08:00
zeertzjq
8a2587be23 Merge pull request #35546 from zeertzjq/vim-a07a2f4
vim-patch:a07a2f4: runtime(astro): catch json_decode() error when parsing tsconfig.json
2025-08-30 08:14:15 +08:00
zeertzjq
834e1181da Merge pull request #35545 from zeertzjq/vim-9.1.1687
vim-patch:9.1.{1687,1693,1697,1713}: m4 ft detection updates
2025-08-30 07:22:13 +08:00
zeertzjq
68a2e0ef78 vim-patch:9.1.1713: filetype: fvwm2m4 files are no longer detected
Problem:  filetype: fvwm2m4 files are no longer recognized
          (after 9.1.1687).
Solution: Add a special case in m4 filetype detection (zeertzjq).

closes: vim/vim#18146

5355e81868

Co-authored-by: Damien Lejay <damien@lejay.be>
2025-08-30 06:57:09 +08:00
zeertzjq
61712cbc3a vim-patch:9.1.1697: tests: no test for aclocal.m4
Problem:  tests: no test for aclocal.m4
          (after v9.1.1693)
Solution: Add a test that aclocal.m4 is detected as config filetype

related: vim/vim#18065

f5670a1596

Co-authored-by: Christian Brabandt <cb@256bit.org>
2025-08-30 06:57:09 +08:00
zeertzjq
8a1afac653 vim-patch:9.1.1693: tests: test_filetype fails in shadow dir
Problem:  tests: test_filetype fails in shadow dir
          (after v9.1.9.1.1687)
Solution: Use a custom test that does not rely on configure.ac
          being existing in the upper directory tree.

de6e560150

Co-authored-by: Christian Brabandt <cb@256bit.org>
2025-08-30 06:57:09 +08:00
zeertzjq
dab31a3637 vim-patch:9.1.1687: filetype: autoconf filetype not always correct
Problem:  filetype: autoconf filetype not always correct
Solution: Detect aclocal.m4 as config filetype, detect configure.ac as
          config filetype, fall back to POSIX m4 (Damien Lejay).

closes: vim/vim#18065

2b55474f0a

Co-authored-by: Damien Lejay <damien@lejay.be>
2025-08-30 06:57:08 +08:00
bfredl
772f1966a3 Merge pull request #31400 from vanaigr/decor-provider-range
feat(decor): add range-based highlighting
2025-08-29 10:33:15 +02:00
zeertzjq
19f2e5c3eb vim-patch:b760062: runtime(debversions): Move bullseye, focal, and oracular to "unsupported" (#35531)
These versions have exited their standard support term as of
- bullseye: 2024-08-14
- focal: 2025-05
- oracular: 2025-07-10

closes: vim/vim#18134

b760062897

Co-authored-by: James McCoy <jamessan@jamessan.com>
2025-08-29 07:23:43 +08:00
Siddhant Agarwal
7a71235399 fix(server): serverlist({peer=true}) does not find peer servers #35506 2025-08-28 06:41:31 -07:00
Shadman
bc6737250d fix(progress): simplify ui-event, introduce default presentation #35527
Problem:
`msg_show` has "progress" info (title, status, percent) which is not presented
by default.

Solution:
Format TUI messages as `{title}: {msg}...{percent}%`. This also gets sent to UI.

- With specific formatting sent to UI we can remove the `progress` item from
  `msg_show` event. It can be added if needed in the future. Also, having
  a default presentation makes the feature more useful.
- For `vim._extui` we just need to implement the replace-msg-with-same-id
  behavior.
- If any UI/plugin wants to do anything fancier, they can handle the `Progress`
  event.
2025-08-28 06:33:41 -07:00
vanaigr
5edbabdbec perf: add on_range in treesitter highlighting 2025-08-28 08:22:38 -05:00
vanaigr
118e7e7111 test: add treesitter long lines benchmark 2025-08-28 08:22:38 -05:00
Christian Clason
c10e36fc01 refactor(lua): consistent use of local aliases 2025-08-28 11:34:01 +02:00
132 changed files with 4331 additions and 1958 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

@@ -825,8 +825,9 @@ will be set to zero, but can be changed and used for the replacing cmdline or
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, progress] ~
Display a message to the user.
["msg_show", kind, content, replace_last, history, append, msg_id] ~
Display a message to the user. Update (replace) any existing message
matching `msg_id`.
kind
Name indicating the message kind:
@@ -886,18 +887,6 @@ must handle.
Unique identifier for the message. It can either be an integer or
string. When message of same id appears it should replace the older message.
progress
Progress-message properties:
• title: Title string of the progress message.
• status: Status of the progress message. Can contain one of
the following values
• success: The progress item completed successfully
• running: The progress is ongoing
• failed: The progress item failed
• cancel: The progressing process should be canceled.
• percent: How much progress is done on the progress
message
["msg_clear"] ~
Clear all messages currently displayed by "msg_show", emitted after
clearing the screen (messages sent by other "msg_" events below should

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
@@ -3290,11 +3309,12 @@ nvim_set_decoration_provider({ns_id}, {opts})
Note: this function should not be called often. Rather, the callbacks
themselves can be used to throttle unneeded callbacks. the `on_start`
callback can return `false` to disable the provider until the next redraw.
Similarly, return `false` in `on_win` will skip the `on_line` calls for
that window (but any extmarks set in `on_win` will still be used). A
plugin managing multiple sources of decoration should ideally only set one
provider, and merge the sources internally. You can use multiple `ns_id`
for the extmarks set/modified inside the callback anyway.
Similarly, return `false` in `on_win` will skip the `on_line` and
`on_range` calls for that window (but any extmarks set in `on_win` will
still be used). A plugin managing multiple sources of decoration should
ideally only set one provider, and merge the sources internally. You can
use multiple `ns_id` for the extmarks set/modified inside the callback
anyway.
Note: doing anything other than setting extmarks is considered
experimental. Doing things like changing options are not explicitly
@@ -3302,8 +3322,8 @@ nvim_set_decoration_provider({ns_id}, {opts})
consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is
quite dubious for the moment.
Note: It is not allowed to remove or update extmarks in `on_line`
callbacks.
Note: It is not allowed to remove or update extmarks in `on_line` or
`on_range` callbacks.
Attributes: ~
Lua |vim.api| only
@@ -3323,9 +3343,16 @@ 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
is end-exclusive and may span multiple lines. Range bounds
point to the first byte of a character. An end position of
the form (lnum, 0), including (number of lines, 0), is
valid and indicates that EOL of the preceding line is
included. >
["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
<
• on_end: called at the end of a redraw cycle >
["end", tick]
@@ -3652,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

@@ -162,7 +162,7 @@ order of increasing priority:
1. Configuration defined for the `'*'` name.
2. Configuration from the result of merging all tables returned by
`lsp/<name>.lua` files in 'runtimepath' for a server of name `name`.
`lsp/<config>.lua` files in 'runtimepath' for the config named `<config>`.
3. Configurations defined anywhere else.
Example: given the following configs... >lua
@@ -280,6 +280,12 @@ FAQ *lsp-faq*
" (async = false is the default for format)
autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false })
<
- Q: How to avoid my own lsp/ folder being overridden?
- A: Place your configs under "after/lsp/". Files in "after/lsp/" are loaded
after those in "nvim/lsp/", so your settings will take precedence over
the defaults provided by nvim-lspconfig. See also: |after-directory|
*lsp-vs-treesitter*
- Q: How do LSP, Treesitter and Ctags compare?
- A: LSP requires a client and language server. The language server uses
@@ -2223,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.
@@ -2240,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
@@ -2259,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`.
@@ -2371,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

@@ -39,6 +39,11 @@ TREESITTER
• todo
UI
• `progress` attribute removed form |ui-messages| msg_show event
==============================================================================
BREAKING CHANGES *news-breaking*
@@ -46,7 +51,7 @@ These changes may require adaptations in your config or plugins.
API
todo
Decoration provider has `on_range()` callback.
BUILD
@@ -63,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
@@ -113,6 +120,7 @@ TREESITTER
`metadata[capture_id].offset`. The offset will be applied in
|vim.treesitter.get_range()|, which should be preferred over reading
metadata directly for retrieving node ranges.
• |Query:iter_captures()| supports specifying starting and ending columns.
TUI
@@ -190,13 +198,13 @@ EVENTS
• |CmdlineLeavePre| triggered before preparing to leave the command line.
• New `append` paremeter for |ui-messages| `msg_show` event.
• New `msg_id` and `progress` paremeter for |ui-messages| `msg_show` event.
• New `msg_id` paremeter for |ui-messages| `msg_show` event.
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
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
@@ -236,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.
@@ -280,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:
@@ -1522,7 +1522,7 @@ them to parse text. See |vim.treesitter.query.parse()| for a working example.
• {has_combined_injections} (`boolean`) whether the query contains
combined injections
• {query} (`TSQuery`) userdata query object
• {iter_captures} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start: integer?, stop: integer?, opts: table?): fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
• {iter_captures} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start_row: integer?, end_row: integer?, opts: table?): fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
See |Query:iter_captures()|.
• {iter_matches} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start: integer?, stop: integer?, opts: table?): fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree`)
See |Query:iter_matches()|.
@@ -1693,7 +1693,7 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
• |vim.treesitter.query.get()|
*Query:iter_captures()*
Query:iter_captures({node}, {source}, {start}, {stop}, {opts})
Query:iter_captures({node}, {source}, {start_row}, {end_row}, {opts})
Iterates over all captures from all matches in {node}.
{source} is required if the query contains predicates; then the caller
@@ -1725,22 +1725,26 @@ Query:iter_captures({node}, {source}, {start}, {stop}, {opts})
contained predicates.
Parameters: ~
• {node} (`TSNode`) under which the search will occur
• {source} (`integer|string`) Source buffer or string to extract text
from
• {start} (`integer?`) Starting line for the search. Defaults to
`node:start()`.
• {stop} (`integer?`) Stopping line for the search (end-exclusive).
Defaults to `node:end_()`.
• {opts} (`table?`) Optional keyword arguments:
• max_start_depth (integer) if non-zero, sets the maximum
start depth for each match. This is used to prevent
traversing too deep into a tree.
• match_limit (integer) Set the maximum number of
in-progress matches (Default: 256).
• {node} (`TSNode`) under which the search will occur
• {source} (`integer|string`) Source buffer or string to extract
text from
• {start_row} (`integer?`) Starting line for the search. Defaults to
`node:start()`.
• {end_row} (`integer?`) Stopping line for the search (end-inclusive,
unless `stop_col` is provided). Defaults to
`node:end_()`.
• {opts} (`table?`) Optional keyword arguments:
• max_start_depth (integer) if non-zero, sets the maximum
start depth for each match. This is used to prevent
traversing too deep into a tree.
• match_limit (integer) Set the maximum number of
in-progress matches (Default: 256).
• start_col (integer) Starting column for the search.
• end_col (integer) Stopping column for the search
(end-exclusive).
Return: ~
(`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
(`fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
capture id, capture node, metadata, match, tree
*Query:iter_matches()*

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

@@ -3,6 +3,7 @@
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
" Last Change: 2024 Apr 21
" 2024 May 24 by Riley Bruins <ribru17@gmail.com> ('commentstring')
" 2025 Aug 29 by Vim project, add try/catch around json_decode(), #18141
if exists("b:did_ftplugin")
finish
@@ -52,13 +53,19 @@ function! s:CollectPathsFromConfig() abort
endif
endif
let paths_from_config = config_json
try
let paths_from_config = config_json
\ ->readfile()
\ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' })
\ ->join()
\ ->json_decode()
\ ->get('compilerOptions', {})
\ ->get('paths', {})
catch /^Vim\%((\a\+)\)\=:E491:/ " invalid json
let paths_from_config = {}
catch /^Vim\%((\a\+)\)\=:E474:/ " invalid json in Nvim
let paths_from_config = {}
endtry
if !empty(paths_from_config)
let b:astro_paths = paths_from_config

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

@@ -8,7 +8,7 @@ local M = {}
--- @param env? table<string,string|number>
--- @return string
local function system(cmd, silent, env)
if vim.fn.executable(cmd[1]) == 0 then
if fn.executable(cmd[1]) == 0 then
error(string.format('executable not found: "%s"', cmd[1]), 0)
end
@@ -651,10 +651,10 @@ function M.init_pager()
-- Raw manpage into (:Man!) overlooks `match('man://')` condition,
-- so if the buffer already exists, create new with a non existing name.
if vim.fn.bufexists(man_bufname) == 1 then
if fn.bufexists(man_bufname) == 1 then
local new_bufname = man_bufname
for i = 1, 100 do
if vim.fn.bufexists(new_bufname) == 0 then
if fn.bufexists(new_bufname) == 0 then
break
end
new_bufname = ('%s?new=%s'):format(man_bufname, i)

View File

@@ -148,7 +148,7 @@ end
---@return fun(line: string): string
local function make_comment_function(parts, indent)
local prefix, nonindent_start, suffix = indent .. parts.left, indent:len() + 1, parts.right
local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(parts.right)
local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(suffix)
return function(line)
if is_blank(line) then

View File

@@ -8,9 +8,10 @@ local M = {}
--- @param listed string[] Already listed servers
--- @return string[] # List of servers found on the current machine in stdpath("run").
function M.serverlist(listed)
local root = vim.fs.normalize(vim.fn.stdpath('run') .. '/..')
local socket_paths = vim.fs.find(function(name, _)
return name:match('nvim.*')
end, { path = vim.fn.stdpath('run'), type = 'socket', limit = math.huge })
end, { path = root, type = 'socket', limit = math.huge })
local found = {} ---@type string[]
for _, socket in ipairs(socket_paths) do

View File

@@ -2128,7 +2128,7 @@ function vim.api.nvim_set_current_win(window) end
--- Note: this function should not be called often. Rather, the callbacks
--- themselves can be used to throttle unneeded callbacks. the `on_start`
--- callback can return `false` to disable the provider until the next redraw.
--- Similarly, return `false` in `on_win` will skip the `on_line` calls
--- Similarly, return `false` in `on_win` will skip the `on_line` and `on_range` calls
--- for that window (but any extmarks set in `on_win` will still be used).
--- A plugin managing multiple sources of decoration should ideally only set
--- one provider, and merge the sources internally. You can use multiple `ns_id`
@@ -2140,7 +2140,7 @@ function vim.api.nvim_set_current_win(window) end
--- Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
--- for the moment.
---
--- Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
--- Note: It is not allowed to remove or update extmarks in `on_line` or `on_range` callbacks.
---
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
--- @param opts vim.api.keyset.set_decoration_provider Table of callbacks:
@@ -2157,11 +2157,18 @@ 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]
--- ```
--- - on_range: called for each buffer range being redrawn.
--- Range is end-exclusive and may span multiple lines. Range
--- bounds point to the first byte of a character. An end position
--- of the form (lnum, 0), including (number of lines, 0), is valid
--- and indicates that EOL of the preceding line is included.
--- ```
--- ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
--- ```
--- - on_end: called at the end of a redraw cycle
--- ```
--- ["end", tick]

View File

@@ -382,6 +382,7 @@ error('Cannot require a meta file')
--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean?
--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean?
--- @field on_range? fun(_: "range", winid: integer, bufnr: integer, start_row: integer, start_col: integer, end_row: integer, end_col: integer): boolean?
--- @field on_end? fun(_: "end", tick: integer)
--- @field _on_hl_def? fun(_: "hl_def")
--- @field _on_spell_nav? fun(_: "spell_nav")

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

@@ -1229,7 +1229,7 @@ function M.config(opts, namespace)
if float_opts then
float_opts = type(float_opts) == 'table' and float_opts or {}
opts.jump.on_jump = function(_, bufnr)
jump_opts.on_jump = function(_, bufnr)
M.open_float(vim.tbl_extend('keep', float_opts, {
bufnr = bufnr,
scope = 'cursor',
@@ -1697,7 +1697,7 @@ M.handlers.underline = {
bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
if not vim.api.nvim_buf_is_loaded(bufnr) then
if not api.nvim_buf_is_loaded(bufnr) then
return
end
@@ -1724,7 +1724,7 @@ M.handlers.underline = {
end
local lines =
vim.api.nvim_buf_get_lines(diagnostic.bufnr, diagnostic.lnum, diagnostic.lnum + 1, true)
api.nvim_buf_get_lines(diagnostic.bufnr, diagnostic.lnum, diagnostic.lnum + 1, true)
vim.hl.range(
bufnr,
@@ -1795,7 +1795,7 @@ M.handlers.virtual_text = {
bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
if not vim.api.nvim_buf_is_loaded(bufnr) then
if not api.nvim_buf_is_loaded(bufnr) then
return
end
@@ -2491,7 +2491,7 @@ function M.open_float(opts, ...)
local location = info.location
local file_name = vim.fs.basename(vim.uri_to_fname(location.uri))
local info_suffix = ': ' .. info.message
related_info_locations[#lines + 1] = info.location
related_info_locations[#lines + 1] = location
lines[#lines + 1] = string.format(
'%s%s:%s:%s%s',
default_pre,
@@ -2881,11 +2881,11 @@ function M.status(bufnr)
return result_str
end
vim.api.nvim_create_autocmd('DiagnosticChanged', {
group = vim.api.nvim_create_augroup('nvim.diagnostic.status', {}),
api.nvim_create_autocmd('DiagnosticChanged', {
group = api.nvim_create_augroup('nvim.diagnostic.status', {}),
callback = function(ev)
if vim.api.nvim_buf_is_loaded(ev.buf) then
vim.api.nvim__redraw({ buf = ev.buf, statusline = true })
if api.nvim_buf_is_loaded(ev.buf) then
api.nvim__redraw({ buf = ev.buf, statusline = true })
end
end,
desc = 'diagnostics component for the statusline',

View File

@@ -734,13 +734,10 @@ local extension = {
luau = 'luau',
lrc = 'lyrics',
m = detect.m,
at = 'm4',
at = 'config',
mc = detect.mc,
quake = 'm3quake',
m4 = function(path, _bufnr)
local pathl = path:lower()
return not (pathl:find('html%.m4$') or pathl:find('fvwm2rc')) and 'm4' or nil
end,
m4 = detect.m4,
eml = 'mail',
mk = detect.make,
mak = detect.make,
@@ -1395,6 +1392,8 @@ local extension = {
yaml = 'yaml',
eyaml = 'yaml',
mplstyle = 'yaml',
kyaml = 'yaml',
kyml = 'yaml',
grc = detect_line1('<%?xml', 'xml', 'yaml'),
yang = 'yang',
yuck = 'yuck',
@@ -2237,6 +2236,7 @@ local pattern = {
},
['/%.'] = {
['/%.aws/credentials$'] = 'confini',
['/%.aws/cli/alias$'] = 'confini',
['/%.gitconfig%.d/'] = starsetf('gitconfig'),
['/%.gnupg/gpg%.conf$'] = 'gpg',
['/%.gnupg/options$'] = 'gpg',
@@ -2245,6 +2245,7 @@ local pattern = {
['/%.pinforc$'] = 'pinfo',
['/%.cargo/credentials$'] = 'toml',
['/%.init/.*%.override$'] = 'upstart',
['/%.kube/kuberc$'] = 'yaml',
},
['calendar/'] = {
['/%.calendar/'] = starsetf('calendar'),
@@ -2347,7 +2348,7 @@ local pattern = {
},
['require'] = {
['%-requirements%.txt$'] = 'requirements',
['requirements%-.*%.txt$'] = 'requirements',
['^requirements%-.*%.txt$'] = 'requirements',
['^requirements/.*%.txt$'] = 'requirements',
['^requires/.*%.txt$'] = 'requirements',
},
@@ -2493,7 +2494,6 @@ local pattern = {
end
end,
['^hg%-editor%-.*%.txt$'] = 'hgcommit',
['%.html%.m4$'] = 'htmlm4',
['^JAM.*%.'] = starsetf('jam'),
['^Prl.*%.'] = starsetf('jam'),
['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc',
@@ -2530,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

@@ -688,10 +688,7 @@ function M.fvwm_v1(_, _)
end
--- @type vim.filetype.mapfn
function M.fvwm_v2(path, _)
if fn.fnamemodify(path, ':e') == 'm4' then
return 'fvwm2m4'
end
function M.fvwm_v2(_, _)
return 'fvwm', function(bufnr)
vim.b[bufnr].fvwm_version = 2
end
@@ -1026,6 +1023,50 @@ function M.m(_, bufnr)
end
end
--- For files ending in *.m4, distinguish:
--- *.html.m4 files
--- - *fvwm2rc*.m4 files
--- files in the Autoconf M4 dialect
--- files in POSIX M4
--- @type vim.filetype.mapfn
function M.m4(path, bufnr)
local fname = fn.fnamemodify(path, ':t')
path = fn.fnamemodify(path, ':p:h')
if fname:find('html%.m4$') then
return 'htmlm4'
end
if fname:find('fvwm2rc') then
return 'fvwm2m4'
end
-- Canonical Autoconf file
if fname == 'aclocal.m4' then
return 'config'
end
-- Repo heuristic for Autoconf M4 (nearby configure.ac)
if
fn.filereadable(path .. '/../configure.ac') ~= 0
or fn.filereadable(path .. '/configure.ac') ~= 0
then
return 'config'
end
-- Content heuristic for Autoconf M4 (scan first ~200 lines)
-- Signals:
-- - Autoconf macro prefixes: AC_/AM_/AS_/AU_/AT_
for _, line in ipairs(getlines(bufnr, 1, 200)) do
if line:find('^%s*A[CMSUT]_') then
return 'config'
end
end
-- Default to POSIX M4
return 'm4'
end
--- @param contents string[]
--- @return string?
local function m4(contents)

View File

@@ -190,9 +190,9 @@ function M.dir(path, opts)
if
opts.depth
and level < opts.depth
and (t == 'directory' or (t == 'link' and opts.follow and (vim.uv.fs_stat(
M.joinpath(path, f)
) or {}).type == 'directory'))
and (t == 'directory' or (t == 'link' and opts.follow and (
uv.fs_stat(M.joinpath(path, f)) or {}
).type == 'directory'))
and (not opts.skip or opts.skip(f) ~= false)
then
dirs[#dirs + 1] = { f, level + 1 }
@@ -369,7 +369,7 @@ function M.find(names, opts)
if
type_ == 'directory'
or (type_ == 'link' and opts.follow and (vim.uv.fs_stat(f) or {}).type == 'directory')
or (type_ == 'link' and opts.follow and (uv.fs_stat(f) or {}).type == 'directory')
then
dirs[#dirs + 1] = f
end

View File

@@ -72,7 +72,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
0,
}
local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
local buf_line_count = api.nvim_buf_line_count(bufnr)
pos1[2] = math.min(pos1[2], buf_line_count)
pos2[2] = math.min(pos2[2], buf_line_count)
@@ -182,8 +182,8 @@ function M.on_yank(opts)
local higroup = opts.higroup or 'IncSearch'
local bufnr = vim.api.nvim_get_current_buf()
local winid = vim.api.nvim_get_current_win()
local bufnr = api.nvim_get_current_buf()
local winid = api.nvim_get_current_win()
if yank_timer and not yank_timer:is_closing() then
yank_timer:close()
@@ -191,7 +191,7 @@ function M.on_yank(opts)
yank_hl_clear()
end
vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
api.nvim__ns_set(yank_ns, { wins = { winid } })
yank_timer, yank_hl_clear = M.range(bufnr, yank_ns, higroup, "'[", "']", {
regtype = event.regtype,
inclusive = true,

View File

@@ -458,7 +458,7 @@ end
--- @return F
local function track(stat, f)
return function(...)
local start = vim.uv.hrtime()
local start = uv.hrtime()
local r = { f(...) }
stats[stat] = stats[stat] or { total = 0, time = 0 }
stats[stat].total = stats[stat].total + 1

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'
@@ -1376,7 +1377,7 @@ function lsp.formatexpr(opts)
local response =
client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
if response and response.result then
lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding)
util.apply_text_edits(response.result, bufnr, client.offset_encoding)
return 0
end
end

View File

@@ -104,7 +104,7 @@ local function incremental_changes(state, encoding, bufnr, firstline, lastline,
local line_ending = vim.lsp._buf_get_line_ending(bufnr)
local incremental_change = sync.compute_diff(
state.lines,
prev_lines,
curr_lines,
firstline,
lastline,

View File

@@ -249,7 +249,7 @@ function State:new(bufnr)
group = self.augroup,
pattern = 'foldexpr',
callback = function()
if vim.v.option_type == 'global' or vim.api.nvim_get_current_buf() == bufnr then
if vim.v.option_type == 'global' or api.nvim_get_current_buf() == bufnr then
vim.lsp._capability.enable('folding_range', false, { bufnr = bufnr })
end
end,

View File

@@ -443,7 +443,7 @@ function M.signature_help(config)
local buf, win = util.open_floating_preview(lines, 'markdown', config)
if hl then
vim.api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1)
api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1)
vim.hl.range(
buf,
sig_help_ns,
@@ -1072,7 +1072,7 @@ end
--- @param opts? vim.lsp.WorkspaceDiagnosticsOpts
--- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics
function M.workspace_diagnostics(opts)
vim.validate('opts', opts, 'table', true)
validate('opts', opts, 'table', true)
lsp.diagnostic._workspace_diagnostics(opts or {})
end
@@ -1435,7 +1435,7 @@ function M.selection_range(direction)
lsp.buf_request(
0,
ms.textDocument_selectionRange,
method,
params,
---@param response lsp.SelectionRange[]?
function(err, response)

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?
@@ -871,7 +874,7 @@ function Client:stop(force)
self._is_stopping = true
local rpc = self.rpc
vim.lsp._watchfiles.cancel(self.id)
lsp._watchfiles.cancel(self.id)
if force or not self.initialized or self._graceful_shutdown_failed then
rpc.terminate()
@@ -921,7 +924,7 @@ function Client:_register(registrations)
for _, reg in ipairs(registrations) do
local method = reg.method
if method == ms.workspace_didChangeWatchedFiles then
vim.lsp._watchfiles.register(reg, self.id)
lsp._watchfiles.register(reg, self.id)
elseif not self:_supports_registration(method) then
unsupported[#unsupported + 1] = method
end
@@ -955,7 +958,7 @@ function Client:_unregister(unregistrations)
self:_unregister_dynamic(unregistrations)
for _, unreg in ipairs(unregistrations) do
if unreg.method == ms.workspace_didChangeWatchedFiles then
vim.lsp._watchfiles.unregister(unreg, self.id)
lsp._watchfiles.unregister(unreg, self.id)
end
end
end
@@ -1096,10 +1099,10 @@ function Client:on_attach(bufnr)
-- on_attach and LspAttach callbacks the ability to schedule wrap the
-- opt-out (deleting the semanticTokensProvider from capabilities)
vim.schedule(function()
for _, Capability in pairs(vim.lsp._capability.all) do
for _, Capability in pairs(lsp._capability.all) do
if
self:supports_method(Capability.method)
and vim.lsp._capability.is_enabled(Capability.name, {
and lsp._capability.is_enabled(Capability.name, {
bufnr = bufnr,
client_id = self.id,
})
@@ -1220,10 +1223,10 @@ function Client:_on_detach(bufnr)
})
end
for _, Capability in pairs(vim.lsp._capability.all) do
for _, Capability in pairs(lsp._capability.all) do
if
self:supports_method(Capability.method)
and vim.lsp._capability.is_enabled(Capability.name, {
and lsp._capability.is_enabled(Capability.name, {
bufnr = bufnr,
client_id = self.id,
})
@@ -1266,7 +1269,7 @@ local function reset_defaults(bufnr)
end
vim._with({ buf = bufnr }, function()
local keymap = vim.fn.maparg('K', 'n', false, true)
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
if keymap and keymap.callback == lsp.buf.hover and keymap.buffer == 1 then
vim.keymap.del('n', 'K', { buffer = bufnr })
end
end)

View File

@@ -864,7 +864,7 @@ end
--- - findstart=0: column where the completion starts, or -2 or -3
--- - findstart=1: list of matches (actually just calls |complete()|)
function M._omnifunc(findstart, base)
vim.lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base })
lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base })
assert(base) -- silence luals
local bufnr = api.nvim_get_current_buf()
local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion })

View File

@@ -498,7 +498,7 @@ function M._workspace_diagnostics(opts)
local function handler(error, result, ctx)
-- Check for retrigger requests on cancellation errors.
-- Unless `retriggerRequest` is explicitly disabled, try again.
if error ~= nil and error.code == lsp.protocol.ErrorCodes.ServerCancelled then
if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then
if error.data == nil or error.data.retriggerRequest ~= false then
local client = assert(lsp.get_client_by_id(ctx.client_id))
client:request(ms.workspace_diagnostic, ctx.params, handler)

View File

@@ -450,7 +450,7 @@ function M.color_presentation()
end
vim.list_extend(text_edits, choice.additionalTextEdits or {})
lsp.util.apply_text_edits(text_edits, bufnr, client.offset_encoding)
util.apply_text_edits(text_edits, bufnr, client.offset_encoding)
end)
end)
end

View File

@@ -177,7 +177,7 @@ function M.get(filter)
--- @param buf integer
vim.tbl_map(function(buf)
vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter)))
end, vim.api.nvim_list_bufs())
end, api.nvim_list_bufs())
return hints
else
bufnr = vim._resolve_bufnr(bufnr)

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 = current.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

@@ -225,13 +225,13 @@ end
---@param message_type lsp.MessageType
function log._from_lsp_level(message_type)
if message_type == protocol.MessageType.Error then
return vim.log.levels.ERROR
return log_levels.ERROR
elseif message_type == protocol.MessageType.Warning then
return vim.log.levels.WARN
return log_levels.WARN
elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
return vim.log.levels.INFO
return log_levels.INFO
else
return vim.log.levels.DEBUG
return log_levels.DEBUG
end
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

@@ -526,11 +526,11 @@ local function merge_dispatchers(dispatchers)
---@type vim.lsp.rpc.Dispatchers
local merged = {
notification = (
dispatchers.notification and vim.schedule_wrap(dispatchers.notification)
dispatchers.notification and schedule_wrap(dispatchers.notification)
or default_dispatchers.notification
),
on_error = (
dispatchers.on_error and vim.schedule_wrap(dispatchers.on_error)
dispatchers.on_error and schedule_wrap(dispatchers.on_error)
or default_dispatchers.on_error
),
on_exit = dispatchers.on_exit or default_dispatchers.on_exit,

View File

@@ -368,7 +368,7 @@ end
--- @param hl_group string
--- @param priority integer
local function set_mark(bufnr, ns, token, hl_group, priority)
vim.api.nvim_buf_set_extmark(bufnr, ns, token.line, token.start_col, {
api.nvim_buf_set_extmark(bufnr, ns, token.line, token.start_col, {
hl_group = hl_group,
end_line = token.end_line,
end_col = token.end_col,

View File

@@ -159,7 +159,7 @@ local function compute_start_range(
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
--- Convert to 0 based for input, and from 0 based for output
char_idx = vim.str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1
char_idx = str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1
end
-- Return the start difference (shared for new and prev lines)

View File

@@ -595,7 +595,7 @@ function M.rename(old_fname, new_fname, opts)
opts = opts or {}
local skip = not opts.overwrite or opts.ignoreIfExists
local old_fname_full = vim.uv.fs_realpath(vim.fs.normalize(old_fname, { expand_env = false }))
local old_fname_full = uv.fs_realpath(vim.fs.normalize(old_fname, { expand_env = false }))
if not old_fname_full then
vim.notify('Invalid path: ' .. old_fname, vim.log.levels.ERROR)
return
@@ -869,7 +869,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
active_offset = { offset - 1, offset + #parameter_label - 1 }
break
end
offset = offset + #param.label + 1
offset = offset + #plabel + 1
end
end
if parameter.documentation then

View File

@@ -365,7 +365,7 @@ local function trigger_event(p, event_name, kind)
spec.version = spec.version or (uv.fs_stat(p.path) and git_get_default_branch(p.path))
local data = { kind = kind, spec = spec, path = p.path }
vim.api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data })
api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data })
end
--- @param title string

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

@@ -80,8 +80,6 @@ function TSQueryCursor:next_match() end
--- @param node TSNode
--- @param query TSQuery
--- @param start integer?
--- @param stop integer?
--- @param opts? { max_start_depth?: integer, match_limit?: integer}
--- @param opts? { start_row: integer, start_col: integer, end_row: integer, end_col: integer, max_start_depth?: integer, match_limit?: integer }
--- @return TSQueryCursor
function vim._create_ts_querycursor(node, query, start, stop, opts) end
function vim._create_ts_querycursor(node, query, opts) end

View File

@@ -343,7 +343,7 @@ function M.inspect_tree(opts)
local win = api.nvim_get_current_win()
local treeview, err = TSTreeView:new(buf, opts.lang)
if err and err:match('no parser for lang') then
vim.api.nvim_echo({ { err, 'WarningMsg' } }, true, {})
api.nvim_echo({ { err, 'WarningMsg' } }, true, {})
return
elseif not treeview then
error(err)
@@ -627,7 +627,7 @@ function M.edit_query(lang)
local base_buf = base_win and api.nvim_win_get_buf(base_win)
local inspect_win = base_buf and vim.b[base_buf].dev_inspect
if base_win and base_buf and api.nvim_win_is_valid(inspect_win) then
vim.api.nvim_set_current_win(inspect_win)
api.nvim_set_current_win(inspect_win)
buf = base_buf
win = base_win
cmd = 'new'

View File

@@ -9,7 +9,7 @@ function M.check()
health.info(
string.format(
'Treesitter ABI support: min %d, max %d',
vim.treesitter.minimum_language_version,
ts.minimum_language_version,
ts.language_version
)
)

View File

@@ -1,10 +1,11 @@
local api = vim.api
local query = vim.treesitter.query
local Range = require('vim.treesitter._range')
local cmp_lt = Range.cmp_pos.lt
local ns = api.nvim_create_namespace('nvim.treesitter.highlighter')
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree
---@class (private) vim.treesitter.highlighter.Query
---@field private _query vim.treesitter.Query?
@@ -57,6 +58,7 @@ end
---@class (private) vim.treesitter.highlighter.State
---@field tstree TSTree
---@field next_row integer
---@field next_col integer
---@field iter vim.treesitter.highlighter.Iter?
---@field highlighter_query vim.treesitter.highlighter.Query
---@field prev_marks MarkInfo[]
@@ -233,6 +235,7 @@ function TSHighlighter:prepare_highlight_states(win, srow, erow)
table.insert(self._highlight_states[win], {
tstree = tstree,
next_row = 0,
next_col = 0,
iter = nil,
highlighter_query = hl_query,
prev_marks = {},
@@ -331,27 +334,36 @@ end
---Queues the remainder if the mark continues after the line.
---@param m MarkInfo
---@param buf integer
---@param line integer
---@param range_start_row integer
---@param range_start_col integer
---@param range_end_row integer
---@param range_end_col integer
---@param next_marks MarkInfo[]
local function add_mark(m, buf, line, next_marks)
local function add_mark(
m,
buf,
range_start_row,
range_start_col,
range_end_row,
range_end_col,
next_marks
)
local cur_start_l = m.start_line
local cur_start_c = m.start_col
if cur_start_l < line then
cur_start_l = line
cur_start_c = 0
if cmp_lt(cur_start_l, cur_start_c, range_start_row, range_start_col) then
cur_start_l = range_start_row
cur_start_c = range_start_col
end
local cur_opts = m.opts
if cur_opts.end_line >= line + 1 then
if cmp_lt(range_end_row, range_end_col, cur_opts.end_line, cur_opts.end_col) then
cur_opts = vim.deepcopy(cur_opts, true)
cur_opts.end_line = line + 1
cur_opts.end_col = 0
cur_opts.end_line = range_end_row
cur_opts.end_col = range_end_col
table.insert(next_marks, m)
end
local empty = cur_opts.end_line < cur_start_l
or (cur_opts.end_line == cur_start_l and cur_opts.end_col <= cur_start_c)
if cur_start_l <= line and not empty then
if cmp_lt(cur_start_l, cur_start_c, cur_opts.end_line, cur_opts.end_col) then
api.nvim_buf_set_extmark(buf, ns, cur_start_l, cur_start_c, cur_opts)
end
end
@@ -359,17 +371,44 @@ end
---@param self vim.treesitter.highlighter
---@param win integer
---@param buf integer
---@param line integer
---@param range_start_row integer
---@param range_start_col integer
---@param range_end_row integer
---@param range_end_col integer
---@param on_spell boolean
---@param on_conceal boolean
local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
self._conceal_checked[line] = self._conceal_line and true or nil
local function on_range_impl(
self,
win,
buf,
range_start_row,
range_start_col,
range_end_row,
range_end_col,
on_spell,
on_conceal
)
if self._conceal_line then
range_start_col = 0
if range_end_col ~= 0 then
range_end_row = range_end_row + 1
range_end_col = 0
end
end
for i = range_start_row, range_end_row - 1 do
self._conceal_checked[i] = self._conceal_line or nil
end
self:for_each_highlight_state(win, function(state)
local root_node = state.tstree:root()
local root_start_row, _, root_end_row, _ = root_node:range()
---@type { [1]: integer, [2]: integer, [3]: integer, [4]: integer }
local root_range = { root_node:range() }
-- Only consider trees that contain this line
if root_start_row > line or root_end_row < line then
if
not Range.intercepts(
root_range,
{ range_start_row, range_start_col, range_end_row, range_end_col }
)
then
return
end
@@ -378,85 +417,114 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
local next_marks = {}
for _, mark in ipairs(state.prev_marks) do
add_mark(mark, buf, line, next_marks)
add_mark(
mark,
buf,
range_start_row,
range_start_col,
range_end_row,
range_end_col,
next_marks
)
end
if state.iter == nil or state.next_row < line then
local next_row = state.next_row
local next_col = state.next_col
if state.iter == nil or cmp_lt(next_row, next_col, range_start_row, range_start_col) then
-- Mainly used to skip over folds
-- TODO(lewis6991): Creating a new iterator loses the cached predicate results for query
-- matches. Move this logic inside iter_captures() so we can maintain the cache.
state.iter =
state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
state.iter = state.highlighter_query:query():iter_captures(
root_node,
self.bufnr,
range_start_row,
root_range[3],
{ start_col = range_start_col, end_col = root_range[4] }
)
end
local captures = state.highlighter_query:query().captures
while line >= state.next_row do
local capture, node, metadata, match = state.iter(line)
local outer_range = { root_end_row + 1, 0, root_end_row + 1, 0 }
if node then
outer_range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
while cmp_lt(next_row, next_col, range_end_row, range_end_col) do
local capture, node, metadata, match = state.iter(range_end_row, range_end_col)
if not node then
next_row = math.huge
next_col = math.huge
break
end
local outer_range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
if cmp_lt(next_row, next_col, outer_range[1], outer_range[2]) then
next_row = outer_range[1]
next_col = outer_range[2]
end
if not capture then
break
end
local outer_range_start_row = outer_range[1]
for _, range in ipairs(tree_region) do
local intersection = Range.intersection(range, outer_range)
if intersection then
local start_row, start_col, end_row, end_col = Range.unpack4(intersection)
if capture then
local hl = state.highlighter_query:get_hl_from_capture(capture)
local hl = state.highlighter_query:get_hl_from_capture(capture)
local capture_name = captures[capture]
local capture_name = captures[capture]
local spell, spell_pri_offset = get_spell(capture_name)
local spell, spell_pri_offset = get_spell(capture_name)
-- The "priority" attribute can be set at the pattern level or on a particular capture
local priority = (
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
or vim.hl.priorities.treesitter
) + spell_pri_offset
-- The "priority" attribute can be set at the pattern level or on a particular capture
local priority = (
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
or vim.hl.priorities.treesitter
) + spell_pri_offset
-- The "conceal" attribute can be set at the pattern level or on a particular capture
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
-- The "conceal" attribute can be set at the pattern level or on a particular capture
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
local url = get_url(match, buf, capture, metadata)
local url = get_url(match, buf, capture, metadata)
if hl and end_row >= line and not on_conceal and (not on_spell or spell ~= nil) then
local opts = {
end_line = end_row,
end_col = end_col,
hl_group = hl,
ephemeral = true,
priority = priority,
conceal = conceal,
spell = spell,
url = url,
}
local mark = { start_line = start_row, start_col = start_col, opts = opts }
add_mark(mark, buf, line, next_marks)
end
if hl and not on_conceal and (not on_spell or spell ~= nil) then
local opts = {
end_line = end_row,
end_col = end_col,
hl_group = hl,
ephemeral = true,
priority = priority,
conceal = conceal,
spell = spell,
url = url,
}
local mark = { start_line = start_row, start_col = start_col, opts = opts }
add_mark(
mark,
buf,
range_start_row,
range_start_col,
range_end_row,
range_end_col,
next_marks
)
end
if
(metadata.conceal_lines or metadata[capture] and metadata[capture].conceal_lines)
and #api.nvim_buf_get_extmarks(buf, ns, { start_row, 0 }, { start_row, 0 }, {}) == 0
then
api.nvim_buf_set_extmark(buf, ns, start_row, 0, {
end_line = end_row,
conceal_lines = '',
})
end
if
(metadata.conceal_lines or metadata[capture] and metadata[capture].conceal_lines)
and #api.nvim_buf_get_extmarks(buf, ns, { start_row, 0 }, { start_row, 0 }, {}) == 0
then
api.nvim_buf_set_extmark(buf, ns, start_row, 0, {
end_line = end_row,
conceal_lines = '',
})
end
end
end
if outer_range_start_row > line then
state.next_row = outer_range_start_row
end
end
state.next_row = next_row
state.next_col = next_col
state.prev_marks = next_marks
end)
end
@@ -464,14 +532,17 @@ end
---@private
---@param win integer
---@param buf integer
---@param line integer
function TSHighlighter._on_line(_, win, buf, line, _)
---@param br integer
---@param bc integer
---@param er integer
---@param ec integer
function TSHighlighter._on_range(_, win, buf, br, bc, er, ec, _)
local self = TSHighlighter.active[buf]
if not self then
return
end
on_line_impl(self, win, buf, line, false, false)
on_range_impl(self, win, buf, br, bc, er, ec, false, false)
end
---@private
@@ -490,9 +561,7 @@ function TSHighlighter._on_spell_nav(_, win, buf, srow, _, erow, _)
local highlight_states = self._highlight_states[win]
self:prepare_highlight_states(win, srow, erow)
for row = srow, erow do
on_line_impl(self, win, buf, row, true, false)
end
on_range_impl(self, win, buf, srow, 0, erow, 0, true, false)
self._highlight_states[win] = highlight_states
end
@@ -510,7 +579,7 @@ function TSHighlighter._on_conceal_line(_, win, buf, row)
local highlight_states = self._highlight_states[win]
self.tree:parse({ row, row })
self:prepare_highlight_states(win, row, row)
on_line_impl(self, win, buf, row, false, true)
on_range_impl(self, win, buf, row, 0, row + 1, 0, false, true)
self._highlight_states[win] = highlight_states
end
@@ -554,7 +623,7 @@ end
api.nvim_set_decoration_provider(ns, {
on_win = TSHighlighter._on_win,
on_line = TSHighlighter._on_line,
on_range = TSHighlighter._on_range,
_on_spell_nav = TSHighlighter._on_spell_nav,
_on_conceal_line = TSHighlighter._on_conceal_line,
})

View File

@@ -188,7 +188,7 @@ end
--- Returns available treesitter languages.
function M._complete()
local parsers = vim.api.nvim_get_runtime_file('parser/*', true)
local parsers = api.nvim_get_runtime_file('parser/*', true)
local parser_names_set = {} ---@type table<string, boolean>
for _, parser in ipairs(parsers) do
local parser_name = vim.fn.fnamemodify(parser, ':t:r')

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

@@ -4,6 +4,7 @@
local api = vim.api
local language = require('vim.treesitter.language')
local memoize = vim.func._memoize
local cmp_ge = require('vim.treesitter._range').cmp_pos.ge
local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
local EXTENDS_FORMAT = '^;+%s*extends%s*$'
@@ -951,18 +952,20 @@ end
---
---@param node TSNode under which the search will occur
---@param source (integer|string) Source buffer or string to extract text from
---@param start? integer Starting line for the search. Defaults to `node:start()`.
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
---@param start_row? integer Starting line for the search. Defaults to `node:start()`.
---@param end_row? integer Stopping line for the search (end-inclusive, unless `stop_col` is provided). Defaults to `node:end_()`.
---@param opts? table Optional keyword arguments:
--- - max_start_depth (integer) if non-zero, sets the maximum start depth
--- for each match. This is used to prevent traversing too deep into a tree.
--- - match_limit (integer) Set the maximum number of in-progress matches (Default: 256).
--- - start_col (integer) Starting column for the search.
--- - end_col (integer) Stopping column for the search (end-exclusive).
---
---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree):
---@return (fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree):
--- capture id, capture node, metadata, match, tree
---
---@note Captures are only returned if the query pattern of a specific capture contained predicates.
function Query:iter_captures(node, source, start, stop, opts)
function Query:iter_captures(node, source, start_row, end_row, opts)
opts = opts or {}
opts.match_limit = opts.match_limit or 256
@@ -970,17 +973,24 @@ function Query:iter_captures(node, source, start, stop, opts)
source = api.nvim_get_current_buf()
end
start, stop = value_or_node_range(start, stop, node)
start_row, end_row = value_or_node_range(start_row, end_row, node)
local tree = node:tree()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
local cursor = vim._create_ts_querycursor(node, self.query, {
start_row = start_row,
start_col = opts.start_col or 0,
end_row = end_row,
end_col = opts.end_col or 0,
max_start_depth = opts.max_start_depth,
match_limit = opts.match_limit or 256,
})
-- For faster checks that a match is not in the cache.
local highest_cached_match_id = -1
---@type table<integer, vim.treesitter.query.TSMetadata>
local match_cache = {}
local function iter(end_line)
local function iter(end_line, end_col)
local capture, captured_node, match = cursor:next_capture()
if not capture then
@@ -1005,9 +1015,22 @@ function Query:iter_captures(node, source, start, stop, opts)
local predicates = processed_pattern.predicates
if not self:_match_predicates(predicates, pattern_i, captures, source) then
cursor:remove_match(match_id)
if end_line and captured_node:range() > end_line then
local row, col = captured_node:range()
local outside = false
if end_line then
if end_col then
outside = cmp_ge(row, col, end_line, end_col)
else
outside = row > end_line
end
end
if outside then
return nil, captured_node, nil, nil
end
return iter(end_line) -- tail call: try next match
end
@@ -1072,7 +1095,14 @@ function Query:iter_matches(node, source, start, stop, opts)
start, stop = value_or_node_range(start, stop, node)
local tree = node:tree()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
local cursor = vim._create_ts_querycursor(node, self.query, {
start_row = start,
start_col = 0,
end_row = stop,
end_col = 0,
max_start_depth = opts.max_start_depth,
match_limit = opts.match_limit or 256,
})
local function iter()
local match = cursor:next_match()

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

@@ -1,30 +1,32 @@
" Vim syntax file
" Language: Debian version information
" Maintainer: Debian Vim Maintainers
" Last Change: 2025 Apr 24
" Last Change: 2025 Aug 26
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/shared/debversions.vim
let s:cpo = &cpo
set cpo-=C
" Version names that are upcoming or released and still within the standard support window
let g:debSharedSupportedVersions = [
\ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy',
\ 'bullseye', 'bookworm', 'trixie', 'forky', 'duke',
\ 'bookworm', 'trixie', 'forky', 'duke',
\
\ 'focal', 'jammy', 'noble', 'oracular', 'plucky', 'questing',
\ 'jammy', 'noble', 'plucky', 'questing',
\ 'devel'
\ ]
" Historic version names, no longer under standard support
let g:debSharedUnsupportedVersions = [
\ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato',
\ 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy',
\ 'jessie', 'stretch', 'buster',
\ 'jessie', 'stretch', 'buster', 'bullseye',
\
\ 'warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty',
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'trusty', 'utopic', 'vivid', 'wily', 'xenial', 'yakkety', 'zesty',
\ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'groovy',
\ 'hirsute', 'impish', 'kinetic', 'lunar', 'mantic',
\ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'focal', 'groovy',
\ 'hirsute', 'impish', 'kinetic', 'lunar', 'mantic', 'oracular',
\ ]
let &cpo=s:cpo

View File

@@ -232,7 +232,7 @@ syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=@vi
syn case match
" All vimCommands are contained by vimIsCommand. {{{2
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutocmd,vimAugroup,vimBehave,vimCall,vimCatch,vimCommandModifier,vimConst,vimDoautocmd,vimDebuggreedy,vimDef,vimDefFold,vimDelcommand,vimDelFunction,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelpgrep,vimHighlight,vimImport,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSynColor,vimSynLink,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutocmd,vimAugroup,vimBehave,vimCall,vimCatch,vimCommandModifier,vimConst,vimDoautocmd,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelpgrep,vimHighlight,vimImport,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSynColor,vimSynLink,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
syn cluster vim9CmdList contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
syn match vimCmdSep "\\\@1<!|" skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc
syn match vimCmdSep ":\+" skipwhite nextgroup=@vimCmdList,vimSubst1
@@ -300,6 +300,10 @@ syn match vimCall "\<call\=\>" skipwhite nextgroup=vimVar,@vimFunc
" TODO: special-cased until generalised range/count support is implemented
syn match vimDebuggreedy "\<0\=debugg\%[reedy]\>" contains=vimCount
" Defer {{{2
" =====
syn match vimDefer "\<defer\=\>" skipwhite nextgroup=@vimFunc
" Exception Handling {{{2
syn keyword vimThrow th[row] skipwhite nextgroup=@vimExprList
syn keyword vimCatch cat[ch] skipwhite nextgroup=vimCatchPattern
@@ -2240,6 +2244,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimDef vimCommand
hi def link vimDefBang vimBang
hi def link vimDefComment vim9Comment
hi def link vimDefer vimCommand
hi def link vimDefParam vimVar
hi def link vimDelcommand vimCommand
hi def link vimDelcommandAttr vimUserCmdAttr

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"
@@ -421,7 +422,7 @@ for _, k in ipairs(keysets) do
local function typename(type)
if type == 'HLGroupID' then
return 'kObjectTypeInteger'
elseif not type or vim.startswith(type, 'Union') then
elseif not type or startswith(type, 'Union') then
return 'kObjectTypeNil'
elseif type == 'StringArray' then
return 'kUnpackTypeStringArray'

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',
@@ -591,7 +607,7 @@ local function render_fields_or_params(xs, generics, classes, cfg)
inline_type(p, classes)
local nm, ty = p.name, p.type
local desc = p.classvar and string.format('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc
local desc = p.classvar and fmt('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc
local fnm = p.kind == 'operator' and fmt('op(%s)', nm) or fmt_field_name(nm)
local pnm = fmt(' • %-' .. indent .. 's', fnm)
@@ -1071,7 +1087,7 @@ local function gen_target(cfg)
for _, f in ipairs(cfg.section_order) do
local section = sections[f]
if section then
print(string.format(" Rendering section: '%s'", section.title))
print(fmt(" Rendering section: '%s'", section.title))
local add_sep_and_header = not vim.tbl_contains(cfg.append_only or {}, f)
docs[#docs + 1] = render_section(section, add_sep_and_header)
end

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

@@ -1004,7 +1004,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// Note: this function should not be called often. Rather, the callbacks
/// themselves can be used to throttle unneeded callbacks. the `on_start`
/// callback can return `false` to disable the provider until the next redraw.
/// Similarly, return `false` in `on_win` will skip the `on_line` calls
/// Similarly, return `false` in `on_win` will skip the `on_line` and `on_range` calls
/// for that window (but any extmarks set in `on_win` will still be used).
/// A plugin managing multiple sources of decoration should ideally only set
/// one provider, and merge the sources internally. You can use multiple `ns_id`
@@ -1016,7 +1016,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
/// for the moment.
///
/// Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
/// Note: It is not allowed to remove or update extmarks in `on_line` or `on_range` callbacks.
///
/// @param ns_id Namespace id from |nvim_create_namespace()|
/// @param opts Table of callbacks:
@@ -1033,11 +1033,18 @@ 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]
/// ```
/// - on_range: called for each buffer range being redrawn.
/// Range is end-exclusive and may span multiple lines. Range
/// bounds point to the first byte of a character. An end position
/// of the form (lnum, 0), including (number of lines, 0), is valid
/// and indicates that EOL of the preceding line is included.
/// ```
/// ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
/// ```
/// - on_end: called at the end of a redraw cycle
/// ```
/// ["end", tick]
@@ -1061,6 +1068,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
{ "on_buf", &opts->on_buf, &p->redraw_buf },
{ "on_win", &opts->on_win, &p->redraw_win },
{ "on_line", &opts->on_line, &p->redraw_line },
{ "on_range", &opts->on_range, &p->redraw_range },
{ "on_end", &opts->on_end, &p->redraw_end },
{ "_on_hl_def", &opts->_on_hl_def, &p->hl_def },
{ "_on_spell_nav", &opts->_on_spell_nav, &p->spell_nav },

View File

@@ -18,6 +18,8 @@ typedef struct {
LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
*Boolean) on_win;
LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
LuaRefOf(("range" _, Integer winid, Integer bufnr, Integer start_row, Integer start_col,
Integer end_row, Integer end_col), *Boolean) on_range;
LuaRefOf(("end" _, Integer tick)) on_end;
LuaRefOf(("hl_def" _)) _on_hl_def;
LuaRefOf(("spell_nav" _)) _on_spell_nav;

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

@@ -165,7 +165,7 @@ void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append,
Object id, Dict progress)
Object id)
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;

View File

@@ -332,7 +332,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
if (!dangerous) {
ex_normal_busy++;
}
exec_normal(true);
exec_normal(true, lowlevel);
if (!dangerous) {
ex_normal_busy--;
}
@@ -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

@@ -513,7 +513,7 @@ static void decor_state_pack(DecorState *state)
state->future_begin = fut_beg;
}
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
void decor_redraw_line(win_T *wp, int row, DecorState *state)
{
decor_state_pack(state);
@@ -527,7 +527,11 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
state->row = row;
state->col_until = -1;
state->eol_col = -1;
}
// Checks if there are (likely) more decorations on the current line.
bool decor_has_more_decorations(DecorState *state, int row)
{
if (state->current_end != 0 || state->future_begin != (int)kv_size(state->ranges_i)) {
return true;
}

View File

@@ -149,6 +149,7 @@ typedef struct {
LuaRef redraw_buf;
LuaRef redraw_win;
LuaRef redraw_line;
LuaRef redraw_range;
LuaRef redraw_end;
LuaRef hl_def;
LuaRef spell_nav;

View File

@@ -25,7 +25,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
{ ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, -1, false, false, 0 }
static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg)
@@ -189,6 +189,30 @@ void decor_providers_invoke_line(win_T *wp, int row)
decor_state.running_decor_provider = false;
}
void decor_providers_invoke_range(win_T *wp, int start_row, int start_col, int end_row, int end_col)
{
decor_state.running_decor_provider = true;
for (size_t i = 0; i < kv_size(decor_providers); i++) {
DecorProvider *p = &kv_A(decor_providers, i);
if (p->state == kDecorProviderActive && p->redraw_range != LUA_NOREF) {
MAXSIZE_TEMP_ARRAY(args, 6);
ADD_C(args, WINDOW_OBJ(wp->handle));
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
ADD_C(args, INTEGER_OBJ(start_row));
ADD_C(args, INTEGER_OBJ(start_col));
ADD_C(args, INTEGER_OBJ(end_row));
ADD_C(args, INTEGER_OBJ(end_col));
if (!decor_provider_invoke((int)i, "range", p->redraw_range, args, true)) {
// return 'false' or error: skip rest of this window
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
}
hl_check_ns();
}
}
decor_state.running_decor_provider = false;
}
/// For each provider invoke the 'buf' callback for a given buffer.
///
/// @param buf Buffer
@@ -272,6 +296,7 @@ void decor_provider_clear(DecorProvider *p)
NLUA_CLEAR_REF(p->redraw_buf);
NLUA_CLEAR_REF(p->redraw_win);
NLUA_CLEAR_REF(p->redraw_line);
NLUA_CLEAR_REF(p->redraw_range);
NLUA_CLEAR_REF(p->redraw_end);
NLUA_CLEAR_REF(p->spell_nav);
NLUA_CLEAR_REF(p->conceal_line);

View File

@@ -1142,6 +1142,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
// Not drawing text when line is concealed or drawing filler lines beyond last line.
const bool draw_text = !concealed && (lnum != buf->b_ml.ml_line_count + 1);
int decor_provider_end_col;
bool check_decor_providers = false;
if (col_rows == 0 && draw_text) {
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
@@ -1163,14 +1166,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
}
}
decor_providers_invoke_line(wp, lnum - 1); // may invalidate wp->w_virtcol
validate_virtcol(wp);
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
if (has_decor) {
extra_check = true;
}
check_decor_providers = true;
// Check for columns to display for 'colorcolumn'.
wlv.color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
@@ -1466,22 +1462,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
// 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
// first character to be displayed.
const int start_col = wp->w_p_wrap
? (startrow == 0 ? wp->w_skipcol : 0)
: wp->w_leftcol;
const int start_vcol = wp->w_p_wrap
? (startrow == 0 ? wp->w_skipcol : 0)
: wp->w_leftcol;
if (has_foldtext) {
wlv.vcol = start_col;
} else if (start_col > 0 && col_rows == 0) {
wlv.vcol = start_vcol;
} else if (start_vcol > 0 && col_rows == 0) {
char *prev_ptr = ptr;
CharSize cs = { 0 };
CharsizeArg csarg;
CSType cstype = init_charsize_arg(&csarg, wp, lnum, line);
csarg.max_head_vcol = start_col;
csarg.max_head_vcol = start_vcol;
int vcol = wlv.vcol;
StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
while (vcol < start_col && *ci.ptr != NUL) {
while (vcol < start_vcol && *ci.ptr != NUL) {
cs = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg);
vcol += cs.width;
prev_ptr = ci.ptr;
@@ -1518,23 +1514,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
// - the visual mode is active, or
// - drawing a fold
// the end of the line may be before the start of the displayed part.
if (wlv.vcol < start_col && (wp->w_p_cuc
|| wlv.color_cols
|| virtual_active(wp)
|| (VIsual_active && wp->w_buffer == curwin->w_buffer)
|| has_fold)) {
wlv.vcol = start_col;
if (wlv.vcol < start_vcol && (wp->w_p_cuc
|| wlv.color_cols
|| virtual_active(wp)
|| (VIsual_active && wp->w_buffer == curwin->w_buffer)
|| has_fold)) {
wlv.vcol = start_vcol;
}
// Handle a character that's not completely on the screen: Put ptr at
// that character but skip the first few screen characters.
if (wlv.vcol > start_col) {
if (wlv.vcol > start_vcol) {
wlv.vcol -= charsize;
ptr = prev_ptr;
}
if (start_col > wlv.vcol) {
wlv.skip_cells = start_col - wlv.vcol - head;
if (start_vcol > wlv.vcol) {
wlv.skip_cells = start_vcol - wlv.vcol - head;
}
// Adjust for when the inverted text is before the screen,
@@ -1588,6 +1584,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
}
}
if (check_decor_providers) {
int const col = (int)(ptr - line);
decor_provider_end_col = decor_providers_setup(endrow - startrow,
start_vcol == 0,
lnum,
col,
wp);
line = ml_get_buf(wp->w_buffer, lnum);
ptr = line + col;
}
decor_redraw_line(wp, lnum - 1, &decor_state);
if (!has_decor && decor_has_more_decorations(&decor_state, lnum - 1)) {
has_decor = true;
extra_check = true;
}
// Correct highlighting for cursor that can't be disabled.
// Avoids having to check this for each character.
if (wlv.fromcol >= 0) {
@@ -1642,6 +1655,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
bool did_decrement_ptr = false;
// Get next chunk of extmark highlights if previous approximation was smaller than needed.
if (check_decor_providers && (int)(ptr - line) >= decor_provider_end_col) {
int const col = (int)(ptr - line);
decor_provider_end_col = invoke_range_next(wp, lnum, col, 100);
line = ml_get_buf(wp->w_buffer, lnum);
ptr = line + col;
if (!has_decor && decor_has_more_decorations(&decor_state, lnum - 1)) {
has_decor = true;
extra_check = true;
}
}
// Skip this quickly when working on the text.
if (draw_cols) {
if (cul_screenline) {
@@ -2740,7 +2765,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
// check if line ends before left margin
wlv.vcol = MAX(wlv.vcol, start_col + wlv.col - win_col_off(wp));
wlv.vcol = MAX(wlv.vcol, start_vcol + wlv.col - win_col_off(wp));
// Get rid of the boguscols now, we want to draw until the right
// edge for 'cursorcolumn'.
wlv.col -= wlv.boguscols;
@@ -2762,7 +2787,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
if (((wp->w_p_cuc
&& wp->w_virtcol >= vcol_hlc(wlv) - eol_hl_off
&& wp->w_virtcol < view_width * (ptrdiff_t)(wlv.row - startrow + 1) + start_col
&& wp->w_virtcol < view_width * (ptrdiff_t)(wlv.row - startrow + 1) + start_vcol
&& lnum != wp->w_cursor.lnum)
|| wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr
|| wlv.diff_hlf != 0 || wp->w_buffer->terminal)) {
@@ -3180,3 +3205,50 @@ static void wlv_put_linebuf(win_T *wp, const winlinevars_T *wlv, int endcol, boo
ScreenGrid *g = grid_adjust(grid, &row, &coloff);
grid_put_linebuf(g, row, coloff, startcol, endcol, clear_width, bg_attr, 0, wlv->vcol - 1, flags);
}
static int decor_providers_setup(int rows_to_draw, bool draw_from_line_start, linenr_T lnum,
colnr_T col, win_T *wp)
{
// Approximate the number of bytes that will be drawn.
// Assume we're dealing with 1-cell ascii and ignore
// the effects of 'linebreak', 'breakindent', etc.
int rem_vcols;
if (wp->w_p_wrap) {
int width = wp->w_view_width - win_col_off(wp);
int width2 = width + win_col_off2(wp);
int first_row_width = draw_from_line_start ? width : width2;
rem_vcols = first_row_width + (rows_to_draw - 1) * width2;
} else {
rem_vcols = wp->w_view_height - win_col_off(wp);
}
// Call it here since we need to invalidate the line pointer anyway.
decor_providers_invoke_line(wp, lnum - 1);
validate_virtcol(wp);
return invoke_range_next(wp, lnum, col, rem_vcols + 1);
}
/// @return New begin column, or INT_MAX.
static int invoke_range_next(win_T *wp, int lnum, colnr_T begin_col, colnr_T col_off)
{
char const *const line = ml_get_buf(wp->w_buffer, lnum);
int const line_len = ml_get_buf_len(wp->w_buffer, lnum);
col_off = MAX(col_off, 1);
colnr_T new_col;
if (col_off <= line_len - begin_col) {
int end_col = begin_col + col_off;
end_col += mb_off_next(line, line + end_col);
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum - 1, end_col);
validate_virtcol(wp);
new_col = end_col;
} else {
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum, 0);
validate_virtcol(wp);
new_col = INT_MAX;
}
return new_col;
}

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

@@ -7072,21 +7072,26 @@ void exec_normal_cmd(char *cmd, int remap, bool silent)
{
// Stuff the argument into the typeahead buffer.
ins_typebuf(cmd, remap, 0, true, silent);
exec_normal(false);
exec_normal(false, false);
}
/// Execute normal_cmd() until there is no typeahead left.
///
/// @param was_typed whether or not something was typed
void exec_normal(bool was_typed)
/// @param use_vpeekc true to use vpeekc() to check for available chars
void exec_normal(bool was_typed, bool use_vpeekc)
{
oparg_T oa;
int c;
// When calling vpeekc() from feedkeys() it will return Ctrl_C when there
// is nothing to get, so also check for Ctrl_C.
clear_oparg(&oa);
finish_op = false;
while ((!stuff_empty()
|| ((was_typed || !typebuf_typed())
&& typebuf.tb_len > 0))
&& typebuf.tb_len > 0)
|| (use_vpeekc && (c = vpeekc()) != NUL && c != Ctrl_C))
&& !got_int) {
update_topline_cursor();
normal_cmd(&oa, true); // execute a Normal mode cmd
@@ -7355,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.
@@ -7571,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

@@ -317,7 +317,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
// Use specified size.
minitems = MAX(minitems, ht->ht_used);
// array is up to 2/3 full
minsize = minitems * 3 / 2;
minsize = (minitems * 3 + 1) / 2;
}
size_t newsize = HT_INIT_SIZE;

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
@@ -1496,7 +1525,7 @@ int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typ
PUSH_ALL_TYPVALS(lstate, argvars, argcount, false);
if (nlua_pcall(lstate, argcount, 1)) {
nlua_print(lstate);
nlua_error(lstate, _("Lua callback: %.*s"));
return FCERR_OTHER;
}

View File

@@ -1361,34 +1361,46 @@ static int tslua_push_querycursor(lua_State *L)
TSQuery *query = query_check(L, 2);
TSQueryCursor *cursor = ts_query_cursor_new();
if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) {
luaL_argcheck(L, lua_istable(L, 3), 3, "table expected");
}
lua_getfield(L, 3, "start_row");
uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 3, "start_col");
uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 3, "end_row");
uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 3, "end_col");
uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1);
lua_pop(L, 1);
ts_query_cursor_set_point_range(cursor, (TSPoint){ start_row, start_col },
(TSPoint){ end_row, end_col });
lua_getfield(L, 3, "max_start_depth");
if (!lua_isnil(L, -1)) {
uint32_t max_start_depth = (uint32_t)luaL_checkinteger(L, -1);
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
}
lua_pop(L, 1);
lua_getfield(L, 3, "match_limit");
if (!lua_isnil(L, -1)) {
uint32_t match_limit = (uint32_t)luaL_checkinteger(L, -1);
ts_query_cursor_set_match_limit(cursor, match_limit);
}
lua_pop(L, 1);
ts_query_cursor_exec(cursor, query, node);
if (lua_gettop(L) >= 3) {
uint32_t start = (uint32_t)luaL_checkinteger(L, 3);
uint32_t end = lua_gettop(L) >= 4 ? (uint32_t)luaL_checkinteger(L, 4) : MAXLNUM;
ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 });
}
if (lua_gettop(L) >= 5 && !lua_isnil(L, 5)) {
luaL_argcheck(L, lua_istable(L, 5), 5, "table expected");
lua_pushnil(L); // [dict, ..., nil]
while (lua_next(L, 5)) {
// [dict, ..., key, value]
if (lua_type(L, -2) == LUA_TSTRING) {
char *k = (char *)lua_tostring(L, -2);
if (strequal("max_start_depth", k)) {
uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1);
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
} else if (strequal("match_limit", k)) {
uint32_t match_limit = (uint32_t)lua_tointeger(L, -1);
ts_query_cursor_set_match_limit(cursor, match_limit);
}
}
// pop the value; lua_next will pop the key.
lua_pop(L, 1); // [dict, ..., key]
}
}
TSQueryCursor **ud = lua_newuserdata(L, sizeof(*ud)); // [node, query, ..., udata]
*ud = cursor;
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); // [node, query, ..., udata, meta]

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

@@ -863,7 +863,7 @@ void free_all_mem(void)
// Close all tabs and windows. Reset 'equalalways' to avoid redraws.
p_ea = false;
if (first_tabpage->tp_next != NULL) {
if (first_tabpage != NULL && first_tabpage->tp_next != NULL) {
do_cmdline_cmd("tabonly!");
}
@@ -873,18 +873,20 @@ void free_all_mem(void)
// Clear user commands (before deleting buffers).
ex_comclear(NULL);
// Clear menus.
do_cmdline_cmd("aunmenu *");
do_cmdline_cmd("tlunmenu *");
do_cmdline_cmd("menutranslate clear");
if (curbuf != NULL) {
// Clear menus.
do_cmdline_cmd("aunmenu *");
do_cmdline_cmd("tlunmenu *");
do_cmdline_cmd("menutranslate clear");
// Clear mappings, abbreviations, breakpoints.
// NB: curbuf not used with local=false arg
map_clear_mode(curbuf, MAP_ALL_MODES, false, false);
map_clear_mode(curbuf, MAP_ALL_MODES, false, true);
do_cmdline_cmd("breakdel *");
do_cmdline_cmd("profdel *");
do_cmdline_cmd("set keymap=");
// Clear mappings, abbreviations, breakpoints.
// NB: curbuf not used with local=false arg
map_clear_mode(curbuf, MAP_ALL_MODES, false, false);
map_clear_mode(curbuf, MAP_ALL_MODES, false, true);
do_cmdline_cmd("breakdel *");
do_cmdline_cmd("profdel *");
do_cmdline_cmd("set keymap=");
}
free_titles();
free_findfile();
@@ -905,7 +907,9 @@ void free_all_mem(void)
free_cd_dir();
free_signs();
set_expr_line(NULL);
diff_clear(curtab);
if (curtab != NULL) {
diff_clear(curtab);
}
clear_sb_text(true); // free any scrollback text
// Free some global vars.
@@ -922,8 +926,10 @@ void free_all_mem(void)
// Close all script inputs.
close_all_scripts();
// Destroy all windows. Must come before freeing buffers.
win_free_all();
if (curwin != NULL) {
// Destroy all windows. Must come before freeing buffers.
win_free_all();
}
// Free all option values. Must come after closing windows.
free_all_options();
@@ -957,8 +963,10 @@ void free_all_mem(void)
reset_last_sourcing();
free_tabpage(first_tabpage);
first_tabpage = NULL;
if (first_tabpage != NULL) {
free_tabpage(first_tabpage);
first_tabpage = NULL;
}
// message history
msg_hist_clear(0);

View File

@@ -152,7 +152,6 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
// Extended msg state, currently used for external UIs with ext_messages
static const char *msg_ext_kind = NULL;
static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 0 };
static DictOf(Object) msg_ext_progress = ARRAY_DICT_INIT;
static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
static sattr_T msg_ext_last_attr = -1;
@@ -293,19 +292,67 @@ 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);
@@ -322,6 +369,17 @@ 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: 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++) {
HlMessageChunk chunk = kv_A(hl_msg, i);
@@ -332,6 +390,7 @@ 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 (history && kv_size(hl_msg)) {
msg_hist_add_multihl(id, hl_msg, false, msg_data);
}
@@ -340,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;
}
@@ -1105,19 +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;
if (strequal(msg_ext_kind, "progress") && msg_data != NULL && ui_has(kUIMessages)) {
kv_resize(msg_ext_progress, 3);
if (msg_data->title.size != 0) {
PUT_C(msg_ext_progress, "title", STRING_OBJ(msg_data->title));
}
if (msg_data->status.size != 0) {
PUT_C(msg_ext_progress, "status", STRING_OBJ(msg_data->status));
}
if (msg_data->percent >= 0) {
PUT_C(msg_ext_progress, "percent", INTEGER_OBJ(msg_data->percent));
}
}
msg_hist_clear(msg_hist_max);
}
@@ -1262,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);
}
}
@@ -2210,8 +2261,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
if (msg_silent != 0 || *str == NUL) {
if (*str == NUL && ui_has(kUIMessages)) {
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
INTEGER_OBJ(-1),
(Dict)ARRAY_DICT_INIT);
INTEGER_OBJ(-1));
}
return;
}
@@ -3239,7 +3289,7 @@ void msg_ext_ui_flush(void)
Array *tofree = msg_ext_init_chunks();
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
msg_ext_append, msg_ext_id, msg_ext_progress);
msg_ext_append, msg_ext_id);
// clear info after emiting message.
if (msg_ext_history) {
api_free_array(*tofree);
@@ -3260,7 +3310,6 @@ void msg_ext_ui_flush(void)
msg_ext_append = false;
msg_ext_kind = NULL;
msg_ext_id = INTEGER_OBJ(0);
kv_destroy(msg_ext_progress);
}
}

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

@@ -2453,7 +2453,9 @@ static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cook
void free_tag_stuff(void)
{
ga_clear_strings(&tag_fnames);
do_tag(NULL, DT_FREE, 0, 0, 0);
if (curwin != NULL) {
do_tag(NULL, DT_FREE, 0, 0, 0);
}
tag_freematch();
tagstack_clear_entry(&ptag_entry);

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

@@ -1053,7 +1053,9 @@ theend:
void ex_comclear(exarg_T *eap)
{
uc_clear(&ucmds);
uc_clear(&curbuf->b_ucmds);
if (curbuf != NULL) {
uc_clear(&curbuf->b_ucmds);
}
}
void free_ucmd(ucmd_T *cmd)

Some files were not shown because too many files have changed in this diff Show More