Compare commits

...

342 Commits

Author SHA1 Message Date
Sean Dewar
8c311386c3 fix(window): don't add a hsep when out of room if global stl is off
Problem: a horizontal separator may be added to a window that doesn't need one
if there is no room when moving a different window.

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

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

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

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

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

(cherry picked from commit e4a100a1e1)
2025-09-02 15:47:15 +00:00
Justin M. Keyes
c5262c4ca8 Merge pull request #35598 from neovim/backport-29073-to-release-0.11
docs: add guide for developing Lua plugins
2025-09-01 22:10:02 -04:00
Justin M. Keyes
dd8e3d7aa5 docs: Lua plugin development guide
(cherry picked from commit a5e7ccc329)
2025-09-01 21:56:58 -04:00
Marc Jakobi
a7491e1457 docs: Lua plugin development guide
(cherry picked from commit 28ab656122)
2025-09-01 23:53:57 +00:00
Justin M. Keyes
1063aff643 ci(release): link to release notes #35585
fix #35580
2025-09-01 11:30:51 -04:00
Wise Man
e415fae42e ci: Windows arm64 packages #35345
Problem:
Neovim binaries are not provided for Windows ARM64.
GitHub Actions now offer native CI runners for Windows on ARM devices
(windows-11-arm), enabling automated builds and testing.

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

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-09-01 11:17:37 -04:00
Justin M. Keyes
7b099d2b2b version bump 2025-08-31 15:29:56 -04:00
Justin M. Keyes
cec0ecabd8 NVIM v0.11.4
See runtime/doc/news.txt (or `:help news` in Nvim).

Following is a list of fix/feature commits in this release.

FEATURES
--------------------------------------------------------------------------------
- 5551da79c1 lsp: improve signature help display #35190
- abfbd155da provider: detect venv python via "pynvim-python" tool #35273

BUILD
--------------------------------------------------------------------------------
- 3343ee971b deps: CMake generation fails when path contains spaces #35332

FIXES
--------------------------------------------------------------------------------
- 27282696fe api: fix not capturing output in cmdline mode (#35322)
- 09b0003d38 api: nvim_create_user_command addr option should allow ranges #35077
- 3ab06d5188 api: on_detach consistently before buf_freeall autocmds (#35369)
- 53db7fc3ef api,lsp: call on_detach before wiping out the buffer #35367
- 30b801eff2 checkhealth: wrong ABI version for treesitter parsers #35327
- a05b70baa6 clipboard: correct blockwise register width computation (#35038)
- 990b320592 cmdline: :checkhealth completion with multiple args (#35060)
- a3590afba5 diff: set default diff flags properly (#35450)
- f4b4c27a35 float: cursor visible in "hidden" floating window (#35219)
- 3cf9dac2ba folds: error when deleting lines at end of buffer #35396
- d70d469c04 health: accept TERM=tmux-direct #35511
- edfb447ff8 health: update advice for Python #35564
- 359d65c902 iter: ArrayIter:last returns nil when filtered to empty #34697
- 44b8255fa2 lsp: close floating preview window correctly #34946
- 6b820258cd lsp: don't override `config.title` in `vim.lsp.buf.signature_help()` #35075
- 8f2d6f7ce2 lsp: show title when global winborder is set (#35181)
- 1bea812953 lsp: update window title when cycling through signatures #35407
- 9261aef2f3 lsp/health: always use vim.inspect to show root_markers (#34667)
- d185057bc7 lsp/health: ensure valid table before concatenating (#34930)
- 09f702bc13 lua: report error in Lua Funcref callback properly (#35555)
- 7f1e112a32 pum: check for cmdline mode properly
- 41fa343484 snippet: jumping backwards to choice node (#35062)
- 2e4baa3679 snippet: setting end_right_gravity (#35061)
- 4b957a4d18 treesitter: inconsistent highlight of multiline combined injection #35307
- fa64f2d09b treesitter: run FileType autocmds in the context of `<abuf>`
- e841d653af treesitter: show capture-level priorities in :Inspect #35443
- e299430ff5 tui: do not remove SIGWINCH handler when resize events are enabled (#35221) (#35238)
- 64afa93187 tutor: use `invalidate` field in `nvim_buf_set_extmark()`
- 2124146164 tutor: use legacy syntax for lesson 3.1 of vim-01-beginner.tutor
- 6fd842a4fd ui: check for cmdline mode properly

VIM PATCHES
--------------------------------------------------------------------------------
- e68d3ef886 5ddcecf: runtime(help): Add better support for language annotation highlighting
- 35a66f74c7 6fea0a5: runtime(help): Add Vim lang annotation support for codeblocks
- 819e545c28 714671d: runtime(misc): use :hor :term to ensure new term window is split horizontally (#35064)
- a65c4be2de 8.1.0425: ml_get error and crash with appendbufline()
- 8dd88056f1 8.2.1672: v_lock is used when it is not initialized (#35416)
- 6c2f06b537 9.1.0748: :keep* commmands are sometimes misidentified as :k
- 54c2ea142a 9.1.1599: :bnext doesn't go to unlisted help buffers (#35216)
- 53a0d99702 9.1.1601: Patch v8.1.0425 was wrong
- ced4eed733 9.1.1607: :apple command detected as :append (#35237)
- bd4b45dd1b 9.1.1608: No command-line completion for :unsilent {command}
- d21db345ef 9.1.1611: possible undefined behaviour in mb_decompose() (#35275)
- 744d96bd76 9.1.1612: Ctrl-G/Ctrl-T do not ignore the end search delimiter
- 5ec7d98857 9.1.1613: tests: test_search leaves a few swapfiles behind
- 39ae9a9971 9.1.1633: Search pattern shown incorrectly with negative offset (#35337)
- e6ea97a691 9.1.1665: Outdated comment in eval.c (#35436)
- 6fd8ba05a6 9.1.1667: Another outdated comment in eval.c (#35438)
- 4c5cb950c6 9.1.1688: potential buffer overrun in bufwrite.c (#35497)
- 99817471d7 9.1.1700: Multiline ignorecase specific pattern does not match with 'ignorecase' (#35520)
- fb6c677d57 b9ea0a8: runtime(doc): tweak documentation style in helphelp.txt
2025-08-31 15:25:45 -04:00
Michael Henry
edfb447ff8 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`.
(cherry picked from commit f311c96973)
2025-08-31 18:51:33 +00:00
zeertzjq
b032c2b53f Merge pull request #35561 from zeertzjq/backport
fix(lua): report error in Lua Funcref callback properly (#35555)
2025-08-31 07:37:42 +08:00
zeertzjq
09f702bc13 fix(lua): report error in Lua Funcref callback properly (#35555) 2025-08-31 07:13:00 +08:00
glepnir
c4845f3a12 docs(lsp): mention lsp/after/ in faq #35534
(cherry picked from commit 1b3abfa688)
2025-08-30 22:56:57 +00:00
zeertzjq
a2603016ce Merge pull request #35546 from zeertzjq/vim-a07a2f4
vim-patch:a07a2f4: runtime(astro): catch json_decode() error when parsing tsconfig.json
(cherry picked from commit 8a2587be23)
2025-08-30 00:55:38 +00:00
Meriel Luna Mittelbach
d70d469c04 fix(health): accept TERM=tmux-direct #35511
tmux-direct is functionally the same as tmux-256color, except it
directly reports 24-bit color and how to set them (setaf/setab)
via ncurses 6.x's extended terminfo format.

(cherry picked from commit a33284c2c0)
2025-08-28 04:25:15 +00:00
zeertzjq
99817471d7 vim-patch:9.1.1700: Multiline ignorecase specific pattern does not match with 'ignorecase' (#35520)
Problem:  a pattern that involves a backref on a different line does not
          match when 'ignorecase' is set (QiWei, after v9.1.0645)
Solution: Use MB_STRNICMP when ignorecase is set, fix tests to close
          swapfiles

related: vim/vim#14756
fixes: vim/vim#17470
closes: vim/vim#18104

bf82e58a70

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 24020ef2dd)
2025-08-27 23:56:49 +00:00
zeertzjq
4c5cb950c6 vim-patch:9.1.1688: potential buffer overrun in bufwrite.c (#35497)
Problem:  potential buffer overrun in bufwrite.c
Solution: Use a temporary variable (John Marriott)

In my Windows 11 Pro 64-bit build MAXPATHL is 1024 and IOSIZE is 1025.
In my Archlinux Linux 64-bit build MAXPATHL is 4096 and IOSIZE is 1025.

In funuction buf_write():
There is a check (line 713) that makes sure the length of fname is less
than MAXPATHL. There is a call to STRCPY() (line 1208) which copies the
string at fname into IObuff (which has size IOSIZE). For Unix builds
fname is set to sfname which may or may not be shorter. However, if
sfname is NULL sfname is set to fname.

Therefore, in builds where MAXPATHL > IOSIZE (eg in my linux build), it
is theoretically possible for the STRCPY() call to exceed the bounds of
IObuff.

This PR addresses this by copying fname into a local variable that has
the same maximum size as fname.

In addition:
Given that the filename is unconditionally overwritten in the for loop,
only copy the directory portion of fname. Move variable i closer to
where it is used.

closes: vim/vim#18095

a19b019b87

Co-authored-by: John Marriott <basilisk@internode.on.net>
(cherry picked from commit 4263ec21c2)
2025-08-27 03:15:15 +00:00
zeertzjq
ec8900f1e6 Merge pull request #35451 from zeertzjq/backport
fix(diff): set default diff flags properly (#35450)
2025-08-24 15:22:22 +08:00
zeertzjq
a3590afba5 fix(diff): set default diff flags properly (#35450) 2025-08-24 14:43:19 +08:00
Riley Bruins
e841d653af fix(treesitter): show capture-level priorities in :Inspect #35443
(cherry picked from commit 29c5559ce1)
2025-08-23 23:18:41 +00:00
zeertzjq
6fd8ba05a6 vim-patch:9.1.1667: Another outdated comment in eval.c (#35438)
Problem:  Another outdated comment in eval.c (after 9.1.1665).
Solution: Remove that comment as well. Add a few more tests for mapnew()
          that fail without patch 8.2.1672 (zeertzjq).

closes: vim/vim#18089

6b56711804
(cherry picked from commit 639f9f4cda)
2025-08-23 11:10:29 +00:00
zeertzjq
e6ea97a691 vim-patch:9.1.1665: Outdated comment in eval.c (#35436)
Problem:  Outdated comment in eval.c.
Solution: Remove the comment, which is no longer true after 8.2.1672.
          Also fix a typo in version9.txt (zeertzjq).

closes: vim/vim#18077

5d3c39af2a
(cherry picked from commit b9699d5701)
2025-08-23 00:46:12 +00:00
tao
3cf9dac2ba fix(folds): error when deleting lines at end of buffer #35396
Problem:
with `foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr()
foldminlines=0`, deleting lines at the end of the buffer always
reports an invalid top error, because the top value (i.e. the
start line number of the deletion) is always 1 greater than
the total line number of the modified buffer.

Solution:
remove the ml_line_count validation

(cherry picked from commit d73cfefed5)
2025-08-22 03:22:33 +00:00
zeertzjq
8dd88056f1 vim-patch:8.2.1672: v_lock is used when it is not initialized (#35416)
Problem:    v_lock is used when it is not initialized. (Yegappan Lakshmanan)
Solution:   Initialize the typval in eval1().

4a091b9978

Co-authored-by: Bram Moolenaar <Bram@vim.org>
(cherry picked from commit 865a28155e)
2025-08-21 23:02:40 +00:00
Tiago Inaba
1bea812953 fix(lsp): update window title when cycling through signatures #35407
(cherry picked from commit 848c7a7894)
2025-08-21 00:51:35 +00:00
Sean Dewar
fa64f2d09b fix(treesitter): run FileType autocmds in the context of <abuf>
Problem: many FileType autocommands assume curbuf is the same as the target
buffer; this can cause &syntax to be restored for the wrong buffer in some cases
when TSHighlighter:destroy is called.

Solution: run nvim_exec_autocmds in the context of the target buffer via
nvim_buf_call.

(cherry picked from commit 3ec63cdab8)
2025-08-19 19:52:41 +00:00
neovim-backports[bot]
3ab06d5188 fix(api): on_detach consistently before buf_freeall autocmds (#35369)
Problem: on_detach may be called after buf_freeall and other important things,
plus its textlock restrictions are insufficient. This can cause issues such as
leaks, internal errors and crashes.

Solution: disable buffer updates in buf_freeall, before autocommands (like the
order after #35355 and when do_ecmd reloads a buffer). Don't do so in
free_buffer_stuff; it's not safe to run user code there, and buf_freeall already
runs before then; just free them to avoid leaks if buf_freeall autocommands
registered more for some reason.

(cherry picked from commit 2211953266)

Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
2025-08-17 15:39:32 -07:00
neovim-backports[bot]
37b2d42459 refactor(tests): remove redundant test (#35368)
(cherry picked from commit 7eb9badd93)

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-08-17 15:14:31 -07:00
neovim-backports[bot]
53db7fc3ef fix(api,lsp): call on_detach before wiping out the buffer #35367
Problem:
Buffer-updates on_detach callback is invoked before buf_freeall(), which
deletes autocmds of the buffer (via apply_autocmds(EVENT_BUFWIPEOUT,
...)). Due to this, buffer-local autocmds executed in on_detach (e.g.,
LspDetach) are not actually invoked.

Solution:
Call buf_updates_unload() before buf_freeall().

(cherry picked from commit 285c04e2d0)

Co-authored-by: Jaehwang Jung <tomtomjhj@gmail.com>
2025-08-17 14:55:19 -07:00
MinimalEffort07
3343ee971b build(deps): CMake generation fails when path contains spaces #35332
Problem:
Additional include directories in DEPS_INCLUDE_FLAGS variable are not
quoted. Paths with spaces break the resulting compile command.

Solution:
Enclose values in double quotes.
Note: normally we should avoid manual quoting, but in this case we can't
because of how `DEPS_INCLUDE_FLAGS` is used in `BuildLuv.cmake`
and `BuildLpeg.cmake`.

(cherry picked from commit 77860f5418)
2025-08-16 23:46:28 +00:00
Michael Henry
abfbd155da feat(provider): detect venv python via "pynvim-python" tool #35273
Problem:
Detection of the pynvim module is currently done by finding the first
Python interpreter in the `PATH` and checking if it can import pynvim.
This has several problems:
- Activation of an unrelated Python virtual environment will break
  automatic detection, unless pynvim is also installed in that
  environment.
- Installing pynvim to the expected location is difficult. User
  installation into the system-wide or user-wide Python site area is now
  deprecated.  On Ubuntu 24.04 with Python 3.12, for example, the
  command `pip install --user pynvim` now fails with the error message
  `error: externally-managed-environment`.
- Users may create a dedicated virtual environment in which to install
  pynvim, but Nvim won't detect it; instead, they must either activate
  it before launching Nvim (which interferes with the user of other
  virtual environments) or else hard-code the variable
  `g:python3_host_prog` in their `init.vim` to the path of the correct
  Python interpreter.  Neither option is desirable.

Solution:
Expose pynvim's Python interpreter on the `PATH` under the
name `pynvim-python`.  Typical user-flow:

1. User installs either uv or pipx.
2. User installs pynvim via:
   ```
   uv tool install --upgrade pynvim
   # Or:
   pipx install --upgrade pynvim
   ```

With corresponding changes in pynvim https://github.com/neovim/pynvim/issues/593
the above user-flow is all that's needed for Nvim to detect the
installed location of pynvim, even if an unrelated Python virtual
environments is activated.  It uses standard Python tooling to automate
the necessary creation of a Python virtual environment for pyenv and the
publication of `pynvim-python` to a directory on `PATH`.

(cherry picked from commit 5f8d4a248a)
2025-08-16 22:32:55 +00:00
zeertzjq
39ae9a9971 vim-patch:9.1.1633: Search pattern shown incorrectly with negative offset (#35337)
Problem:  Search pattern shown incorrectly with negative offset.
          (lkintact)
Solution: Don't prepend a '+' sign to a negative offset (zeertzjq).

fixes: vim/vim#17993
closes: vim/vim#17994

ade0815856
(cherry picked from commit 8d154e5927)
2025-08-15 00:34:11 +00:00
zeertzjq
5ec7d98857 vim-patch:9.1.1613: tests: test_search leaves a few swapfiles behind
Problem:  tests: test_search leaves a few swapfiles behind
Solution: Use :bw! instead of :close to close the swapfile at the end of
          the test.

related: vim/vim#17933

a2bb21a895

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 9e8d551b1e)
2025-08-15 07:48:56 +08:00
zeertzjq
744d96bd76 vim-patch:9.1.1612: Ctrl-G/Ctrl-T do not ignore the end search delimiter
Problem:  Ctrl-G/Ctrl-T does not ignore the end search delimiter
          (irisjae)
Solution: Check if the pattern ends with a search delimiter and ignore
          it, unless it is part of the pattern.

fixes: vim/vim#17895
closes: vim/vim#17933

c03990d30f

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 1eca030fb2)
2025-08-15 07:48:56 +08:00
Jalil David Salamé Messina
30b801eff2 fix(checkhealth): wrong ABI version for treesitter parsers #35327
Don't print ABI version of duplicated parsers that are later in the
runtime path (see [#35326]).

Change the sorting from `name > path` to `name > rtpath_index`, this
ensures the first (loaded) parser is first in the list and any
subsequent parsers can be considered "not loaded".

This is fuzzy at best since `vim.treesitter.language.add` can take a
path to a parser and change the load order.

The correct solution is for `vim.treesitter.language.inspect` to return
the parser path so we can compare against it and/or for it to also be
able to take a path to a parser so we can inspect it without loading it
first.

(cherry picked from commit bd45e2be63)
2025-08-14 19:19:01 +00:00
zeertzjq
27282696fe fix(api): fix not capturing output in cmdline mode (#35322)
(cherry picked from commit 7b9512e613)
2025-08-13 12:40:07 +00:00
Steven Xu
4b957a4d18 fix(treesitter): inconsistent highlight of multiline combined injection #35307
Problem:
Combined injections not entirely highlighted.

Solution:
Reapply layer highlights on each line.

Co-authored-by: Artem <vanaigranov@gmail.com>
2025-08-12 14:14:29 -07:00
zeertzjq
819e545c28 vim-patch:714671d: runtime(misc): use :hor :term to ensure new term window is split horizontally (#35064)
Problem:  :term splits new window above in vim, but in nvim it change
          the buffer for current window
Solution: :hor term to ensure consistent splitting for Vim and Neovim

closes: vim/vim#17822

714671de35

Co-authored-by: phanium <91544758+phanen@users.noreply.github.com>
(cherry picked from commit 1f7432a272)
2025-08-10 23:17:58 +00:00
zeertzjq
d21db345ef vim-patch:9.1.1611: possible undefined behaviour in mb_decompose() (#35275)
Problem:  possible undefined behaviour in mb_decompose(), when using the
          same pointer as argument several times
Solution: use separate assignments to avoid reading and writing the same
          object at the same time (Áron Hárnási)

closes: vim/vim#17953

c43a0614d4

Co-authored-by: Áron Hárnási <aron.harnasi@gmail.com>
(cherry picked from commit 77500c5ad5)
2025-08-09 23:50:33 +00:00
zeertzjq
bd4b45dd1b vim-patch:9.1.1608: No command-line completion for :unsilent {command}
Problem:  No command-line completion for :unsilent {command}.
Solution: Add missing command arg completion (Doug Kearns).
          (author)

Add completion tests for all command modifiers.

closes: vim/vim#17524

126731c8fd

Co-authored-by: Doug Kearns <dougkearns@gmail.com>
(cherry picked from commit fe42c81f2e)
2025-08-09 01:25:25 +00:00
zeertzjq
6c2f06b537 vim-patch:9.1.0748: :keep* commmands are sometimes misidentified as :k
Problem:  The :keep{alt,jumps,marks,patterns} commmands are sometimes
          misidentified as :k.
Solution: Make sure one_letter_cmd() only returns true for :k and not
          other :keep* commands (Doug Kearns).

This currently manifests as missing completion for :keep* commands and
incorrect results from fullcommand().

E.g., fullcommand("keepmarks") returns "k" rather than "keepmarks".

The correct command, however, is executed as command modifiers are
handled specially in do_one_cmd() rather than using find_ex_command().

Fix exists(':k') so that it returns 2 for a full match.

closes: vim/vim#15742

ea84202372

Cherry-pick Test_ex_command_completion() from patch 9.1.0624.

Co-authored-by: Doug Kearns <dougkearns@gmail.com>
(cherry picked from commit 70bb7999f7)
2025-08-09 01:25:25 +00:00
brianhuster
fb6c677d57 vim-patch:b9ea0a8: runtime(doc): tweak documentation style in helphelp.txt
closes: vim/vim#16302

b9ea0a89fa

Co-authored-by: h-east <h.east.727@gmail.com>

I removed some parts that are not applicable to Nvim, like HelpTOC

(cherry picked from commit 7082367b3a)
2025-08-08 23:37:28 +00:00
brianhuster
e68d3ef886 vim-patch:5ddcecf: runtime(help): Add better support for language annotation highlighting
closes: vim/vim#16238

5ddcecf05f

Co-authored-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: h_east <h.east.727@gmail.com>
(cherry picked from commit 60b866049c)
2025-08-08 23:37:28 +00:00
brianhuster
35a66f74c7 vim-patch:6fea0a5: runtime(help): Add Vim lang annotation support for codeblocks
closes: vim/vim#16215

6fea0a5480

Co-authored-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
(cherry picked from commit 11ae879ebd)
2025-08-08 23:37:28 +00:00
zeertzjq
ced4eed733 vim-patch:9.1.1607: :apple command detected as :append (#35237)
Problem:  :apple command detected as :append (dai475694450)
Solution: Disallow to define a custom command with lower-case letter,
          correctly detect :insert/:change/:append ex commands
          (Hirohito Higashi).

fixes: vim/vim#17893
closes: vim/vim#17930

efd83d441b

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
(cherry picked from commit e7dfbf1343)
2025-08-08 14:21:31 +00:00
Gregory Anders
e299430ff5 fix(tui): do not remove SIGWINCH handler when resize events are enabled (#35221) (#35238)
When Nvim is started in one terminal emulator,
suspended, and later resumed in a different terminal emulator (as can
happen when using e.g. a multiplexer), the new terminal emulator will
not have all of the same modes enabled that the original terminal
emulator had. This is a problem in particular for in-band resize events
because we were disabling the SIGWINCH signal handler when we determined
that the terminal supported this mode.

However, if the new terminal does not support this mode then the
SIGWINCH handler remains disabled, and Neovim no longer properly
resizes. Instead of disabling the SIGWINCH handler, we track the state
of the resize events mode internally. If the mode is enabled then we
return early from the SIGWINCH handler before performing any ioctls or
other system calls. But if the mode is not enabled we proceed as normal.

(cherry picked from commit b1679f0ab6)
2025-08-08 13:50:02 +00:00
zeertzjq
53a0d99702 vim-patch:9.1.1601: Patch v8.1.0425 was wrong
Problem:  Patch v8.1.0425 was wrong
Solution: Revert that patch (Hirohito Higashi)

This is because the root cause was fixed in 8.1.0786 and a regression
occurred elsewhere.

related: vim/vim#3455
related: vim/vim#3830
fixes: vim/vim#11558
closes: vim/vim#17899

6abe5e4904

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
(cherry picked from commit 7230296bdb)
2025-08-08 00:12:27 +00:00
zeertzjq
a65c4be2de vim-patch:8.1.0425: ml_get error and crash with appendbufline()
Problem:    ml_get error and crash with appendbufline(). (Masashi Iizuka)
Solution:   Set per-window buffer info. (Hirohito Higashi, closes vim/vim#3455)

9cea87c577

Co-authored-by: Bram Moolenaar <Bram@vim.org>
(cherry picked from commit c6f0a19206)
2025-08-08 00:12:27 +00:00
zeertzjq
30db74de66 Merge pull request #35224 from zeertzjq/backport
Backport #35198 #35202 #35210
2025-08-08 06:31:10 +08:00
zeertzjq
6fd842a4fd fix(ui): check for cmdline mode properly
Backport of #35202
2025-08-08 06:11:28 +08:00
zeertzjq
7f1e112a32 fix(pum): check for cmdline mode properly
Backport of #35198 and #35210
2025-08-08 06:11:25 +08:00
zeertzjq
54c2ea142a vim-patch:9.1.1599: :bnext doesn't go to unlisted help buffers (#35216)
Problem:  :bnext doesn't go to unlisted help buffers when cycling
          through help buffers (after 9.1.0557).
Solution: Don't check if a help buffer is listed (zeertzjq).

From <https://github.com/vim/vim/issues/4478#issuecomment-498831057>:

> I think we should fix that, since once you get to a non-help buffer
> all unlisted buffers are skipped, thus you won't encounter another
> help buffer.

This implies that cycling through help buffers should work even if help
buffers are unlisted. Otherwise this part of :bnext isn't really useful,
as :h makes help buffers unlisted by default.

related: vim/vim#4478
related: vim/vim#15198
closes: vim/vim#17913

9662f33480
(cherry picked from commit 53ac2ad20a)
2025-08-07 16:47:04 +00:00
neovim-backports[bot]
f4b4c27a35 fix(float): cursor visible in "hidden" floating window (#35219)
Problem:
Cursor is visible in "hidden" floating window.

Solution:
Hide cursor when curwin is a hidden floating window.
Show cursor after returning to a normal (non-hidden) window.

(cherry picked from commit d4f2b9050d)

Co-authored-by: glepnir <glephunter@gmail.com>
2025-08-07 16:18:32 +00:00
skewb1k
5551da79c1 feat(lsp): improve signature help display #35190
- Add delimiter between function signature and documentation, matching hover formatting
- Show title only if there are multiple clients or multiple signatures
- Avoid duplicating the title inside the window if it's already shown in the border

(cherry picked from commit d26db4bfbf)
2025-08-07 03:29:04 +00:00
glepnir
8f2d6f7ce2 fix(lsp): show title when global winborder is set (#35181)
Problem: make_floating_popup_options only shows when opts.border is explicitly set, ignoring global winborder setting

Solution: check both opts.border and vim.o.winborder when determining whether to show title
(cherry picked from commit 5b1b46ea5a)
2025-08-07 03:01:03 +00:00
glepnir
09b0003d38 fix(api): nvim_create_user_command addr option should allow ranges #35077
Problem: Using `addr` without `range` in nvim_create_user_command gives
"No range allowed" error, inconsistent with `:command -addr` behavior.

Solution: Set EX_RANGE flag when `addr` option is specified to match
`:command` behavior.

(cherry picked from commit 22df649210)
2025-08-03 03:40:46 +00:00
TheBlob42
41fa343484 fix(snippet): jumping backwards to choice node (#35062)
Avoid duplicate text when jumping back to a choice node. Set cursor to
end of tabstop range and prioritize current choice in completion items.

(cherry picked from commit 628d569a59)
2025-07-28 00:35:42 +00:00
TheBlob42
2e4baa3679 fix(snippet): setting end_right_gravity (#35061)
When right_gravity is set to true for deactivating tabstop expansion we
have to set end_right_gravity to false to avoid expanding the tabstop
region on the right side. Vice versa for activating tabstop expansion
again.

(cherry picked from commit dff78f580d)
2025-07-28 00:24:44 +00:00
Maria José Solano
6b820258cd fix(lsp): don't override config.title in vim.lsp.buf.signature_help() #35075
(cherry picked from commit afebbd0f34)
2025-07-27 03:17:18 +00:00
zeertzjq
990b320592 fix(cmdline): :checkhealth completion with multiple args (#35060)
(cherry picked from commit 5de2ec76a3)
2025-07-25 23:29:54 +00:00
zeertzjq
a05b70baa6 fix(clipboard): correct blockwise register width computation (#35038)
(cherry picked from commit 95dfb063da)
2025-07-23 03:18:38 +00:00
glepnir
359d65c902 fix(iter): ArrayIter:last returns nil when filtered to empty #34697
Problem: After filtering out all elements, ArrayIter:last still returns a stale element.
Solution: Add check for self._head == self._tail and return nil early.

Fix #34696

(cherry picked from commit 4fe51dfdae)
2025-07-23 02:33:18 +00:00
Phạm Bình An
62aae1084f docs: update usr_02.txt #35031
Problem:
- There is reference to gVim in the usr_02.txt file, even though Nvim
  has no built-in GUI.
- `:h help-summary` has a section about optional features (e.g.
  `+conceal`) even though such thing does not exist in Nvim (`:h
  +conceal` will give E149 error).

Solution:
- Remove reference to gVim.
- Replace the section about optional features with a section about Lua.

(cherry picked from commit d591275db7)
2025-07-22 23:15:21 +00:00
Justin M. Keyes
e534afa5ab Merge #34986 from brianhuster/release-0.11 2025-07-22 07:46:18 -07:00
brianhuster
2124146164 fix(tutor): use legacy syntax for lesson 3.1 of vim-01-beginner.tutor
Problem:
- Extmark breaks lesson 3.1 of vim-01-beginner.tutor because when users
  delete the line and put it elsewhere, the extmark doesn't move to the
  put location.
- This doesn't mean the extmark implementation is bad though (note that
  thanks to extmark, for the first time, we can make lesson 2.6 really
  interactive), it's just that the tutor format has never been made for
  kinds of lessons like lesson 3.1, which is why all "expected" in that
  lesson are -1, which also means that lesson is not interactive in the
  first place. Also see lesson 2.1.3 in vim-02-beginner, where the mark
  is just used to mark the first line of the exercise, which also prove
  my point.

Solution:
- For a not-really-interactive lesson like lesson 3.1, just use legacy
  syntax. I borrow the old vimtutor's `--->` to mark the exercises of
  the lesson.
- Less redundant interactive marks also make the json files smaller and
  more maintainable.
2025-07-19 22:40:28 +07:00
brianhuster
64afa93187 fix(tutor): use invalidate field in nvim_buf_set_extmark()
Problem:
If users delete a line containing extmark, it will move to the next
line, which could highlight the next line in an unwanted way.

Solution:
- Use `invalidate` field in `nvim_buf_set_extmark()` to prevent the
extmark from moving.
- Also save from "priority" hacking, since we can check if the extmark
  is valid in `nvim_buf_get_extmarks()` now.
2025-07-19 09:42:16 +07:00
brianhuster
e6a0f0ee71 test(tutor_spec): remove test description("Tutor: tutor")
Problem:
It is redundant since we have another test that test interactive marks
in lesson 2.6 of tutor 1
2025-07-18 16:45:25 +07:00
brianhuster
685302682a refactor(tutor): reimplement interactive marks as extmark in Lua
Problem:
From https://matrix.to/#/!cylwlNXSwagQmZSkzs:matrix.org/$Ofj-TFIsEMbp0O9OhE8xuZSNi-nhRLtZTOgs6JRLNrs?via=matrix.org&via=gitter.im&via=mozilla.org

In lesson 2.6, users are asked to remove the second, forth and fifth
lines with `dd` command, then they are asked to undo twice to make the
text go back to original state. But after that, the mark ✗ appears
again, which confuses the user because they think they do something
wrong. This is a limitation with the current implementation, which is
based on line number only.

Solution:
Reimplement interactive marks as extmarks in Lua. This also make the
feature less fragile, as users can remove, add some arbitrary lines
without breaking the interactive marks.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-07-18 16:45:06 +07:00
PilgrimLyieu
5e7021eb1b docs(tutor): Chinese (zh-CN) translation #34803
Co-authored-by: glepnir <glephunter@gmail.com>
2025-07-18 16:44:14 +07:00
Donatas
44b8255fa2 fix(lsp): close floating preview window correctly #34946
Problem:
After 28b7c2d (found with bisect) the hover preview window does not
close when :edit'ing another file, even when you move the cursor.

Solution:
Change the BufLeave to target the original buffer, not the preview
buffer.

(cherry picked from commit ace254c9ff)
2025-07-16 03:46:59 +00:00
Phạm Bình An
407fc0bb16 revert: "fix(runtime): set 'foldmethod' for Lua ftplugin #34929" (#34947)
This reverts commit 12276832ab.

(cherry picked from commit 9789a3b854)
2025-07-15 22:59:33 +00:00
Phạm Bình An
657540945c fix(runtime): set 'foldmethod' for Lua ftplugin #34929
Problem:
Neovim's Lua ftplugin doesn't set `'foldmethod'`, though Vim one sets it 1341176e7b/runtime/ftplugin/lua.vim (L66-L68)

Solution:
Set it

(cherry picked from commit 12276832ab)
2025-07-14 22:04:30 +00:00
Gregory Anders
9261aef2f3 fix(lsp/health): always use vim.inspect to show root_markers (#34667)
In https://github.com/neovim/neovim/pull/34092 we changed the
healthcheck to display root markers as a concatenated list if the first
item in root_markers is a string (not a table). However, this does not
solve the general case, because root_markers can contain a string as the
first element, but a table as the 2nd element.

Because root_markers has a more complex structure we should always just
display it using vim.inspect, rather than adding a special case for when
all items are a string.

(cherry picked from commit f0c0c24ed7)
2025-07-14 12:59:40 +00:00
neovim-backports[bot]
d185057bc7 fix(lsp/health): ensure valid table before concatenating (#34930)
The root_markers field can now contain a table of tables (as of
https://github.com/neovim/neovim/pull/33485) and :checkhealth will show
an error in that case since Lua cannot concatenate a table of tables.

Ensure that tables contain strings before concatenating and if not, fall
back to using vim.inspect().

(cherry picked from commit 5ad01184f3)

Co-authored-by: Gregory Anders <greg@gpanders.com>
2025-07-14 05:15:27 -07:00
Justin M. Keyes
6cfaa9c204 version bump 2025-07-12 14:36:34 -04:00
Justin M. Keyes
b2684d9f66 NVIM v0.11.3
For notable changes, see runtime/doc/news.txt (or `:help news` in Nvim).

Following is a list of fixes/features.

FEATURES
--------------------------------------------------------------------------------
- db3b856779 defaults: map "grt" to LSP type_definition #34663
- ecf5164d2d lsp: pass resolved config to cmd() #34560
- 5d0766ddce vim.fs: vim.fs.root() can control priority #34413

FIXES
--------------------------------------------------------------------------------
- f2c4305114 api: add missing nargs field to user command Lua callbacks #34210
- aa6136f956 api: adjust fix for reconfiguring float "relative" (#34287)
- 6889f9168b api: populate lhsrawalt in nvim_get_keymap response
- 0d66963089 api: reconfiguring float "relative" does not clear "win" (#34271)
- 8d3b7b57c8 api: update topline when flushing with nvim__redraw() (#34346)
- 77eb278adf clipboard: enable cache for function providers #34470
- 0613faf596 column: missing redraw with virt_lines_leftcol (#34650)
- 4303337c77 diagnostics: validate opts.signs #34565
- 902c946bcd editorconfig: a custom property is treated as a section (#34445)
- 7b2119dbd9 exrc: exrc knows its own location #34638
- 7da0c46e1b health: bad format() call #34906
- c97ad3cb41 health: floating window closes when opening TOC (gO)
- 68d204462c health: highlight group conflicts with help #34616
- 282f9fb816 incsearch: include compsing characters with Ctrl-L
- d0a24ea03e lsp: _cancel_all_requests() tries to cancel completed requests #34105
- 4621527f59 lsp: add `RequestFailed` error code constant #34645
- 1077374380 lsp: advertise supported fold kinds (#34461)
- 0f1cada0f7 lsp: announce diagnostic tag support (#34436)
- ff8acfffd2 lsp: include client ID when receiving unknown fold kind (#34535)
- c13eba5254 lsp: only auto-detach lsp.config enabled clients #34325
- ea8db9003b lsp: use correct deprecation function (#34518)
- f7b1b0595d menu: fix listing of submenus (#34315)
- adf31505d8 messages: make swapfile attention message part of prompt (#34414)
- 89959ab9dc messages: recognize cmdline one_key/number prompt State (#34206)
- ec84c8df0e msgpack: flush incomplete big UI event before packing RPC event
- d9c10ea753 redraw: update curswant for Visual selection (#34241)
- 388b559848 runtime: no conceal in qf on :lopen #34854
- d9b9514e8e startup: make startup windows if there are only floating windows (#34349)
- ef68eae09a term: terminal attr index may exceed TERM_ATTRS_MAX #34318
- c4a760c734 terminal: don't disable scrolloff for non-terminal buffers (#34451)
- 36c6f488e4 terminal: fix OSC 8 parsing (#34424)
- 68677eb477 terminal: stack overflow when too many csi args (#34012)
- 3d5be364bc treesitter: enable a gc for wasmtime
- a80bdf0d9b treesitter: ensure TSLuaTree is always immutable
- 07d9197840 treesitter: ensure TSNode's tree is immutable
- 7184230e94 treesitter: ensure window is valid in async parsing #34385
- 8183eb32e1 treesitter: scope highlight state per window
- dfeec113be treesitter: support multiple `@injection.content` captures
- 70b4e7948f tui: avoid memory leak and compiler warning on Windows (#34225)
- 6f8efea940 tui: check for title support correctly (#34866)
- 43804477ca tui: don't crash when nvim__screenshot() is called with bad path (#34594)
- 0eec4a8ecc tui: wait for embedded server's exit code
- bfcf541a9e tutor: cannot find tutors in pack/*/start/* #34689
- f9f0345eba vim.json: loss of precision on integers >14 digits #34876
- 203d4f916d vim.system: clear_env=true gives an invalid env to uv.spawn #33955
- e732cbe36c vim.system: env=nil passes env=nil to uv.spawn
- 7286e514f2 vim.version: vim.VersionRange:has(<prerelease>) (#33324)
- 742ea00742 window: don't enter unfocusable or hidden prevwin (#34486)
- e0ddf93bb0 windows: don't set window icon on SIGHUP #34260

VIM PATCHES
--------------------------------------------------------------------------------
- ae05e0399b 0fb6cea: runtime(lua): update 'path' option in filetype plugin #33876
- 7ef602d470 2323f22: runtime(new-tutor): add chapter two to the interactive tutorial
- 2d13ae0dd4 7a734b7: tests: fix typo in comment (after v9.1.1511)
- d32a4dd4b0 9.1.1404: wrong link to Chapter 2 in new-tutor
- 28531d18f0 9.1.1421: tests: need a test for the new-style tutor.tutor (#34267)
- d28ad6e03f 9.1.1450: Session has wrong arglist with :tcd and :arglocal (#34430)
- 9ffa94b07b 9.1.1463: Integer overflow in getmarklist() after linewise operation (#34532)
- d5cbc99358 9.1.1482: scrolling with 'splitkeep' and line() (#34670)
- 2df746e4e8 9.1.1506: tests: missing cleanup in Test_search_cmdline_incsearch_highlight() (#34748)
- 730a5e0599 9.1.1511: tests: two edit tests change v:testing from 1 to 0
- 87ba1d7465 9.1.1521: completion: pum does not reset scroll pos on reopen with 'noselect' (#34836)
- 222b3d5021 bfeefc4: runtime(doc): clarify the effect of exclusive single char selections (#34289)
- 9d8c5119e2 eb59129: runtime(typescript): remove Fixedgq() function from indent script (#34334)
2025-07-12 14:34:12 -04:00
neovim-backports[bot]
7da0c46e1b fix(health): bad format() call #34906
Problem:
Bad format() call on PUC Lua

    Error: Failed to run healthcheck for "vim.health" plugin. Exception:
    runtime/lua/vim/health/health.lua:89: bad argument #1 to 'format' (string expected, got nil)

Solution:
Avoid passing nil.

(cherry picked from commit 2422fbdd5f)

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-07-12 11:32:56 -07:00
Christian Clason
91ef8606f2 build(deps): update tree-sitter parsers and queries #34905
bump
* tree-sitter to v0.25.6
* tree-sitter-c to v0.24.1
* tree-sitter-lua to v0.4.0
* tree-sitter-vim to v0.7.0 (and update queries)
* tree-sitter-vimdoc to v4.0.0
* tree-sitter-query to v0.6.2
* tree-sitter-markdown to v0.5.0
2025-07-12 11:27:31 -07:00
luukvbaal
89959ab9dc fix(messages): recognize cmdline one_key/number prompt State (#34206)
Problem:  Since 48e2a736, prompt messages are handled by an actual
          active cmdline, resulting in `State` no longer being equal
          to `MODE_CONFIRM` which is used in some places. E.g. to
          specify the current `mode()` or to re-emit a confirm message.
Solution: Replace `MODE_CONFIRM` with a new `MODE_CMDLINE` sub-mode when
          `ccline.one_key/mouse_used` is set. Use it to avoid clearing
          mouse_used prompt messages, and to re-emit one_key messages
          (when ext_messages is inactive, for which this is unnecessary).
(cherry picked from commit e876a739ee)
2025-07-12 14:45:29 +00:00
luukvbaal
adf31505d8 fix(messages): make swapfile attention message part of prompt (#34414)
Problem:  The swapfile attention message is not repeated after clearing
          the screen.
          After clearing the screen `msg_scrolled` is reset without
          clearing other related variables, causing an assert.
Solution: Make the attention message part of the confirm prompt.
          Call `msg_reset_scroll()`.
(cherry picked from commit d86d4bacc1)
2025-07-12 14:14:40 +00:00
jade
f9f0345eba fix(vim.json): loss of precision on integers >14 digits #34876
Problem: multiple DAP servers keep assuming they can have internal IDs
         up to 2**52, which get corrupted by the Neovim JSON encoder.
Solution: change (1) constant and add a test so nobody breaks it while
          updating the library.

Fixes: https://github.com/neovim/neovim/issues/24532
Fixes: https://github.com/mfussenegger/nvim-dap/issues/1534
Fixes: https://github.com/facebook/buck2/issues/1032
(cherry picked from commit 7a69fefdb9)
2025-07-11 02:55:34 +00:00
zeertzjq
6f8efea940 fix(tui): check for title support correctly (#34866)
(cherry picked from commit e65c0a0810)
2025-07-10 07:06:22 +00:00
phanium
388b559848 fix(runtime): no conceal in qf on :lopen #34854
Problem:
No conceal in qf on `lopen` since 74fcc945. Repro:

    nvim --clean +'tab Man ls' +'norm gO' +lclose +lopen

Solution:
Consider "Table of contents" title.

(cherry picked from commit 76f6868e0a)
2025-07-09 17:07:07 +00:00
neovim-backports[bot]
c97ad3cb41 fix(health): floating window closes when opening TOC (gO)
fix(health): floating window closes when opening TOC (gO) #34794

Problem: Health check floating window gets closed when pressing 'gO' to show TOC because LSP floating preview system auto-closes on BufEnter events triggered by :lopen.

Solution: Temporarily disable BufEnter event for the current window during TOC operations and adjust window layout to prevent overlap.
(cherry picked from commit 28b7c2df52)

Co-authored-by: glepnir <glephunter@gmail.com>
2025-07-08 23:10:18 +00:00
zeertzjq
2ddb5d21bb test(api): nvim_get_keymap returns correct lhsraw and lhsrawalt
(cherry picked from commit 0c973bf442)
2025-07-08 00:29:05 +00:00
ncrpy
6889f9168b fix(api): populate lhsrawalt in nvim_get_keymap response
Problem:
The `nvim_get_keymap()` function is missing the `lhsrawalt` field in its response for mappings with an alternate key representation. This makes its return value inconsistent with its documented `maparg()`-like structure and its formal type definition.

Solution:
Corrects the `keymap_array` function to pass the alternate mapping keys (`current_maphash->m_alt->m_keys`) to `mapblock_fill_dict`. The argument responsible for this was previously hardcoded to `NULL`.

For example, for a mapping of `<C-x>`, the API will now correctly return both `lhsraw` (`<80><fc>^DX`) and `lhsrawalt` (the alternate form, e.g., `^X`).

(cherry picked from commit d523750de0)
2025-07-08 00:29:05 +00:00
zeertzjq
2d3a4154c5 Merge pull request #34839 from zeertzjq/backport
vim-patch:9.1.1521: completion: pum does not reset scroll pos on reopen with 'noselect' (#34836)
2025-07-08 07:36:36 +08:00
zeertzjq
87ba1d7465 vim-patch:9.1.1521: completion: pum does not reset scroll pos on reopen with 'noselect' (#34836)
Problem:  When 'wildmode' is set to include "noselect", the popup menu (pum)
          incorrectly retained its scroll position when reopened. This
          meant that after scrolling down through the menu with `<C-n>`,
          reopening the menu (e.g., by retyping the command and
          triggering completion again) would show the menu starting from
          the previously scrolled position, rather than from the top.
          This could confuse users, as the first visible item would not
          be the first actual match in the list.

Solution: Ensure that the popup menu resets its scroll position to the
          top when reopened (Girish Palya).

closes: vim/vim#17673

0cd7f3536b

Co-authored-by: Girish Palya <girishji@gmail.com>
2025-07-08 06:49:27 +08:00
Maria José Solano
0ab089add4 refactor(lsp): consistent usage of vim.notify #34802
(cherry picked from commit 580b8cfac7)
2025-07-06 14:40:39 +00:00
zeertzjq
2d13ae0dd4 vim-patch:7a734b7: tests: fix typo in comment (after v9.1.1511)
related: vim/vim#17660

7a734b7148
(cherry picked from commit 8a4977e286)
2025-07-06 10:11:30 +00:00
zeertzjq
730a5e0599 vim-patch:9.1.1511: tests: two edit tests change v:testing from 1 to 0
Problem:  tests: two edit tests change v:testing from 1 to 0.
Solution: Don't change v:testing in these two tests, since it's already
          set to 1 in runtest.vim (zeertzjq).

closes: vim/vim#17660

96076bf41e
(cherry picked from commit 11e967d5af)
2025-07-06 10:11:30 +00:00
zeertzjq
85c9014c09 refactor(getchar): rename test variable (#34769)
Also, test_disable_char_avail() is superseded by test_override() in Vim,
so remove that from vim_diff.txt.

(cherry picked from commit 0c02c9c70b)
2025-07-04 22:22:35 +00:00
zeertzjq
282f9fb816 fix(incsearch): include compsing characters with Ctrl-L
Cherry-picked from Vim patch 8.1.0579.

(cherry picked from commit f348c0ebba)
2025-07-04 04:36:00 +00:00
zeertzjq
d1214da08f test(old): emulate test_override('char_avail') using FFI
Add a non-static variable for this, otherwise it'll be really hacky.
This avoid having to rewrite many incsearch tests in Lua.

(cherry picked from commit c925e7b8ba)
2025-07-04 04:36:00 +00:00
zeertzjq
f0f163b267 test(editor/defaults_spec): fix flakiness (#34752)
(cherry picked from commit 17ecb2b988)
2025-07-04 03:21:14 +00:00
zeertzjq
2df746e4e8 vim-patch:9.1.1506: tests: missing cleanup in Test_search_cmdline_incsearch_highlight() (#34748)
Problem:  tests: missing cleanup test_override('char_avail', 0) in
          Test_search_cmdline_incsearch_highlight().
Solution: Add the missing cleanup (zeertzjq).

closes: vim/vim#17655

29b29c6b30
(cherry picked from commit eef62e815d)
2025-07-03 23:22:07 +00:00
zeertzjq
b9dbdfef0e test(old): emulate test_override('starting') with FFI (#34742)
I was initially trying to port several cmdline tests from Vim involving
test_override('char_avail') without having to rewrite entire tests in
Lua, but haven't figured out a good way achieve that yet. Nevertheless
emulating test_override('starting') is easier.

(cherry picked from commit 715c28d67f)
2025-07-03 23:15:36 +00:00
Alex Díaz
0f81af53b0 build: support static build #34728
(cherry picked from commit e91224bfaa)
2025-07-02 22:47:42 +00:00
Rodrigodd
a80bdf0d9b fix(treesitter): ensure TSLuaTree is always immutable
Problem:

The previous fix in #34314 relies on copying the tree in `tree_root` to
ensure the `TSNode`'s tree cannot be mutated. But that causes the
problem where two calls to `tree_root` return nodes from different
copies of a tree, which do not compare as equal. This has broken at
least one plugin.

Solution:

Make all `TSTree`s on the Lua side always immutable, avoiding the need
to copy the tree in `tree_root`, and make the only mutation point,
`tree_edit`, copy the tree instead.

(cherry picked from commit 168bf0024e)
2025-07-02 17:03:47 +00:00
Rodrigodd
37fb09c162 test(treesitter): test tree:root() is idempotent
Test for regression #34605

(cherry picked from commit 94f44d58fd)
2025-07-02 17:03:47 +00:00
Lewis Russell
e732cbe36c fix(vim.system): env=nil passes env=nil to uv.spawn
731e616a79 made it so passing `{env = nil, clear_env = true }` would
pass `{env = {}}` to `vim.uv.spawn`.

However this is not what `clear_env` is (arguably) supposed to do.
If `env=nil` then that implies the uses wants `vim.uv.spawn()` to use
the default environment. Adding `clear_env = true` simply prevents
`NVIM` (the base environment) from being added.

Fixes #34730

(cherry picked from commit 4eebc46930)
2025-07-02 16:53:26 +00:00
Justin M. Keyes
2d3517012a Merge #34722 from justinmk/release 2025-07-01 04:34:05 -07:00
Yochem van Rosmalen
41ceefe804 test(exrc): lua exrc knows its location #34713
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
(cherry picked from commit 99873296be)
2025-07-01 12:54:59 +02:00
Yochem van Rosmalen
7b2119dbd9 fix(exrc): exrc knows its own location #34638
Problem:
'exrc' files are inherently bound to their location / workspace and
therefore require to "know" their location on the filesystem. However,
currently using `debug.getinfo(1, 'S')` returns `"<nvim>"`.

Solution:
Include the filepath as chunkname in `loadstring()` and `nlua_exec()`.

(cherry picked from commit f7c939fa7a)
2025-07-01 12:53:39 +02:00
Gabriel Ford
ef68eae09a fix(term): terminal attr index may exceed TERM_ATTRS_MAX #34318
Problem: Currently terminal highlight attribute buffers are statically allocated
be the size of `TERM_ATTRS_MAX`. This unique case isn't respected in
some places in the ui_compositor. Due to this, when a terminal window
has lines longer them `TERM_ATTRS_MAX`, the compositor will go past the
end of the buffer causing a crash due to out of bounds access.

Solution: Add check to ensure we don't query terminal highlight attrs
past `TERM_ATTRS_MAX` in `win_line()`.

Fixes #30374

(cherry picked from commit d6d1bfd20d)
2025-06-30 13:58:31 +00:00
Gabriel Ford
43804477ca fix(tui): don't crash when nvim__screenshot() is called with bad path (#34594)
Problem: Currently `vim.api.nvim__screenshot()` crashes when called with an
invalid path. This is because we don't check if `fopen()` returns a null
pointer.

Solution: Bail out if `fopen()` returns a null pointer.

Fixes: https://github.com/neovim/neovim/issues/34593
(cherry picked from commit 331de6afa6)
2025-06-29 12:04:03 +00:00
Phạm Bình An
bfcf541a9e fix(tutor): cannot find tutors in pack/*/start/* #34689
Problems:
- Unlike in Vim, Neovim does not report pack/*/start/* in the resolved value of 'rtp' (see `:help packages-runtimepath`)
- This means that the tutor plugin cannot find the tutors in pack/*/start/*

Solution:
- Use nvim_list_runtime_paths() instead of &rtp

(cherry picked from commit bff7d3fd9f)
2025-06-28 17:06:05 +00:00
Phạm Bình An
28531d18f0 vim-patch:9.1.1421: tests: need a test for the new-style tutor.tutor (#34267)
Problem:  tests: need a test for the new-style tutor.tutor, patch
          9.1.1384 broke the expected positions for the signs
Solution: Update all number keys in tutor.tutor.json to match the
          correct line numbers in tutor.tutor, replace tabs by spaces,
          add a screen-dump test to verify it does not regress
          (Pham Bình An)

closes: vim/vim#17416

a541f1de2b
(cherry picked from commit f1f106be3d)
2025-06-28 09:37:37 +00:00
luukvbaal
d5cbc99358 vim-patch:9.1.1482: scrolling with 'splitkeep' and line() (#34670)
Problem:  Topline is preemptively updated by line() in WinResized
          autocmd with 'splitkeep' != "cursor".
Solution: Set `skip_update_topline` when 'splitkeep' != "cursor".
          (Luuk van Baal)

fe803c8c04
(cherry picked from commit 0b91e9f83b)
2025-06-27 09:48:40 +00:00
Justin M. Keyes
db3b856779 feat(defaults): map "grt" to LSP type_definition #34663
backport #34642

(cherry picked from commit 5d06eade25)

Co-authored-by: Caleb White <cdwhite3@pm.me>
2025-06-26 18:45:02 +00:00
Justin M. Keyes
e6eb910496 Merge pull request #34668 from brianhuster/release-0.11 2025-06-26 11:32:41 -07:00
Phạm Bình An
7286e514f2 fix(vim.version): vim.VersionRange:has(<prerelease>) (#33324)
Problem:
`vim.version.range('>=0.10'):has('0.12.0-dev')` returns false, which is
wrong per semver.

Solution:
`vim.VersionRange:has()` shouldn't have special handling for prereleases
(Why would we need it when `__eq`, `__lt`, `__le` already handle
 prereleases?).

Closes #33316
2025-06-27 01:05:23 +07:00
Phạm Bình An
ae05e0399b vim-patch:0fb6cea: runtime(lua): update 'path' option in filetype plugin #33876
Problem:  Lua doesn't support importing module in path related to current
          file like JS does (https://www.reddit.com/r/lua/comments/wi0bau/whats_the_correct_way_to_run_a_lua_file_that_uses/)
Solution: Remove `.` from Lua buffer-local option `'path'`

closes: vim/vim#17267

0fb6ceac4c
2025-06-27 00:59:40 +07:00
Maria José Solano
4621527f59 fix(lsp): add RequestFailed error code constant #34645
Also remove `serverErrorStart/End` as [the spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#errorCodes)
says that they're deprecated and don't even represent a real error code.

(cherry picked from commit 3eaa6c5a66)
2025-06-26 09:59:01 +00:00
zeertzjq
638bc951b2 Merge pull request #34651 from zeertzjq/backport
fix(column): missing redraw with virt_lines_leftcol (#34650)
2025-06-26 13:45:47 +08:00
zeertzjq
0613faf596 fix(column): missing redraw with virt_lines_leftcol (#34650)
Problem:  Missing number column redraw with virt_lines_leftcol.
Solution: Set virt_line_index to -1 when skipping a virtual line.
2025-06-26 13:20:47 +08:00
Birdee
203d4f916d fix(vim.system): clear_env=true gives an invalid env to uv.spawn #33955
Problem:
In setup_env, some needed logic is bypassed when clear_env=true.

Solution:
Drop the early return in setup_env().

Co-authored-by: BirdeeHub <birdee@localhost>
(cherry picked from commit 731e616a79)
2025-06-25 22:44:52 +00:00
Dmytro Meleshko
68d204462c fix(health): highlight group conflicts with help #34616
(cherry picked from commit 6942dec9b2)
2025-06-23 17:04:55 +00:00
Axel
70b4e7948f fix(tui): avoid memory leak and compiler warning on Windows (#34225)
Problem:  On Windows, the value of `term` is overwritten without freeing
          the old allocated value, which may lead to a memory leak.
	  GCC also gives a "incompatible pointer type" warning about
	  passing `*term` to os_tty_guess_term().
Solution: Don't override the old allocated value, and copy the guessed
          value to `term` if its old value is NULL.
(cherry picked from commit fb5a51e775)
2025-06-23 14:47:37 +00:00
Gabriel Ford
ee06d0e64d build(msvc): suppress msvc build warning for different enum types (#34591)
Problem: Currently our CI is failing due to msvc warning 5287. This
warning tells us that we are performing a binary expression with enums
of two different types. In this case however, this is clearly intended
and valid.

Solution: Suppress warning 5287 on the line this occurs.
(cherry picked from commit 0980617c0d)
2025-06-23 00:27:08 +00:00
Oskar Haarklou Veileborg
1976ca68b5 fix(lsp) type annotation for vim.lsp.Config.cmd #34574
The type annotation for `vim.lsp.ClientConfig.cmd` was changed,
but the update was not propagated to `vim.lsp.Config`.

(cherry picked from commit 150513a163)
2025-06-19 11:07:22 +00:00
Bruce Wen
4303337c77 fix(diagnostics): validate opts.signs #34565
(cherry picked from commit 3594c213a7)
2025-06-18 17:53:10 +00:00
Justin M. Keyes
ecf5164d2d backport: feat(lsp): pass resolved config to cmd() #34560
Problem:
In LSP configs, the function form of `cmd()` cannot easily get the
resolved root dir (workspace). One of the main use-cases of a dynamic
`cmd()` is to be able to start a new server  whose binary may be located
*in the workspace* ([example](https://github.com/neovim/nvim-lspconfig/pull/3912)).

Compare `reuse_client()`, which also receives the resolved config.

Solution:
Pass the resolved config to `cmd()`.


(cherry picked from commit 32f30c4874)

Co-authored-by: Julian Visser <12615757+justmejulian@users.noreply.github.com>
2025-06-18 12:46:53 +00:00
phanium
6396bfb29f docs: vim.fs.dir.Opts type #34546
Follow the pattern of vim.fs.find.Opts

(cherry picked from commit 496691f985)
2025-06-17 14:38:36 +00:00
Maria José Solano
ea8db9003b fix(lsp): use correct deprecation function (#34518)
(cherry picked from commit 00ad477419)
2025-06-17 03:14:09 +00:00
Maria José Solano
ff8acfffd2 fix(lsp): include client ID when receiving unknown fold kind (#34535)
(cherry picked from commit cd06e0c9d6)
2025-06-17 03:04:36 +00:00
zeertzjq
9ffa94b07b vim-patch:9.1.1463: Integer overflow in getmarklist() after linewise operation (#34532)
Problem:  Integer overflow in getmarklist() after linewise operation.
Solution: Don't add 1 to MAXCOL (zeertzjq)

related: neovim/neovim#34524
closes: vim/vim#17552

93318a9933
(cherry picked from commit 3e984cf02b)
2025-06-16 23:45:02 +00:00
Riley Bruins
1077374380 fix(lsp): advertise supported fold kinds (#34461)
This commit also makes it so that folds which have an unsupported fold
kind have their `kind` ignored.

(cherry picked from commit 35756022cb)
2025-06-16 20:52:17 +00:00
Lewis Russell
3d5be364bc fix(treesitter): enable a gc for wasmtime
(cherry picked from commit 5fe582448c)
2025-06-16 08:48:29 +00:00
zeertzjq
ce292026ea docs(meta): fix incorrect bar -> backtick replacement (#34520)
(cherry picked from commit 4b2c2eb120)
2025-06-16 01:43:41 +00:00
luukvbaal
742ea00742 fix(window): don't enter unfocusable or hidden prevwin (#34486)
Problem:  When closing a floating window, the next window to be entered
          may be unfocusable or hidden.
Solution: Don't enter prevwin when it is unfocusable or hidden. Enter
          firstwin instead (like for when prevwin is no longer valid).
(cherry picked from commit 0d658660c2)
2025-06-14 22:19:32 +00:00
Justin M. Keyes
a34b8e42df docs: vim.fs., diagnostics, lsp #34488
backport of #34402

(cherry picked from commit 8001276bd0)
2025-06-13 16:22:10 +00:00
Antoine
77eb278adf fix(clipboard): enable cache for function providers #34470
Problem:
With these settings, copy/pasting `blockwise-visual` (with `CTRL+V`)
incorrectly pastes as a `linewise` mode because `regtype` is ignored:

    vim.opt.clipboard = 'unnamedplus'
    vim.g.clipboard = 'osc52'

To reproduce: press `CTRL+V` and select some characters press `p` and
observe that it is pasted in `linewise` mode.

Solution:
Enable the [clipboard.vim](https://github.com/neovim/neovim/blob/master/runtime/autoload/provider/clipboard.vim#L281-L283))
cache for function providers, so that `regtype` is maintained for the OSC52
clipboard provider.

(cherry picked from commit ac12dc49cc)
2025-06-12 23:08:24 +00:00
Andre Toerien
d0a24ea03e fix(lsp): _cancel_all_requests() tries to cancel completed requests #34105
Problem:
The cancel function returned by `vim.lsp.buf_request` tries to cancel
all the requests, including those that have already been completed,
causing "Cannot find request with id ... whilst attempting to cancel"
errors to be logged when it is called.

Solution:
Only cancel the requests that are present in `client.requests`.

(cherry picked from commit a9b8a8dc6c)
2025-06-12 17:00:27 +00:00
bfredl
ec84c8df0e fix(msgpack): flush incomplete big UI event before packing RPC event
This might happen, e.g. if we send a fast RPC reply in a os_breakcheck()
in the middle of redrawing and big ui_ext events are produced.

fixes #31316

(cherry picked from commit b8ee354f12)
2025-06-12 13:28:41 +00:00
zeertzjq
c4a760c734 fix(terminal): don't disable scrolloff for non-terminal buffers (#34451)
(cherry picked from commit 6a71239cd5)
2025-06-11 15:21:19 +00:00
fortime
902c946bcd fix(editorconfig): a custom property is treated as a section (#34445)
Problem: A custom property containing a pair of square brackets will be
treated as a section.
Solution: Change the logic parsing a section, remove the first match
regex `%b[]`.

Signed-off-by: fortime <palfortime@gmail.com>
(cherry picked from commit 966b1da183)
2025-06-11 14:21:32 +00:00
Riley Bruins
0f1cada0f7 fix(lsp): announce diagnostic tag support (#34436)
This commit also adds a type annotation to the returned client
capabilities table, because without it lua_ls does not provide
autocompletion for the fields within the table.

(cherry picked from commit bac133e4b6)
2025-06-11 04:53:05 +00:00
zeertzjq
c5bc0289ed test(screen): still match by full row when {MATCH:} is present (#34437)
Add '^' and '$' around the pattern. This makes it less likely to make
mistakes of when writing tests with {MATCH:}, as most such tests have
text before and after {MATCH:}.

(cherry picked from commit 37d6ac8a15)
2025-06-11 02:41:36 +00:00
zeertzjq
d28ad6e03f vim-patch:9.1.1450: Session has wrong arglist with :tcd and :arglocal (#34430)
Problem:  Session has wrong arglist with :tcd and :arglocal.
Solution: Also use absolute path for :argadd when there is tabpage-local
          directory (zeertzjq).

related: neovim/neovim#34405
closes: vim/vim#17503

a304e49790
(cherry picked from commit 612f8e7c9e)
2025-06-11 00:05:59 +00:00
zeertzjq
b0ced63e43 Merge pull request #34428 from zeertzjq/backport
fix(tui): wait for embedded server's exit code
2025-06-11 07:14:39 +08:00
zeertzjq
0eec4a8ecc fix(tui): wait for embedded server's exit code
Uses the undocumented "error_exit" UI event for a different purpose:
When :detach is used on the server, send an "error_exit" with 0 `status`
to indicate that the server shouldn't wait for client exit.
2025-06-11 06:33:03 +08:00
Gregory Anders
36c6f488e4 fix(terminal): fix OSC 8 parsing (#34424)
vterm does not send us the terminator in the string fragment. Our OSC 8
parser assumed that it was and therefore treated short strings as
invalid (as it assumed it was missing a terminator).

(cherry picked from commit b5aef05b8f)
2025-06-10 22:26:56 +00:00
Siddhant Agarwal
5d0766ddce backport: feat(vim.fs): vim.fs.root() can control priority #34413
feat(vim.fs): vim.fs.root() can control priority

Adds the capability of controlling the priority of searched markers in
vim.fs.root() by nesting lists.

(cherry picked from commit 0f0b96dd0f)
2025-06-10 07:19:51 -07:00
Mtende
718b3ffe74 docs(diagnostics): default keymaps #34400
(cherry picked from commit 2d980e37c8)
2025-06-09 16:10:53 +00:00
notomo
7184230e94 fix(treesitter): ensure window is valid in async parsing #34385
Problem: Error occurs if window is invalid in the middle of parsing.

Solution: Check if window is valid in parsing.

- Error
```
vim.schedule callback: ...im/share/nvim/runtime/lua/vim/treesitter/highlighter.lua:485: Invalid window id: 1037
stack traceback:
	[C]: in function 'nvim__redraw'
	...im/share/nvim/runtime/lua/vim/treesitter/highlighter.lua:485: in function 'cb'
	...m/share/nvim/runtime/lua/vim/treesitter/languagetree.lua:494: in function '_run_async_callbacks'
	...m/share/nvim/runtime/lua/vim/treesitter/languagetree.lua:550: in function <...m/share/nvim/runtime/lua/vim/treesitter/languagetree.lua:529>
```

- Reproduce script
```lua
local bufnr = vim.api.nvim_create_buf(false, true)
local many_lines = vim.fn["repeat"]({ "local test = 'a'" }, 100000)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, many_lines)
local window = vim.api.nvim_open_win(bufnr, true, {
  relative = "editor",
  row = 0,
  col = 0,
  width = 10,
  height = 10,
})
vim.bo.filetype = "lua"
vim.schedule(function()
  vim.api.nvim_win_close(window, true)
end)
```

(cherry picked from commit 58d85cd03d)
2025-06-09 00:10:20 +00:00
luukvbaal
d9b9514e8e fix(startup): make startup windows if there are only floating windows (#34349)
Problem:  If user init creates a floating window, startup windows
          (e.g. to accomodate arglist files) are no longer created.
Solution: Check that firstwin->w_next is not floating when deciding
          whether to make startup windows.
(cherry picked from commit c8c78b531b)
2025-06-08 23:30:27 +02:00
Riley Bruins
dfeec113be fix(treesitter): support multiple @injection.content captures
Before, only the last capture's range would be counted for injection.
Now all captured ranges will be counted in the ranges array. This is
more intuitive, and also provides a nice solution/alternative to the
"scoped injections" issue.

(cherry picked from commit 8b41df185c)
2025-06-08 16:55:19 +00:00
Riley Bruins
8183eb32e1 fix(treesitter): scope highlight state per window
**Problem:** There is a lot of distracting highlight flickering when
editing a buffer with multiple open windows. This is because the
parsing/highlighting state is shared across all windows.

**Solution:** Greatly reduce flicker in window splits by scoping the
highlighter state object and the `parsing` state object to each
individual window, so there is no cross-window interference.
2025-06-08 11:01:50 +02:00
luukvbaal
8d3b7b57c8 fix(api): update topline when flushing with nvim__redraw() (#34346)
Problem:  nvim__redraw may update the screen with an invalid topline.
Solution: Update the topline before calling `update_screen()` (as
          :redraw does).
(cherry picked from commit af82f36108)
2025-06-07 11:35:59 +00:00
zeertzjq
c0201909c7 test(tui_spec): avoid dangling process in OSC 52 test (#34356)
(cherry picked from commit 22389159f5)
2025-06-07 09:17:04 +00:00
Rodrigodd
a03057560a test(treesitter): test node access after tree edit
(cherry picked from commit 4c333fdbb7)
2025-06-06 15:04:39 +00:00
Rodrigodd
95e5c9f33e refactor(treesitter): avoid unnecessarily copying tree
node:tree() now already copies the tree before returning it, for memory
safety reasons.

(cherry picked from commit 744674900f)
2025-06-06 15:04:39 +00:00
Rodrigodd
07d9197840 fix(treesitter): ensure TSNode's tree is immutable
Problem:

TSNode contains a `const TSTree*` and a `const void *id`. The `id`
points to Tree-sitter's internal type `Subtree`, which resides inside
the `TSTree` but may be deallocated if the `TSTree` is mutated (which
is likely why it is `const`).

The Lua method `TSTree:edit()` mutates the tree, which can deallocate
`id`.

See #25254 and #31758.

Solution:

To avoid this, we now make a copy of the tree before pushing its root to
the Lua stack. This also removes the fenv from TSLuaTree, as it was only
used when pushing the tree root to the Lua stack.

We also copy the tree in `node_tree`.

`ts_tree_copy()` just increments a couple of reference counters, so it's
relatively cheap to call.

(cherry picked from commit 99e6294819)
2025-06-06 15:04:39 +00:00
Kai-Hsiang Hsu
c13eba5254 fix(lsp): only auto-detach lsp.config enabled clients #34325
Problem: A custom server (initialized through `vim.lsp.start`) gets
         unexpectedly detached.

Solution: Only auto-detach the clients enabled through `vim.lsp.enable`
          to prevent unexpected behavior.
(cherry picked from commit e5c5b563ec)
2025-06-06 14:07:14 +00:00
brianhuster
d32a4dd4b0 vim-patch:9.1.1404: wrong link to Chapter 2 in new-tutor
Problem:  wrong link to Chapter 2 in vim-01-beginner.tutor
Solution: Fix the link to Chapter 2, add test for links in tutor files
          (Phạm Bình An)

In order to write the test, I exposed the function `s:GlobTutorials` as
`tutor#GlobTutorials` and make it also accept a `locale` argument.

closes: vim/vim#17356

e8302da74a

Co-authored-by: Phạm Bình An <111893501+brianhuster@users.noreply.github.com>
(cherry picked from commit f791ae82e5)
2025-06-06 04:01:34 +00:00
brianhuster
7ef602d470 vim-patch:2323f22: runtime(new-tutor): add chapter two to the interactive tutorial
closes: vim/vim#16803

2323f225ca

Co-authored-by: RestorerZ <restorer@mail2k.ru>
(cherry picked from commit 41c850e2c8)
2025-06-06 04:01:34 +00:00
Phạm Bình An
9d8c5119e2 vim-patch:eb59129: runtime(typescript): remove Fixedgq() function from indent script (#34334)
Problem:
1. The `Fixedgq()` function is broken (see vim/vim#17412)
2. The `'formatexpr'` for Typescript is not documented, which causes
   confusion to users when they try to set `'formatprg'`, since
   `'formatexpr'` always takes precedence over `'formatprg'`. See also
   https://github.com/HerringtonDarkholme/yats.vim/issues/209
3. Typescript already has a very good and popular formatter called
   `prettier`, that can be easily integrated to Vim via `'formatprg'`
   (see vim/vim#16989). I don't think there are any good reasons to reinvent a
   half-baked version in Vim.

Solution:  Remove the Fixedgq() 'formatexpr' function.

fixes: vim/vim#17412
closes: vim/vim#17452

eb59129d2c
(cherry picked from commit 4e71d3bfab)
2025-06-06 03:20:45 +00:00
zeertzjq
f7b1b0595d fix(menu): fix listing of submenus (#34315)
Problem:  Listing submenus with :menu doesn't work.
Solution: Don't go to the parent of the return value of find_menu(), and
          handle empty path at the caller.

Related #8194, which actually only fixed the problem for menu_get(), not
for :menu Ex command.

(cherry picked from commit 5e470c7af5)
2025-06-05 01:48:42 +00:00
Justin M. Keyes
25a869a097 Merge pull request #34305
backport: fix(windows): don't set window icon on SIGHUP
2025-06-04 10:20:05 -07:00
Christian Clason
243c15ea83 ci(release): drop manual shasum collection
This is now included for all GH release assets out-of-the-box, see
https://github.blog/changelog/2025-06-03-releases-now-expose-digests-for-release-assets/

These can be accessed programmatically through
  `gh release view --json assets <release tag>`
and then looking at the `digest` key.
2025-06-04 19:03:35 +02:00
Emanuel Krollmann
33cec55a26 refactor(windows): redundant icon messages #34274
Problem:  Two separate window messages are used to get
          the original console icon and set a new
          one on windows, although the `WM_SETICON`
          message returns the original icon itself.

Solution: Replace the two `WM_GETICON` messages with
          two `WM_SETICON` messages, save the return
          values and remove the call to `os_icon_set`.
          Also, replace `os_icon_set` with `os_icon_reset`
          as its only usage is now resetting the
          icon to the original one.
(cherry picked from commit 7e393ff4f2)
2025-06-04 18:31:31 +02:00
Emanuel Krollmann
e0ddf93bb0 fix(windows): don't set window icon on SIGHUP #34260
Problem:  When using conhost and pressing the 'x' button
          to close it while nvim is open, nvim hangs up
          while trying to reset the window icon, causing a big
          delay before the terminal actually closes. #34171

Solution: Set the window handle to NULL after receiving SIGHUP
          so that nvim will not try resetting the icon.

(cherry picked from commit 52991d8728)
2025-06-04 18:31:30 +02:00
luukvbaal
aa6136f956 fix(api): adjust fix for reconfiguring float "relative" (#34287)
Problem:  "win" is cleared in float config after 96330843, even with
          unchanged "relative".
Solution: Don't clear "win". Avoid erroring for deleted "win" by setting
          the parent win to curwin directly when "win" is zero or not
          present in config.
(cherry picked from commit eeacd7bd71)
2025-06-03 11:55:51 +00:00
zeertzjq
222b3d5021 vim-patch:bfeefc4: runtime(doc): clarify the effect of exclusive single char selections (#34289)
closes: vim/vim#17410

bfeefc474a

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit aa4fa24963)
2025-06-03 00:36:20 +00:00
luukvbaal
0d66963089 fix(api): reconfiguring float "relative" does not clear "win" (#34271)
Problem:  Unable to change the "relative" of a flag after its target
          "win" no longer exists.
Solution: Unset target window if it is not present in config and
          reconfigured "relative" no longer expects a target window.
(cherry picked from commit 963308439a)
2025-06-02 14:09:14 +00:00
glepnir
f2c4305114 fix(api): add missing nargs field to user command Lua callbacks #34210
Problem: nvim_create_user_command() Lua callbacks were missing the documented nargs field in the options table passed to the callback function.

Solution: Add nargs field derivation from command argument type flags in nlua_do_ucmd(), using the same logic as nvim_parse_cmd().
(cherry picked from commit 5cfbc35aa8)
2025-06-01 23:18:32 +00:00
Justin M. Keyes
d519b77d2b docs: news, intro, lsp, api #34264 2025-06-01 23:16:54 +00:00
zeertzjq
d9c10ea753 fix(redraw): update curswant for Visual selection (#34241)
Problem:  Blockwise Visual selection not redrawn correctly when moving
          cursor for more than 1 cells with 'virtualedit'.
Solution: Restore the curswant update removed in 6679687bb3.
(cherry picked from commit 7ed8e96994)
2025-05-31 00:11:49 +00:00
Christian Clason
1eb33f7d00 ci(release): bump windows runner to windows-2022
Windows-20219 runner will be retired June 30, 2025 and receive several
brownouts before then.

(cherry picked from commit 9e81408b69)
2025-05-30 17:08:05 +00:00
Phạm Bình An
92dc936ca7 docs: rename builtin.txt, eval.txt #34212
Problem:
Despite the name, `builtin.txt` only includes Vimscript functions, which
is confusing, especially to people who only use Lua to configure Nvim

Solution: From justinmk's suggestion
- Rename `builtin.txt` to `vimfn.txt`
- Rename `eval.txt` to `vimeval.txt`
- The tags `*builtin.txt*` and `*eval.txt*` should be kept for Vim-patches

Closes #33743

(cherry picked from commit 9d5eb3eda5)
2025-05-30 16:27:49 +00:00
phanium
68677eb477 fix(terminal): stack overflow when too many csi args (#34012)
fix: stack overflow when too many csi args

Problem:
Crash when csi contains > 16 args. It's easy to trigger
when you attempt to pipe external terminal scrollback buffer.
```sh
nvim --clean --cmd "term printf
'\e[38:2:59:66:97;48:2:36:40:59;58:2:224:175:104;4:3m'"
```

Solution:
Increase buffer size.

(cherry picked from commit 756751afa3)
2025-05-30 12:28:38 +00:00
bfredl
f4a79230c8 version bump 2025-05-30 11:41:44 +02:00
bfredl
a73904168a NVIM 0.11.2
This is a maintenance release, focusing on bug fixes. Some enhancements related
to vim.lsp.enable are also included.

FEATURES
--------------------------------------------------------------------------------
- 4e43264cd3 lsp: vim.lsp.is_enabled() #33703
- c4b9bdbdf4 lsp: start/stop LSPs as necessary during vim.lsp.enable() #33702
- 216c56b7e0 lsp: detach LSP clients when 'filetype' changes #33707
- 533ec6d492 lsp: `root_markers` can control priority
- ad7211ac8f checkhealth: trigger FileType event after showing report
- f25f6c8d13 health: summary in section heading #33388

FIXES
--------------------------------------------------------------------------------
- 710d561f88 lsp: don't eagerly enable LSP configs during startup #33762
- 0ed06d7271 lsp: check if client is stopping before reuse #33796
- f184c562c5 lsp: detect if Client:request resolved synchronously #33624
- b868257ef9 lsp: fix error with InsertReplaceEdit events #33973
- 6b69b3217b lsp: improper diagnostic end_col computation
- e512c9589e lsp: improve error completion message #33812
- 47686a1454 lsp: only auto-detach lsp.config clients #33834
- 901eeeb2e6 lsp: use `bufnr` when getting clients in `symbols_to_items` (#33760)
- a242902430 :print: don't use schar_from_ascii() for illegal byte (#34046)
- 4b6caa913c cmdline: do not move UI cursor when entering cmdline #33729
- fd8e0ae62d decor: extmark highlight not applied (#33858)
- 81233a41d7 display: adjust setting winline info for concealed lines (#33717)
- 4cb2b19197 folds: adjust filler text drawing for transparent folds
- bdd8498ed7 folds: avoid unnecessary loop with horizontal scrolling (#33932)
- 32842b0ee3 health: checkhealth float opens extra empty buffer #33648
- dc87a0d80a lua: vim.validate `message` param #33675
- 334d8f506f move: consume skipcol before revealing filler lines (#34143)
- 6a87b57c06 runtime: 'includeexpr' with non-Nvim-style Lua modules #33867
- 2b2a3449f7 runtime: conceal paths in help, man ToC loclist #33764
- 3db39ed21f runtime: cpoptions is reset in Lua file #33671
- cefc91a82e system: don't treat NUL at start as no input (#34167)
- 8daffd0cbb terminal: check size when switching buffers
- 0db89468d7 termkey: out-of-bounds write in array #33868
- 6563c6bde7 treesitter: close `:InspectTree` with `q`
- 5c6ee251a6 treesitter: eliminate flicker for single windows #33842
- 3b3cf1d7ef treesitter: invalidate conceal_lines marks (#33832)
- 58460e2d52 treesitter: parser metadata annotations
- 034d3c8f6c treesitter: proper tree `contains()` logic with combined injections
- 560c6ca947 trust: support for trusting directories #33735
- 12ae7aa846 tui: clear primary device callback before invoking it (#34032)
- 4296511087 tui: don't process UI events when suspending or stopping (#33710)
- cf73f21c07 tui: don't try to add unsupported modifiers (#33799)
- 465c181581 tui: forward C0 control codes literally (#33759)
- 0c2bf55e90 tutor: l:lang is undefined
- 3a0d37681f vim.system: improve error message when cwd does not exist
- 9b3426691c window: skip unfocusable and hidden floats with "{count}<C-W>w" #33810

VIM PATCHES
--------------------------------------------------------------------------------
- 9965cfb84c 9.1.1361: [security]: possible use-after-free when closing a buffer (#33820)
- 3c102303f5 9.1.1375: [security]: possible heap UAF with quickfix dummy buffer
- 1921dda92e 3704b5b: runtime(tutor): improve tutor.vim plugin and filetype plugin
- 4e5af2f5a6 5a8f995: runtime(doc): remove outdated Contribution section in pi_tutor (#34094)
- 3273c595c0 829eda7: runtime(new-tutor): update tutor and correct comandline completion
- 3e83a33108 9.1.1297: Ctrl-D scrolling can get stuck #33453
- 6417ba0c2f 9.1.1376: quickfix dummy buffer may remain as dummy buffer
- 30fa1c5f8c 9.1.1380: 'eventignorewin' only checked for current buffer
- 6b140ae899 9.1.1384: still some problem with the new tutors filetype plugin
- f623fad9c4 9.1.1385: inefficient loop for 'nosmoothscroll' scrolling (#33992)
- d50f71d2f1 9.1.1387: memory leak when buflist_new() fails to reuse curbuf
- 27abf5c81b 9.1.1388: Scrolling one line too far with 'nosmoothscroll' page scrolling (#34023)
- 917f496f75 9.1.1395: search_stat not reset when pattern differs in case (#34058)
- b07bffdc47 9.1.1402: multi-byte mappings not properly stored in session file (#34131)
- ff83c712cf 9.1.1405: tests: no test for mapping with special keys in session file (#34146)
- d1ca551983 9.1.1407: Can't use getpos('v') in OptionSet when using setbufvar() (#34177)

DOCUMENTATION
--------------------------------------------------------------------------------
- e5e69f758d add missing change to getcharstr() signature (#33797)
- 95ee908c40 backport #33549 and #33524 to 0.11 (#33678)
- 472d41b5b6 default mappings #33706
- 3a4d3934c4 fixups (#33815)
- fa292e6f61 lsp, emoji, startup #33683
- 714622fb45 lsp, lua #33682
- d68d212ad4 provide example_init.lua #33524
- 968947b3c3 lua: typing for vim.fn.winlayout #33817
- e304677993 tutor: move lesson 7.2 below lesson 7.3 #33662
2025-05-30 11:39:24 +02:00
Christian Clason
58460e2d52 fix(treesitter): parser metadata annotations
Problem: `TSLangInfo` annotation does not reflect the structure returned
by `vim.treesitter.language.inspect()`.

Solution: Move version information under new (optional since ABI 15 only)
`TSLangMetadata` field.

(cherry picked from commit f82219c490)
2025-05-29 15:28:00 +00:00
Lewis Russell
3a0d37681f fix(vim.system): improve error message when cwd does not exist
Problem:
vim.uv.spawn will emit ENOENT for either when the cmd or cwd do not
exist and does not tell you which.

Solution:
If an error occurs, check if cwd was supplied and included in the error
message if it does not exist.

(cherry picked from commit 532610388b)
2025-05-29 13:29:23 +00:00
Sean Dewar
4cb2b19197 fix(folds): adjust filler text drawing for transparent folds
Problem: Search highlighting is applied strangely to the filler text of
transparent folds, and EOL list characters are not shown.

Solution: Don't apply search highlighting to the last column of the window row
if the last text char on the line is highlighted. Display the EOL list char if
needed. Don't highlight the entire filler text when matching EOL, just highlight
the EOL list char or the first filler char.

(cherry picked from commit 66dddd8b51)
2025-05-27 09:40:55 +01:00
zeertzjq
d1ca551983 vim-patch:9.1.1407: Can't use getpos('v') in OptionSet when using setbufvar() (#34177)
Problem:  Can't use getpos('v') in OptionSet when using setbufvar().
Solution: Don't reset Visual selection when switching to the same
          buffer (zeertzjq).

closes: vim/vim#17373

5717ee33db
(cherry picked from commit bd01bd6564)
2025-05-25 23:17:15 +00:00
zeertzjq
5e0ef6afc7 Merge pull request #34168 from zeertzjq/backport
fix(system): don't treat NUL at start as no input (#34167)
2025-05-25 10:21:25 +08:00
zeertzjq
cefc91a82e fix(system): don't treat NUL at start as no input (#34167) 2025-05-25 09:32:45 +08:00
luukvbaal
334d8f506f fix(move): consume skipcol before revealing filler lines (#34143)
Problem:  When scrolling (the text) down with 'smoothscroll', filler
          lines are revealed before the text skipped with `w_skipcol`.
Solution: Check `w_skipcol` before filler lines.

(cherry picked from commit 6ce2877327)
2025-05-23 23:14:18 +00:00
zeertzjq
ff83c712cf vim-patch:9.1.1405: tests: no test for mapping with special keys in session file (#34146)
Problem:  tests: no test for mapping with special keys in session file.
Solution: Add a special keys to an existing test.  Also test with UTF-8
          characters containing 0x80 or 0x9b bytes (zeertzjq).

closes: vim/vim#17360

9ff1e598e8
(cherry picked from commit 071dcab68f)
2025-05-23 23:04:01 +00:00
zeertzjq
b07bffdc47 vim-patch:9.1.1402: multi-byte mappings not properly stored in session file (#34131)
Problem:  multi-byte mappings not properly stored in session file
Solution: unescape the mapping before writing out the mapping, prefer
          single-byte mapping name if possible (Miguel Barro)

closes: vim/vim#17355

5b07aff2f6

Co-authored-by: GuyBrush <miguel.barro@live.com>
(cherry picked from commit 153a910897)
2025-05-23 00:15:14 +00:00
Evan Hahn
b868257ef9 fix(lsp): fix error with InsertReplaceEdit events #33973
Problem:
Some LSPs cause the following completion error (reformatted slightly):

    Error executing vim.schedule lua callback:
    .../runtime/lua/vim/lsp/completion.lua:373
    attempt to index field 'range' (a nil value)

This is because an internal function assumes edits are either missing
or of type `TextEdit`, but there's a third [possibility][0] that's not
handled: the `InsertReplaceEdit`.

This was previously reported in at least two issues:

- https://github.com/neovim/neovim/issues/33142
- https://github.com/neovim/neovim/issues/33224

Solution:
Don't assume the edit is a `TextEdit`. This implicitly handles
`InsertReplaceEdit`s.

Also, add a test case for this, which previously caused an error.

[0]: 2c07428966/runtime/lua/vim/lsp/_meta/protocol.lua (L1099)

(cherry picked from commit 927927e143)
2025-05-22 13:50:07 +00:00
Phạm Bình An
e304677993 docs(tutor): move lesson 7.2 below lesson 7.3 #33662
Problem:

- Lesson 7.3 (Cmdline Completion) teaches an important way to discover
  Nvim features. I think users should learn it before they start
  configuring Nvim
- Nvim can be configured in Lua as well, but lesson 7.2 (Configuring
  Nvim) only mentions init.vim. And I think Nvim is promoting Lua more

Solution:

- Move lesson 7.2 to be after lesson 7.3
- Lesson 7.2 should teach about init.lua

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
(cherry picked from commit dd43eb445a)
2025-05-21 09:51:16 +00:00
Phạm Bình An
4e5af2f5a6 vim-patch:5a8f995: runtime(doc): remove outdated Contribution section in pi_tutor (#34094)
Problem:  The Github repo link in the Contribution section has been
          archived for 5 years. So people who want to contribute to the
          tutor plugin should just send PR to Vim repo, similar to most
          other Vim features, so there is no need for a Contribution
          section in the plugin doc.

Solution: Replace it with an Original Author note at the beginning of
          the help document.

closes: vim/vim#17341

5a8f9958e2
(cherry picked from commit 1524868711)
2025-05-20 01:16:27 +00:00
zeertzjq
917f496f75 vim-patch:9.1.1395: search_stat not reset when pattern differs in case (#34058)
Problem:  search_stat not reset when pattern differs in case
          (tahzibijafar)
Solution: use STRNCMP instead of MB_STRNICMP macro

There was a long standing todo comment, that using MB_STRNICMP is wrong.
So let's change it to STRNCMP() instead. Even if it not handle
multi-byte characters correctly, then Vim will rather recompute the
search stat, instead of re-using the old (and possibly wrong) value.

fixes: vim/vim#17312
closes: vim/vim#17314

670d0c1468

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit ec5f054dc9)
2025-05-17 00:12:13 +00:00
zeertzjq
a242902430 fix(:print): don't use schar_from_ascii() for illegal byte (#34046)
(cherry picked from commit 6af1b7e5e8)
2025-05-16 23:03:09 +00:00
zeertzjq
12ae7aa846 fix(tui): clear primary device callback before invoking it (#34032)
A primary device callback may set a new callback (e.g. when suspending),
so clearing it after invoking it is too late.

While at it, add missing reset of did_set_grapheme_cluster_mode in
terminfo_start().

(cherry picked from commit 17e13ce3b6)
2025-05-15 13:13:02 +00:00
luukvbaal
27abf5c81b vim-patch:9.1.1388: Scrolling one line too far with 'nosmoothscroll' page scrolling (#34023)
Problem:  One-off error in "count" to make "w_skipcol" zero with
          'nosmoothscroll' page scrolling when last virtual line
          in a buffer line is exactly the entire window width.
          (Hirohito Higashi)
Solution: Properly compute the smallest integer value necessary
          to make "w_skipcol" zero (Luuk van Baal)

c6c72d165c
(cherry picked from commit f87b6230f1)
2025-05-15 08:24:06 +00:00
Sean Dewar
d50f71d2f1 vim-patch:9.1.1387: memory leak when buflist_new() fails to reuse curbuf
Problem:  buflist_new() leaks ffname and fails to reuse curbuf when
          autocommands from buf_freeall change curbuf. Plus, a new
          buffer is not allocated in this case, despite what the comment
          above claims.
Solution: Remove the condition so ffname is not leaked and so a new
          buffer is allocated like before v8.2.4791. It should not be
          possible for undo_ftplugin or buf_freeall autocommands to
          delete the buffer as they set b_locked, but to stay consistent
          with other uses of buf_freeall, guard against that anyway
          (Sean Dewar).

Note that buf is set to NULL if it was deleted to guard against the (rare)
possibility of messing up the "buf != curbuf" condition below if a new buffer
happens to be allocated at the same address.

closes: vim/vim#17319

0077282c82

Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
(cherry picked from commit 6b9665a507)
2025-05-15 08:14:43 +00:00
neovim-backports[bot]
f623fad9c4 vim-patch:9.1.1385: inefficient loop for 'nosmoothscroll' scrolling (#33992)
Problem:  Loop that ensures "w_skipcol" is zero with 'nosmoothscroll'
	  for (half)-page scrolling is inefficient.
Solution: Calculate the required "count" instead of looping until
	  "w_skipcol" is zero (Luuk van Baal).

acf0ebe8a8
(cherry picked from commit d539a952da)
---------

Co-authored-by: luukvbaal <luukvbaal@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2025-05-13 10:43:25 +02:00
luukvbaal
3e83a33108 vim-patch:9.1.1297: Ctrl-D scrolling can get stuck #33453
Problem:  cursor_correct() calculates a valid cursor position which
	  is later changed by update_topline() and causes Ctrl-D
          scrolling to be stuck (Daniel Steinberg, after v9.1.0258).
Solution: Update the valid cursor position before validating topline
          (Luuk van Baal).

c98250377d
(cherry picked from commit aa47c8efa9)
2025-05-13 07:02:50 +00:00
brianhuster
0c2bf55e90 fix(tutor): l:lang is undefined
Problem:
The scope `elseif $LC_MESSAGES =~ '\a\a' || $LC_MESSAGES ==# "C"` in
function `s:Locale` in file `runtime/autoload/tutor.vim` leaves a case
when l:lang is not defined.

Solution:
Define `l:lang` at function scope instead of `if` scope

(cherry picked from commit e5665754d1)
2025-05-13 02:35:41 +00:00
brianhuster
6b140ae899 vim-patch:9.1.1384: still some problem with the new tutors filetype plugin
Problem:  still some problem with the new tutors filetype plugin
Solution: refactor code to enable/disable tutor mode into
          tutor#EnableInteractive() function, include a test
          (Phạm Bình An)

I find it annoying that Tutor's interactive mode is always on (or debug
mode is off) even when I open a tutor file with :edit command.
I think it makes more sense to make this "interactive mode":

- Always on when it is opened with :Tutor command
- Off otherwise

For more references, see `:help` feature, it is a much better than
:Tutor, since I don't have to run `:let g:help_debug = 1` just to be able
to edit and save a help file

Therefore, I remove `g:tutor_debug`

closes: vim/vim#17299

13bea589a2

Co-authored-by: Phạm Bình An <phambinhanctb2004@gmail.com>
(cherry picked from commit e01f196e44)
2025-05-13 02:35:41 +00:00
brianhuster
1921dda92e vim-patch:3704b5b: runtime(tutor): improve tutor.vim plugin and filetype plugin
- Set g:tutor_debug on startup if it doesn't exist so that users can get
  cmdline completion when interactively setting it.
- set b:undo_ftplugin in filetype plugin
- set default runtime file headers

closes: vim/vim#17274

3704b5b58a

Co-authored-by: Phạm Bình An <phambinhanctb2004@gmail.com>
(cherry picked from commit 238e1d6ecc)
2025-05-13 02:35:41 +00:00
Sean Dewar
30fa1c5f8c vim-patch:9.1.1380: 'eventignorewin' only checked for current buffer
Problem:  When an autocommand executes for a non-current buffer,
          'eventignorewin' is only checked from the buffer's last
          wininfo (overwrites win_ignore in the loop), not from the
          value of 'eventignorewin' in all windows showing the buffer as
          described (after v9.1.1084)

Solution: Fix the check and don't use wininfo, as that may only contain
          windows that recently showed the buffer. Consider all the
          buffer's windows in all tabpages (Sean Dewar).

closes: vim/vim#17294

d4110e0695

Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
(cherry picked from commit ef5c5dc99b)
2025-05-11 22:48:42 +00:00
Sean Dewar
6417ba0c2f vim-patch:9.1.1376: quickfix dummy buffer may remain as dummy buffer
Problem:  when failing to wipeout a quickfix dummy buffer, it will
          remain as a dummy buffer, despite being kept.
Solution: clear its dummy BF_DUMMY flag in this case (Sean Dewar).

closes: vim/vim#17283

270124f46a

Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
(cherry picked from commit d95b0a5396)
2025-05-11 16:38:20 +01:00
Sean Dewar
3c102303f5 vim-patch:9.1.1375: [security]: possible heap UAF with quickfix dummy buffer
Problem:  heap use-after-free possible when autocommands switch away from the
          quickfix dummy buffer, but leave it open in a window.
Solution: close its windows first before attempting the wipe.
          (Sean Dewar)

related: vim/vim#17283

b4074ead5c

Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
(cherry picked from commit 05dab80d8d)
2025-05-11 16:38:20 +01:00
Riley Bruins
034d3c8f6c fix(treesitter): proper tree contains() logic with combined injections
**Problem:** `LanguageTree:contains()` considers any range within the
start of the first tree and end of the last tree as "within" the
language tree. In the case of combined injections, this is problematic
because we only want to consider ranges within any of the combined trees
as "contained" (as opposed to any range within the entire range spanned
by all combined trees).

**Solution:** Use a more discriminative check in
`LanguageTree:contains()`.

(cherry picked from commit de45b8e275)
2025-05-11 07:32:19 +00:00
Sean Dewar
9965cfb84c vim-patch:9.1.1361: [security]: possible use-after-free when closing a buffer (#33820)
Problem:  [security]: Possible to open more windows into a closing
          buffer without splitting, bypassing existing "b_locked_split"
          checks and triggering use-after-free
Solution: Disallow switching to a closing buffer. Editing a closing
          buffer (via ":edit", etc.) was fixed in v9.1.0764, but add an
          error message and check just "b_locked_split", as "b_locked"
          is necessary only when the buffer shouldn't be wiped, and may
          be set for buffers that are in-use but not actually closing.
          (Sean Dewar)

closes: vim/vim#17246

6cb1c82840
(cherry picked from commit 627c648252)
2025-05-10 17:07:31 +00:00
zeertzjq
bdd8498ed7 fix(folds): avoid unnecessary loop with horizontal scrolling (#33932)
Fix #33931

(cherry picked from commit 1d9990daac)
2025-05-10 03:21:57 +00:00
Sean Dewar
8daffd0cbb fix(terminal): check size when switching buffers
Problem: terminal not always resized when switching to its buffer.
Solution: add missing calls to terminal_check_size.

Adjust screen test for v0.11.
(cherry picked from commit e56292071a)
2025-05-09 17:49:50 +00:00
Artem
fd8e0ae62d fix(decor): extmark highlight not applied (#33858)
Problem: If the only highlight present in the buffer is an extmark, and its end
position is outside the screen, redraws that start on lines after the
first line of the mark will consider the buffer as not having any highlights,
and skip drawing the mark's highlight.
Solution: Check the updated number of decor ranges.

(cherry picked from commit 6adf48b66d)
2025-05-09 11:02:31 +00:00
zeertzjq
a56dcbbad7 test(swapfile): don't check for line with full file path (#33896)
Wrapping can happen anywhere where in the full file path, breaking the
matching. Match a line with the "Xswaptest" line instead.

(cherry picked from commit 1b8ae4336d)
2025-05-08 02:45:56 +00:00
Phạm Bình An
6a87b57c06 fix(runtime): 'includeexpr' with non-Nvim-style Lua modules #33867
Closes #33862

(cherry picked from commit db2b774a16)
2025-05-07 01:06:12 +00:00
Riley Bruins
5c6ee251a6 fix(treesitter): eliminate flicker for single windows #33842
This commit will eliminate flicker while editing one open window. It
works by querying previously calculated trees for highlight marks, in
the case that we are still asynchronously parsing in `on_win`.

It will not fully solve the case of flicker when there are multiple open
windows, since the parser will drop previously parsed injection trees
whenever it receives a call to parse a different region of the buffer.
This will require a refactor of `languagetree.lua`.

(cherry picked from commit 8d75910ef9)
2025-05-06 15:22:43 +00:00
gcrtnst
0db89468d7 fix(termkey): out-of-bounds write in array #33868
Problem:
termkey crashes due to an out-of-bounds write in an array when it
received a CSI sequence with 17 or more arguments. This could be
observed on startup with certain terminal emulators like [RLogin], which
send a response to the `CSI c` query containing 17 parameters.

The termkey code has a boundary check, but its comparison operator is
incorrect.

Solution:
Correct the comparison operator to ensure proper boundary checking.

With this change, I have confirmed that the crash no longer occurs on
RLogin. https://github.com/kmiya-culti/RLogin

Fixes #24356

(cherry picked from commit 8707ec2644)
2025-05-06 12:54:26 +00:00
Justin M. Keyes
dc87a0d80a fix(lua): vim.validate message param #33675
Problem:
vim.validate does not handle `message` param.

Solution:
Add the missing logic.

(cherry picked from commit 40351bbbbe)
2025-05-05 00:09:31 +00:00
zeertzjq
c753e70abb test(lua/secure_spec): avoid magic number (#33700)
Avoid magic number in skipping condition by moving the expected message
to a variable.

(cherry picked from commit c489b5a3e3)
2025-05-04 23:50:15 +00:00
Eike
0ed06d7271 fix(lsp): check if client is stopping before reuse #33796
Problem:
Stopping a language server and then calling vim.lsp.start() with the same name/root will return the old language server that's in the middle of shutting down. vim.lsp.start() won't return a new server until the old process has terminated.

Solution:
Introducing a client._is_stopping field that tracks the shutdown phase, preventing the client from being reused.

(cherry picked from commit 0862c1036a)
2025-05-04 22:26:30 +00:00
glepnir
e512c9589e fix(lsp): improve error completion message #33812
problem: Error notifications from LSP responses were difficult to read due to
inconsistent formatting and missing critical information like client name and error codes.

solution: Implemented a more structured error notification format using pipe separators to
clearly display client name, error code (when available), and error message.

(cherry picked from commit 5c15df449a)
2025-05-04 17:38:54 +00:00
glepnir
47686a1454 fix(lsp): only auto-detach lsp.config clients #33834
Problem:
enable() routine detaches clients even if they were manually started
and not managed by vim.lsp.config.

Solution:
Skip clients that aren't managed by vim.lsp.config.

(cherry picked from commit 91e116f3a6)
2025-05-04 13:39:40 +00:00
luukvbaal
3b3cf1d7ef fix(treesitter): invalidate conceal_lines marks (#33832)
Problem:  Spliced conceal_lines marks after changing the buffer text are
          left valid, concealing lines that shouldn't be.
Solution: Set the `invalidate` extmark property.
(cherry picked from commit 9274532615)
2025-05-03 23:41:23 +00:00
Phạm Bình An
472d41b5b6 docs: default mappings #33706
Problem:
Docs don't mention that `gc` is just a mapping, not
a builtin normal-mode command.

Solution:
Update docs for all "default mappings".

(cherry picked from commit 2c1f5a6aa5)
2025-05-03 23:07:06 +00:00
Jeremy Fleischman
216c56b7e0 feat(lsp): detach LSP clients when 'filetype' changes #33707
Problem:
When the buffer 'filetype' changes, invalid or non-applicable LSP
clients are not detached.

https://github.com/neovim/neovim/issues/33443
https://github.com/neovim/nvim-lspconfig/issues/3326

Solution:
In the enable() routine, check can_start() on _existing_ clients.

(cherry picked from commit b877aa34cf)
2025-05-03 22:49:53 +00:00
PRIZ ;]
968947b3c3 docs(lua): typing for vim.fn.winlayout #33817
Problem:
`any[]` means nothing, and the return value is not the same as what's
documented in the comment (eg, Lua returns `{ "row", { { "leaf", 1000 },
{ "leaf", 1001 } } }`, not `{ "row", { "leaf", 1000, "leaf", 1001 } }`)

Solution:
Create two classes (vim.fn.winlayout.leaf and vim.fn.winlayout.branch)
and one alias that links the two together.

Also: Due to LuaLS limitations, there is an empty class,
vim.fn.winlayout.empty

Signed-Off-By: VoxelPrismatic <voxelprismatic@pm.me>
(cherry picked from commit 902b689c4d)
2025-05-03 19:10:02 +00:00
Sean Dewar
9b3426691c fix(window): skip unfocusable and hidden floats with "{count}<C-W>w" #33810
Problem: Using `<C-W>w`, `<C-W>W` or the ":wincmd" variants with a count can
enter unfocusable or hidden floating windows. This is especially problematic
when using the new in-development extui, which creates many unfocusable floats
for various UI elements.

Solution: Skip unfocusable and hidden floating windows. Instead, skip to the
next focusable, non-hidden window in the current tabpage's window list. Reword
the documentation a bit (hopefully an improvement?)

(cherry picked from commit 403fcacfc1)
2025-05-03 19:02:39 +00:00
Jeremy Fleischman
4e43264cd3 feat(lsp): vim.lsp.is_enabled() #33703
Problem:
No way to check if a LSP config is enabled without causing it to
resolve. E.g. `vim.lsp.config['…'] ~= nil` will resolve the config,
which could be an unwanted and somewhat expensive side-effect.

Solution:
Introduce `vim.lsp.is_enabled()`.

(cherry picked from commit 03d378fda6)
2025-05-03 17:45:39 +00:00
Sean Dewar
3a4d3934c4 docs: fixups (#33815)
- Add missing diagnostics virtual lines hl groups.
- Fix LSP dynamic registration example; curbuf may not actually be attached to
  the client, and it may be attached to many such buffers.

(cherry picked from commit 94bc7f47bf)
2025-05-03 17:23:08 +01:00
Michele Campeotto
2b2a3449f7 fix(runtime): conceal paths in help, man ToC loclist #33764
Problem:
The check for concealing paths in TOCs in the qf syntax file fails
because the TOC tile has changed.

Solution:
Force the qf syntax file to be reloaded after the qf_toc variable
has been set, so that the it can apply the correct settings.

Using the explicit qf_toc key, already used in the syntax file, instead
of the title is less prone to breaking.

It was also already being set for man pages but it had no effect because
the syntax file had already been loaded when the variable was set.

Fixes #33733

(cherry picked from commit f048298e9a)
2025-05-03 14:33:34 +00:00
Riley Bruins
6b69b3217b fix(lsp): improper diagnostic end_col computation
**Problem:** For multiline diagnostics, the end column was improperly
calculated by checking the byte index of the character position on the
*start* line.

**Solution:** Calculate the byte index for end_col using the *end* line.

(cherry picked from commit 5d1fd4aca5)
2025-05-03 08:28:03 +00:00
zeertzjq
cf73f21c07 fix(tui): don't try to add unsupported modifiers (#33799)
Problem:  The TUI doesn't forward a key properly when it has unsupported
          modifiers like NumLock.
Solution: Don't try to add modifiers when only unsupported modifiers are
          present.

Related #33791

(cherry picked from commit adbd33027f)
2025-05-03 04:37:40 +00:00
zeertzjq
e5e69f758d docs: add missing change to getcharstr() signature (#33797)
(cherry picked from commit 862e676efc)
2025-05-03 00:51:46 +00:00
Maria José Solano
901eeeb2e6 fix(lsp): use bufnr when getting clients in symbols_to_items (#33760)
(cherry picked from commit 34c769dd89)
2025-05-02 21:07:05 +00:00
Christian Clason
6563c6bde7 fix(treesitter): close :InspectTree with q
Problem: `:InspectTree` window does not follow precedent for focused
"info windows" (like `checkhealth`, `Man`, etc.).

Solution: Map `q` to `<C-w>c`.
(cherry picked from commit 5a2edc483d)
2025-05-02 20:06:26 +00:00
zeertzjq
465c181581 fix(tui): forward C0 control codes literally (#33759)
This fixes the problem that sending a raw C0 control code to trigger a
mapping for it does not work in Terminal mode.

Note: this isn't done for 00 or 7F, as that'll be backward-incompatible.
(cherry picked from commit 4b5364b423)
2025-05-02 10:16:30 +00:00
Phạm Bình An
710d561f88 fix(vim.lsp.enable): don't eagerly enable LSP configs during startup #33762
closes #33761

(cherry picked from commit 1fb0126a08)
2025-05-02 09:59:07 +00:00
luukvbaal
81233a41d7 fix(display): adjust setting winline info for concealed lines (#33717)
Problem:  Wrong winline info after partial redraw. Setting
          `conceal_cursor_used` is unnecessarily spread out.
Solution: Rather than adjusting `wl_lastlnum` for the previous
          winline, adjust it when setting the current winline.
          Set `conceal_cursor_used` when the current window is redrawn.
(cherry picked from commit 97a6259442)
2025-05-01 12:05:15 +02:00
Jeremy Fleischman
c4b9bdbdf4 feat(lsp): start/stop LSPs as necessary during vim.lsp.enable() #33702
Problem:
enable() could be more flexible, so that it works even if called "late".

Solution:
- enable(true) calls `doautoall nvim.lsp.enable FileType`.
- enable(false) calls `client:stop()` on matching clients.

This will be useful for e.g. :LspStop/:LspStart also.

(cherry picked from commit 4bc7bac884)
2025-05-01 00:24:08 +00:00
Jeremy Fleischman
560c6ca947 fix(trust): support for trusting directories #33735
Problem:
Directories that are "trusted" by `vim.secure.read()`, are not detectable later
(they will prompt again). https://github.com/neovim/neovim/discussions/33587#discussioncomment-12925887

Solution:
`vim.secure.read()` returns `true` if the user trusts a directory.

Also fix other bugs:

- If `f:read('*a')` returns `nil`, we treat that as a successful read of
  the file, and hash it. `f:read` returns `nil` for directories, but
  it's also documented as returning `nil` "if it cannot read data with the
  specified format". I reworked the implementation so we explicitly
  treat directories differently. Rather than hashing `nil` to put in the
  trust database, we now put "directory" in there explicitly*.
- `vim.secure.trust` (used by `:trust`) didn't actually work for
  directories, as it would blindly read the contents of a netrw buffer
  and hash it. Now it uses the same codepath as `vim.secure.read`, and
  as a result, works correctly for directories.

(cherry picked from commit 272dba7f07)
2025-04-30 14:35:41 -07:00
luukvbaal
4b6caa913c fix(cmdline): do not move UI cursor when entering cmdline #33729
Problem:  Cursor is still moved to curwin when entering cmdline (after d41b8d47).

Solution: Remove call to `setcursor()`.
(cherry picked from commit 0015a105ca)
2025-04-30 14:38:40 +00:00
Justin M. Keyes
0fbc78caa1 Merge #33734 from justinmk/release
backport: LSP root_markers, docs
2025-04-30 07:21:21 -07:00
Lorenzo Bellina
533ec6d492 feat(lsp): root_markers can control priority
Problem:
root_markers cannot specify "equal priority filenames.

Solution:
Support nesting:

    {
      ...
      root_markers = { { ".stylua.toml", ".luarc.json" }, { ".git "} }
      ...
    }

Co-authored-by: Maria José Solano <majosolano99@gmail.com>
Co-authored-by: Gregory Anders <github@gpanders.com>
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-04-30 15:59:00 +02:00
Justin M. Keyes
9dfb429e93 test: drop redundant clear() #33654
followup to #33647 after overlapping merge
2025-04-30 15:58:03 +02:00
Justin M. Keyes
051e14d347 test: drop redundant clear() #33647
Problem:
Tests call `clear()` even though `clear_notrace()` is already called in
a `before_each()` handler, wasting precious milliseconds!

Solution:
Remove redundant `clear()` calls.
2025-04-30 15:57:55 +02:00
Justin M. Keyes
714622fb45 docs: lsp, lua #33682
- sort fields alphabetically.
- in the `vim.lsp.Client` docs, reference `vim.lsp.ClientConfig` instead
  of duplicating its docs.
- cleanup lots of redundant-yet-drifted field docs.
2025-04-30 15:51:38 +02:00
zeertzjq
4296511087 fix(tui): don't process UI events when suspending or stopping (#33710)
When the TUI is suspending or stopping, redraw events should not be
processed, as when it next processes redraw events it's already waiting
for a DA1 response after having disabled some terminal modes.

Fix #33708

(cherry picked from commit c35dde03c8)
2025-04-29 10:04:38 +00:00
Justin M. Keyes
f9c7a69cec Revert "fix(desktop): cannot open filename with spaces using OS file manager" #33684
This reverts commit 6e12ef4a7b

> Paths with spaces were already working. The original bug is most
> likely with user's terminal desktop entry, file manager or DE, and has
> nothing to do with nvim.desktop.

These are 3 different implementations that work correctly with unquoted %F and spaces:
```
$ DE=generic xdg-open "D I R/F I L E.txt" # pure bash
$ gio open "D I R/F I L E.txt" # glib2
$ handlr open "D I R/F I L E.txt" # rust
```

(cherry picked from commit 07a207a5f1)
2025-04-27 23:20:07 +00:00
Justin M. Keyes
fa292e6f61 docs: lsp, emoji, startup #33683
Co-authored-by: Maria José Solano <majosolano99@gmail.com>
2025-04-27 23:00:36 +00:00
Justin M. Keyes
bc66a5ff6f Merge pull request #33680 from justinmk/release 2025-04-27 15:32:22 -07:00
Yochem van Rosmalen
f25f6c8d13 feat(health): summary in section heading #33388
Problem:
As checkhealth grows, it is increasingly hard to quickly glance through
the information.

Solution:
Show a summary of ok, warn, and error outputs per section.
2025-04-27 22:35:39 +02:00
Justin M. Keyes
ad7211ac8f feat(checkhealth): trigger FileType event after showing report
Problem:
`FileType` event is fired before checkhealth report is finished, so
user can't override report settings or contents.
https://github.com/neovim/neovim/pull/33172#issuecomment-2833513916

Solution:
- Trigger FileType event later.
- Document how to remove emojis.
2025-04-27 20:39:14 +02:00
Justin M. Keyes
95ee908c40 docs: backport #33549 and #33524 to 0.11 (#33678)
* vim-patch:829eda7: runtime(new-tutor): update tutor and correct comandline completion

Problem: Some parts of the tutor are outdated.

- For example, pressing `<Tab>` after typing `:e` does not complete the
command `:edit`, but shows a completion menu with the first entry being
`:earlier`.

closes: vim/vim#17107

829eda7d38

Co-authored-by: Phạm Bình An <phambinhanctb2004@gmail.com>

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
(cherry picked from commit 374e52a7ee)

* docs: provide example_init.lua #33524

Problem:
There are some "boilerplate" steps for new users. Although we are
constantly improving defaults and lifting patterns into core, users
eventually want to know how to start their own config, add plugins, etc.

Solution:
Add `runtime/example_init.lua` and refer to it from docs.

(cherry picked from commit 86b34ad073)

---------

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2025-04-27 10:33:37 -07:00
Phạm Bình An
d68d212ad4 docs: provide example_init.lua #33524
Problem:
There are some "boilerplate" steps for new users. Although we are
constantly improving defaults and lifting patterns into core, users
eventually want to know how to start their own config, add plugins, etc.

Solution:
Add `runtime/example_init.lua` and refer to it from docs.

(cherry picked from commit 86b34ad073)
2025-04-28 00:01:50 +07:00
brianhuster
3273c595c0 vim-patch:829eda7: runtime(new-tutor): update tutor and correct comandline completion
Problem: Some parts of the tutor are outdated.

- For example, pressing `<Tab>` after typing `:e` does not complete the
command `:edit`, but shows a completion menu with the first entry being
`:earlier`.

closes: vim/vim#17107

829eda7d38

Co-authored-by: Phạm Bình An <phambinhanctb2004@gmail.com>

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
(cherry picked from commit 374e52a7ee)
2025-04-27 23:44:37 +07:00
Phạm Bình An
3db39ed21f fix(runtime): cpoptions is reset in Lua file #33671
closes #33670

(cherry picked from commit 923efaea28)
2025-04-27 12:19:58 +00:00
Bartłomiej Maryńczak
f184c562c5 fix(lsp): detect if Client:request resolved synchronously #33624
Problem:
In cases when the (in-process) LSP server responds to the request
immediately and calls `notify_reply_callback` the request will still be
marked as pending, because the code assumes that the response will occur
asynchronously. Then the request will be pending forever, because it was
already set as "completed" before we even set it as "pending".

A workaround is to wrap `notify_replay_callback` in `vim.shedule` ([like
so](https://github.com/neovim/neovim/pull/24338#issuecomment-2809568617)]
but that seems counterintuitive.

Solution:
Handle this case in Client:request().

(cherry picked from commit 8315697449)
2025-04-27 00:54:11 +00:00
Andre Toerien
32842b0ee3 fix(health): checkhealth float opens extra empty buffer #33648
(cherry picked from commit d927a87ed6)
2025-04-26 16:37:46 +00:00
Justin M. Keyes
4f0e828190 version bump 2025-04-26 16:31:02 +02:00
Justin M. Keyes
a9a3981669 NVIM v0.11.1
For notable changes, see runtime/doc/news.txt (or `:help news` in Nvim).

Following is a list of fixes/features commits.

BREAKING
--------------------------------------------------------------------------------
- bbf912d72f provider: drop Python 3.7, 3.8 support #33088

FEATURES
--------------------------------------------------------------------------------
- 66953b16a2 lsp: workspace_required (#33608)
- 7efb3ba6f7 checkhealth: use emoji for OK/WARN/ERROR (#33195)
- 91d11c8bc1 clipboard: g:clipboard="foo" forces the "foo" clipboard tool #33235
- 12da443930 float: 'winborder' "bold" style #33189
- 5829b5de0a vim.hl: allow multiple timed highlights simultaneously #33283

FIXES
--------------------------------------------------------------------------------
- 91481ae7d5 bug in stylize_markdown
- 5fc6bd6454 api: nvim_set_keymap() throws error even in pcall() #33228
- 2b2a90051e api: use E226 instead of E227 for duplicate abbreviation (#33159)
- 056dbf3ea7 api: use original LHS in keymap error message #33135
- fb71d631a5 api: wrong return value with reverse range + overlap #32956
- fcee5df0fc checkhealth: check g:loaded_xx_provider for all providers #33168
- 89e0ea1788 checkhealth: check outdated pynvim version properly #33175
- adfd4b9f4f checkhealth: don't override user "q" keymap #33132
- 06df3e0c0d cmdline: avoid empty @: register after :<CR> (#33126)
- 53def2a877 cmdline: empty ext_cmdline block events for :<CR> #33118
- b8e5fd51fd column: don't count signs on lines beyond eob #33410
- e4007551c4 completion: avoid freeing uninitialized value (#33459)
- 6e51d39696 decor: enable decoration provider in on_start #33337
- 837c9d0409 defaults: enable :terminal [[,]] motion in operator-pending mode #33217
- 2182272139 defaults: keywordprg=:help on Windows #33336
- 7e884b78bf defaults: visual-mode [[,]] for :terminal shell prompts #33203
- 2a04161a35 desktop: cannot open filename with spaces using OS file manager #33161
- 2cd735d159 display: scroll logic does not take into account concealed topline (#33054)
- c4a19bff4e display: scroll redrawing doesn't account for virt_lines above fold #33374
- 02123bac0d display: wrong cursor column with 'concealcursor' = "n" and virt_text (#33218)
- 837faf7065 editor: respect [+cmd] when executing :drop #33339
- 155529b91a events: avoid superfluous CursorMovedI on first autocmd (#33588)
- c158d41cec health: expecting nonexistent "inotifywait" function #33312
- 06c2886569 health: message should mention "vim.provider" #33095
- 279a0e78c9 highlight: no match highlight during :substitute prompt #33262
- b5158e8e92 lsp: "bold" border for vim.lsp.buf.hover #33395
- 1e8e74dbff lsp: better handling of "*" configs
- 478f5d0070 lsp: cycling signatures with the default `config.focusable`
- 2435d72283 lsp: opts.reuse_win does not jump if buf is already open #33476
- 95f96a3d1b lsp: prioritize showing active signature
- a9afa6b152 lsp: warn on missing config in :checkhealth #33087
- 2b14447803 man.lua: noisy "ENOENT" error on Windows #33435
- 8aa49a8e9b man.lua: useless executability check #33438
- 4d87229789 marks: clamp conceal_lines corrected line number #33464
- 9da90af0f7 marks: wrong display after inserting/deleting lines #33389
- 3b0c88a537 messages: single msg_show event for multiple :set options #33555
- 3df9db58dc messages: verbose message emitted without kind #33305
- c61e8c6e70 mouse: crash with click on win-separator in statusline (#33091)
- dcd5e4574a mouse: do not fetch clipboard twice when pasting with middle button #33494
- 526444c4ff mouse: mouseclick after conceal_lines is miscalculated #33451
- 326eacef93 move: adjust for concealed lines above topline after scrolling up (#33211)
- 649bce6e73 provider: misleading :checkhealth if user sets g:loaded_python3_provider=1 #32696
- c57a36cd59 pum: fix heap-buffer-overflow with 'rightleft' (#33146)
- c1d3777db2 snippet: use <cmd>call cursor() for visual range
- 70f3e15298 treesitter: don't memoize modified headings (#33186)
- e342b9a25a treesitter: fix `:InspectTree` incorrect injections
- 4c1121bd33 treesitter: not refreshing virtualtext contents #33361
- 646a8f663e tui: ensure all pending escape sequences are processed before exiting #32151
- 9909580df2 ui: exclude unfocusable windows from 'complete' "w" completion
- ccb078bbb0 ui: wincmd _ should not increase 'cmdheight' above 0 (#33056)
- 29011e4d45 vim.hl: nvim_buf_del_extmark on invalid buffer #33331
- 9056c01a95 vim.lsp.inlay_hint: requesting inlay_hints even when disabled #32999
- 4422b9bbd0 vim.system: unclear non-executable message #33455
- a4b6705e87 window: crash on negative window height with 'winbar' #33250

PERFORMANCE
--------------------------------------------------------------------------------
- b9c9b15ad7 snippet: use "[count]|" to move cursor #33571

VIM PATCHES
--------------------------------------------------------------------------------
- a97713485d 9.0.1653: Amiga: default 'viewdir' may not work
- cdd3f2a2e6 9.0.1654: MS-Windows: test for default 'viewdir' fails
- b7de104d86 9.1.0721: tests: test_mksession does not consider XDG_CONFIG_HOME
- 6514e2c7ba 9.1.1247: fragile setup to get (preferred) keys from key_name_entry (#33102)
- 2084cda6f9 9.1.1260: Hang when filtering buffer with NUL bytes (#33192)
- 02bf23b4bf 9.1.1269: completion: compl_shown_match is updated when starting keyword completion
- e3506ede27 9.1.1272: completion: in keyword completion Ctrl_P cannot go back after Ctrl_N
- a92155b86b 9.1.1286: filetype: help files not detected when 'iskeyword' includes ":" (#33377)
- b694131c3b 9.1.1303: missing out-of-memory check in linematch.c (#33487)
- 0c315feddf 9.1.1305: completion menu active after switching windows/tabs (#33488)
- 12298be0fe 9.1.1310: completion: redundant check for preinsert effect (#33505)
- 4205fdee1d 9.1.1314: max allowed string width too small
- 248528c907 9.1.1317: noisy error when restoring folds from session fails
- 1a6ddeee41 9.1.1318: tests: test_format fails
- 448c2cec9d 9.1.1337: Undo corrupted with 'completeopt' "preinsert" when switching buffer (#33600)
2025-04-26 16:29:18 +02:00
Yi Ming
478f5d0070 fix(lsp): cycling signatures with the default config.focusable
(cherry picked from commit 342974773c)
2025-04-26 13:55:12 +00:00
Yi Ming
95f96a3d1b fix(lsp): prioritize showing active signature
(cherry picked from commit 9e93bfdb5f)
2025-04-26 13:55:12 +00:00
Luuk van Baal
9909580df2 fix(ui): exclude unfocusable windows from 'complete' "w" completion
Problem:  As in f85bc41, assume unfocusable windows to be UI windows
          whose buffer content is unexpectedly included in 'complete'
          "w" completion.
Solution: Exclude unfocusable windows when looping over windows.
(cherry picked from commit d01b2611a6)
2025-04-25 23:19:28 +00:00
Justin M. Keyes
66953b16a2 feat(lsp): workspace_required (#33608)
Problem:
Some language servers do not work properly without a workspace folder.

Solution:
Add `workspace_required`, which skips starting the lsp client if no
workspace folder is found.

Co-authored-by: Michael Strobel <71396679+Kibadda@users.noreply.github.com>
2025-04-24 07:06:30 -07:00
zeertzjq
448c2cec9d vim-patch:9.1.1337: Undo corrupted with 'completeopt' "preinsert" when switching buffer (#33600)
Problem:  Undo corrupted with 'completeopt' "preinsert" when switching
          buffer or window.
Solution: Do not delete preinsert text when switching buffer or window.
          (zeertzjq)

related: neovim/neovim#33581
closes: vim/vim#17193

1343681aba
(cherry picked from commit 63689deb45)
2025-04-24 00:45:04 +00:00
Luuk van Baal
c1d3777db2 fix(snippet): use <cmd>call cursor() for visual range
Problem:  Change applied in d3e495ce uses a byte-offset where a virtual
          column is expected.
Solution: Set the cursor directly through a <Cmd> mapping, while making
          sure the commands are ordered correctly by adding them to the
          type-ahead buffer.
(cherry picked from commit 019b2050e1)
2025-04-23 09:24:54 +00:00
zeertzjq
155529b91a fix(events): avoid superfluous CursorMovedI on first autocmd (#33588)
(cherry picked from commit 1dbede5b93)
2025-04-23 03:47:35 +00:00
zeertzjq
c633250cc0 Merge pull request #33589 from zeertzjq/backport
vim-patch:9.1.1305: completion menu active after switching windows/tabs (#33488)
2025-04-23 11:42:36 +08:00
zeertzjq
0c315feddf vim-patch:9.1.1305: completion menu active after switching windows/tabs (#33488)
Problem:  When switching to another window or tab page while the
          completion menu is active, the menu stays visible, although it
          belongs to the previous window/tab page context (Evgeni
          Chasnovski).
Solution: Track the window and tab page where completion started. Detect
          changes in the main editing loop and cancel completion mode if
          the current window or tab page differs from where completion
          started.

fixes: vim/vim#17090
closes: vim/vim#17101

cf7f01252f

Co-authored-by: glepnir <glephunter@gmail.com>
2025-04-23 11:09:01 +08:00
Gregory Anders
646a8f663e fix(tui): ensure all pending escape sequences are processed before exiting #32151
Problem:
Neovim disables a number of terminal modes when it exits, some of which
cause the terminal to send asynchronous events to Neovim. It's possible
that Neovim exits before the terminal has received and processed all of
the sequences to disable these modes, causing the terminal to emit one
of these asynchronous sequences after Neovim has already exited. If this
happens, then the sequence is received by the user's shell (or some
other program that is not Neovim).

Solution:
When Neovim exits, it now emits a Device Attributes request (DA1)
after disabling all of the different modes. When the terminal responds
to this request we know that it has already received all of our other
sequences disabling the other modes. At that point, it should not be
emitting any further asynchronous sequences. This means the process of
exiting Neovim is now asynchronous as well since it depends on receiving
the DA1 response from the terminal.

(cherry picked from commit 82f08f33c1)
2025-04-22 12:51:47 +00:00
luukvbaal
b9c9b15ad7 perf(snippet): use "[count]|" to move cursor #33571
Problem:
Flicker when using vim.snippet.jump().

Solution:
Pass count instead of multiple <right> keys.

(cherry picked from commit d3e495ce03)
2025-04-22 12:21:35 +00:00
luukvbaal
fb71d631a5 fix(api): wrong return value with reverse range + overlap #32956
Problem:  When iterating in reverse with {start} > {end} in
          `nvim_buf_get_extmarks()`, marks that overlap {start} and are
          greater than {end} are included in the return value twice.
          Marks that overlap {end} and do not overlap {start} are not
          not included in the return value at all. Marks are not
          actually returned in a meaningful "traversal order".

Solution: Rather than actually iterating in reverse, (also possible but
          requires convoluted conditions and would require fetching
          overlapping marks for both the {start} and {end} position,
          while still ending up with non-traversal ordered marks),
          iterate normally and reverse the return value.
(cherry picked from commit 65170e8dad)
2025-04-21 23:49:27 +00:00
luukvbaal
3b0c88a537 fix(messages): single msg_show event for multiple :set options #33555
Problem:  :set opt1 opt2... emits a separate event for each option.

Solution: Only set the kind for the first printed option value.
(cherry picked from commit 986b92eb07)
2025-04-21 16:23:38 +00:00
zeertzjq
1a6ddeee41 vim-patch:9.1.1318: tests: test_format fails
Problem:  tests: test_format fails (after 9.1.1314).
Solution: Increase the string size.  Add missing test_format.res in
          NEW_TESTS_RES (zeertzjq).

closes: vim/vim#17144

e9a27ef373
(cherry picked from commit 0251a25541)
2025-04-19 00:12:55 +00:00
zeertzjq
4205fdee1d vim-patch:9.1.1314: max allowed string width too small
Problem:  max allowed string width too small
Solution: increased MAX_ALLOWED_STRING_WIDTH from 6400 to 1MiB
          (Hirohito Higashi)

closes: vim/vim#17138

06fdfa11c5

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: John Marriott <basilisk@internode.on.net>
(cherry picked from commit ccdb37b075)
2025-04-19 00:12:55 +00:00
zeertzjq
248528c907 vim-patch:9.1.1317: noisy error when restoring folds from session fails
Problem:  noisy error when restoring folds from session fails
Solution: ignore errors silently when sourcing session file.
          (Igor Lacerda)

fixes: vim/vim#15813
closes: vim/vim#17127

cca5dea76e

Co-authored-by: Igor Lacerda <igorlfs@ufmg.br>
(cherry picked from commit e2e6c159d3)
2025-04-18 01:04:01 +00:00
zeertzjq
b7de104d86 vim-patch:9.1.0721: tests: test_mksession does not consider XDG_CONFIG_HOME
Problem:  tests: test_mksession does not consider XDG_CONFIG_HOME
Solution: allow to match $HOME/.vim/ and $HOME/.config/vim for &viewdir
          (John M Devin)

closes: vim/vim#15639

5b9237c2e7

Co-authored-by: John M Devin <john.m.devin@gmail.com>
(cherry picked from commit 0eb708aa8a)
2025-04-18 01:04:01 +00:00
zeertzjq
cdd3f2a2e6 vim-patch:9.0.1654: MS-Windows: test for default 'viewdir' fails
Problem:    MS-Windows: test for default 'viewdir' fails.
Solution:   Escape the pattern.

813b7a85f2

Co-authored-by: Bram Moolenaar <Bram@vim.org>
(cherry picked from commit bd0555ecd4)
2025-04-18 01:04:01 +00:00
zeertzjq
a97713485d vim-patch:9.0.1653: Amiga: default 'viewdir' may not work
Problem:    Amiga: default 'viewdir' may not work.
Solution:   Use "home:" instead of "$VIM". Add a test. (Christian Brabandt,
            closes vim/vim#12576)

b8b1c8ebd4

Cherry-pick Test_mkview_manual_fold() changes from 9.0.{0363,0626}.

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 827cfe4a76)
2025-04-18 01:04:01 +00:00
zeertzjq
12298be0fe vim-patch:9.1.1310: completion: redundant check for preinsert effect (#33505)
Problem:  Duplicate check for preinsert effect, particularly for Ctrl_w
          and Ctrl_U.
Solution: Remove the specific check for Ctrl_w and Ctrl_U to eliminate
          redundancy (glepnir).

closes: vim/vim#17129

1c2b258250

Co-authored-by: glepnir <glephunter@gmail.com>
(cherry picked from commit f9f6dc4262)
2025-04-17 23:25:23 +00:00
Au.
2435d72283 fix(lsp): opts.reuse_win does not jump if buf is already open #33476
Problem:
`vim.lsp.buf.[implementation|definition|...]({ reuse_win = true })` does not
jump cursor to existing window if buffer is already open.

Steps to reproduce:
1. `nvim repro.lua`
2. Insert anything that lsp can read to open the library definition/implementation, e.g., `vim.keymap.set`
3. open `repro.lua` buffer and the library buffer side by side.
4. type `gd` over `set` to jump to the library definition.

The open buffer is scrolled to the target line, but cursor does not jump.

Solution:
Call nvim_set_current_win if necessary.

(cherry picked from commit 6926fc1615)
2025-04-17 15:12:46 +00:00
yuukibarns
e342b9a25a fix(treesitter): fix :InspectTree incorrect injections
(cherry picked from commit 284b0e4fa2)
2025-04-16 14:31:32 +00:00
Justin M. Keyes
4422b9bbd0 fix(vim.system): unclear non-executable message #33455
Problem:
When a command is not found or not executable, the error message gives
no indication about what command was actually tried.

Solution:
Always append the command name to the error message.

BEFORE:

    E5108: Error executing lua …/_system.lua:248: ENOENT: no such file or directory

AFTER:

    E5108: Error executing lua …/_system.lua:249: ENOENT: no such file or directory: "foo"

fix #33445

(cherry picked from commit 223ac7782e)
2025-04-16 12:38:46 +00:00
Dmitry Torokhov
dcd5e4574a fix(mouse): do not fetch clipboard twice when pasting with middle button #33494
Problem:
When doing paste operation mouse code tries to figure out it it is
dealing with a multi-line register by calling yank_register_mline(),
which fetches register data and checks its type. Later the code calls
either do_put() or insert_reg() which fetch register data again. This is
unnoticeable when working with internal neovim registers, but starts
hurting when dealing with clipboards, especially remote one (forwarded X
or socket tunnel or similar).

Solution:
Change yank_register_mline() to also return pointer to the
register structure prepared for pasting, and insert_reg() to accept
such register pointer and use it if it is supplied. do_put() already
has support for accepting a register structure to be used for pasting.

Fixes #33493

(cherry picked from commit 7432781e71)
2025-04-16 10:50:01 +00:00
zeertzjq
b694131c3b vim-patch:9.1.1303: missing out-of-memory check in linematch.c (#33487)
Problem:  missing out-of-memory check in linematch.c
Solution: return early in case of memory allocation failure, move the
          pow() calculation ouside of the for() loop
          (John Marriott)

closes: vim/vim#17118

2137710b43

Co-authored-by: John Marriott <basilisk@internode.on.net>
(cherry picked from commit d2d1b5e944)
2025-04-15 23:37:37 +00:00
luukvbaal
4d87229789 fix(marks): clamp conceal_lines corrected line number #33464
Problem:  Line number corrected for conceal_lines may be set beyond eob
          when the last buffer line is concealed, causing ml_get errors.

Solution: Avoid setting line number beyond eob.
(cherry picked from commit 3341ab0776)
2025-04-14 11:38:29 +00:00
zeertzjq
e4007551c4 fix(completion): avoid freeing uninitialized value (#33459)
(cherry picked from commit 51caf0a3af)
2025-04-14 05:31:00 +00:00
luukvbaal
526444c4ff fix(mouse): mouseclick after conceal_lines is miscalculated #33451
Problem:  Computed buffer line for mouse position does not take into
          account concealed lines on the reached row.

Solution: Adjust for concealed lines at the end of the loop computing
          the buffer position.
(cherry picked from commit 2f8fb4f28a)
2025-04-13 21:57:00 +00:00
Emanuel Krollmann
8aa49a8e9b fix(man.lua): useless executability check #33438
Problem:
executability check using `uv.fs_access`
doesn't work currently and can't work on windows

Solution:
only check for executable with `vim.fn.executable`

(cherry picked from commit b8763cb215)
2025-04-12 23:53:54 +00:00
neovim-backports[bot]
2b14447803 fix(man.lua): noisy "ENOENT" error on Windows #33435
Problem:
:Man shows noisy "ENOENT: no such file or directory" error on Windows.

Solution:
Do some checks before calling `vim.system`.

(cherry picked from commit a8dd5c7e41)

Co-authored-by: Emanuel Krollmann <115734183+Sodastream11@users.noreply.github.com>
2025-04-12 15:53:32 -07:00
Justin M. Keyes
7384983721 docs: clipboard, eval #33328 2025-04-12 10:56:28 -07:00
luukvbaal
b8e5fd51fd fix(column): don't count signs on lines beyond eob #33410
Problem:  Computed previous buffer line count may be beyond end of
          buffer. This results in signs being removed from `b_signcols`
          that were never included in it, tripping an assertion.

Solution: Store the previous line count as it was before appending or
          deleting lines. Use it to clamp the edited region when
          clearing signs before a splice, after which it is reset.
(cherry picked from commit 4a706a7092)
2025-04-11 12:28:22 +00:00
luukvbaal
9da90af0f7 fix(marks): wrong display after inserting/deleting lines #33389
Problem:  Lines to/from which virt_lines or inline virt_text may have
          moved are left valid. Similarly the modified region may be
          too small to account for moved decorations after inserting
          or deleting lines. `redrawOneLine()` can be replaced with
          a call to `changed_lines_redraw_buf()`.

Solution: Invalidate the line after a change if there is virt_lines, or
          inline virt_text in the buffer with 'wrap' enabled. Extend the
          modified region for inserted or deleted lines if there may be
          decorations in the buffer. Remove `redrawOneLine()`.
          Simplify the logic for `changed_lines_invalidate_win()`.

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
(cherry picked from commit 064ff74cdb)
2025-04-11 12:03:53 +00:00
Siddhant Agarwal
b5158e8e92 fix(lsp): "bold" border for vim.lsp.buf.hover #33395
Problem: vim.lsp.buf.hover allows a bold border size which hasn't been
defined

Solution: Define the bold border size for vim.lsp.buf.hover
(cherry picked from commit f068386c9f)
2025-04-09 11:57:22 +00:00
jyn
837faf7065 fix(editor): respect [+cmd] when executing :drop #33339
Problem:
Normally, `:drop +41 foo.txt` will open foo.txt with the cursor on line
41. But if foo.txt is already open, it instead is a no-op, even if the
cursor is on a different line.

Steps to reproduce:

    nvim --clean foo.txt
    :drop +30 foo.txt

Solution:
Handle +cmd in ex_drop().

(cherry picked from commit 3647b821ea)
2025-04-08 13:47:08 +00:00
luukvbaal
c4a19bff4e fix(display): scroll redrawing doesn't account for virt_lines above fold #33374
Problem:  Logic computing the new height of the modified area does not
          take into account virtual lines attached to a folded line.

Solution: Remove `hasFolding()` branch and let `plines_win_full()` do its job.
(cherry picked from commit 5b1561bb71)
2025-04-08 13:20:27 +00:00
zeertzjq
a92155b86b vim-patch:9.1.1286: filetype: help files not detected when 'iskeyword' includes ":" (#33377)
Problem:  Help files not detected when 'iskeyword' includes ":".
Solution: Do not use \< and \> in the pattern (zeertzjq).

fixes: vim/vim#17069
closes: vim/vim#17071

e370141bf4
(cherry picked from commit 8af9f8ab5e)
2025-04-08 00:52:17 +00:00
luukvbaal
6e51d39696 fix(decor): enable decoration provider in on_start #33337
Problem:  An on_win-disabled decoration provider is left disabled for
          the on_buf callback during the next redraw (if the provider
          does not subscribe to on_end).

Solution: Move re-activation of the provider from after the on_end
          callback to before the on_start callback.
(cherry picked from commit ca16b54c86)
2025-04-07 11:33:43 +00:00
Dmitry Zolotukhin
4c1121bd33 fix(treesitter): not refreshing virtualtext contents #33361
Problem: In some cases, when treesitter is enabled, deleting a
line below virtualtext will not refresh all updated lines.
https://github.com/neovim/neovim/issues/33358

Solution: Revert a part of https://github.com/neovim/neovim/pull/31324
to ensure that the full range (with virtual lines) is refreshed.

(cherry picked from commit cf59631f65)
2025-04-07 11:30:12 +00:00
Felipe Lema
c158d41cec fix(health): expecting nonexistent "inotifywait" function #33312
Problem:
55e4301036 changed the program name but not the function name.

Solution:
Fix the healthcheck.

(cherry picked from commit bd37348939)
2025-04-07 09:40:09 +00:00
Emanuel Krollmann
2182272139 fix(defaults): keywordprg=:help on Windows #33336
Problem:
As `:h kp` says, the default value for keywordprg
should be ':help' on Windows. It is currently
always ':Man'.

Solution:
Add condition to options.lua which sets keywordprg
to ':help' if running on windows.

(cherry picked from commit 3ebde5ea14)
2025-04-07 09:35:36 +00:00
phanium
29011e4d45 fix(vim.hl): nvim_buf_del_extmark on invalid buffer #33331
Problem:
nvim_buf_del_extmark error if buffer is destroyed before timer stops

Solution:
check nvim_buf_is_valid.

(cherry picked from commit 28e8190185)
2025-04-05 23:19:19 +00:00
Lewis Russell
91481ae7d5 fix: bug in stylize_markdown
`stripped` and `markdown_lines` are iterated together so must have the same length.

(cherry picked from commit 379c37fa0b)
2025-04-04 14:08:52 +00:00
luukvbaal
3df9db58dc fix(messages): verbose message emitted without kind #33305
Problem:  Successive autocmd verbose messages may be emitted without a kind.

Solution: Always set the kind when preparing to emit a verbose message.
(cherry picked from commit 98f5aa2564)
2025-04-04 12:53:23 +00:00
glepnir
e3506ede27 vim-patch:9.1.1272: completion: in keyword completion Ctrl_P cannot go back after Ctrl_N
Problem:  completion: in keyword completion Ctrl_P cannot go back after
          Ctrl_N
Solution: in find_compl_when_fuzzy() always return first match of array, after Ctrl_P
          use compl_shown_match->cp_next instead of compl_first_match.
          (glepnir)

closes: vim/vim#17043

3e50a28a03

Co-authored-by: glepnir <glephunter@gmail.com>
(cherry picked from commit b01921cb55)
2025-04-04 06:40:34 +00:00
glepnir
02bf23b4bf vim-patch:9.1.1269: completion: compl_shown_match is updated when starting keyword completion
Problem:  compl_shown_match is updated when starting keyword completion
          and does not include fuzzy matching.
Solution: Do not update compl_shown_match when starting keyword
          completion, since it is the one already selected by the
          keyword completion direction. (glepnir)

closes: vim/vim#17033

e4e4d1c381

Co-authored-by: glepnir <glephunter@gmail.com>
(cherry picked from commit 8cf413e450)
2025-04-04 06:40:34 +00:00
tstsrt
cbf4906c11 test(plugin/shada_spec): failure if timezone isn't a whole hour ahead of UTC (#33257)
Problem: When running functional tests locally, test `syntax/shada.vim works`
fails if the local timezone is not a whole number of hours ahead of UTC.

Solution: Use '!%M' for minute format so that UTC is used in the expected
timestamp instead of the local timezone, just like '%H' for hours.

(cherry picked from commit d9405c7935)
2025-04-04 04:24:11 +00:00
zeertzjq
4cc97cf009 Merge pull request #33295 from siddhantdev/backport-33283-to-release-0.11
feat(vim.hl): allow multiple timed highlights simultaneously #33283
2025-04-04 09:50:29 +08:00
zeertzjq
c6ef13dc45 test(lua/hl_spec): fix hang on exit with ASAN (#33298) 2025-04-04 09:25:39 +08:00
zeertzjq
1a2d0484ac docs: news.txt 2025-04-04 09:25:14 +08:00
Siddhant Agarwal
5829b5de0a feat(vim.hl): allow multiple timed highlights simultaneously #33283
Problem: Currently vim.hl.range only allows one timed highlight.
Creating another one, removes the old one.

Solution: vim.hl.range now returns a timer and a function. The timer
keeps track of how much time is left in the highlight and the function
allows you to clear it, letting the user decide what to do with old
highlights.

(cherry picked from commit eae2d3b145)
2025-04-04 00:14:43 +05:30
Evgeni Chasnovski
dd547ef1ea docs(diagnostic): mention severity in Opts.VirtualLines (#33293)
Problem: `severity` field is recognized by
  `vim.diagnostic.Opts.VirtualLines`, but it is not explicitly
  documented.

Solution: document it.
(cherry picked from commit 71e133e5e6)
2025-04-03 16:49:58 +00:00
Deveshi Dwivedi
91d11c8bc1 feat(clipboard): g:clipboard="foo" forces the "foo" clipboard tool #33235
(cherry picked from commit 9722bd7b1b)
2025-04-03 14:26:45 +00:00
zeertzjq
1daea6e1fd test(lua/secure_spec): fix failure with long path (#33280)
Ref #33278

(cherry picked from commit 974a3aa2c4)
2025-04-03 13:56:49 +00:00
luukvbaal
279a0e78c9 fix(highlight): no match highlight during :substitute prompt #33262
Problem:  Redrawing during a substitute confirm prompt causes the match
          highlight to disappear.
Solution: Unset `highlight_match` after the prompt has returned.
          Use global highlight definitions in searchhl_spec.lua.
(cherry picked from commit 3af43cffa0)
2025-04-02 12:48:35 +00:00
tstsrt
5fc6bd6454 fix(api): nvim_set_keymap() throws error even in pcall() #33228
Problem: When `nvim_set_keymap` tries to overwrite a `<unique>` mapping,
it throws an error even when called in `pcall`.

Solution: src/nvim/mapping.c:buf_do_map no longer calls `semsg`. Its
callers now decide whether to ignore the error, or use
`semsg` (not caught)/`api_set_error` (caught by `pcall`).

(cherry picked from commit ec18ebcb41)
2025-04-01 15:50:54 +00:00
zeertzjq
2b2a90051e fix(api): use E226 instead of E227 for duplicate abbreviation (#33159)
(cherry picked from commit 99529577cc)
2025-04-01 15:04:03 +00:00
Lewis Russell
1e8e74dbff fix(lsp): better handling of "*" configs
Problem:

If a config name contains "*" it causes rtp discovery of `lsp/` to
consider the `*` as a wildcard and could lead to strange and unintended
behaviour. For example, accessing the `'*'` config from a `lsp/` file
would cause an infinite loop.

Solution:

- Explicitly disallow a config name from containing wildcards, with the
  exception of `'*'`.
- When Resolving `'*'` config, skip the rtp step.

(cherry picked from commit 2ee896201c)
2025-04-01 13:54:47 +01:00
luukvbaal
a4b6705e87 fix(window): crash on negative window height with 'winbar' #33250
Problem:  Negative window and grid height with 'winbar'.
Solution: Clamp the height when subtracting the 'winbar' height.
(cherry picked from commit 0e7479bb76)
2025-04-01 12:28:28 +00:00
Sean Dewar
f68e0fed26 docs(eval): fix dict param type of mapset
Match maparg's return type.

(cherry picked from commit ec6670080a)
2025-04-01 10:21:42 +00:00
Sean Dewar
09fd22d5d7 docs(eval): fix lnum type for functions using tv_get_lnum
These occurrences also accept string, which is used like in getline.

Also make the lnum field of vim.fn.sign_placelist.list.item optional, as it can
be omitted like vim.fn.sign_place.dict's.

(cherry picked from commit 4a36f234ac)
2025-04-01 10:21:42 +00:00
luukvbaal
326eacef93 fix(move): adjust for concealed lines above topline after scrolling up (#33211)
Problem:  Scrolling up does not adjust `w_topline` for concealed lines
          directly above it, resulting in (non-visual) asymmetry when
          scrolling up/down.
Solution: Adjust `w_topline` for concealed lines after scrolling up.
(cherry picked from commit 32325a66ca)
2025-04-01 06:34:00 +00:00
luukvbaal
02123bac0d fix(display): wrong cursor column with 'concealcursor' = "n" and virt_text (#33218)
Problem:  Inline virtual text placed in a decor provider callback
          invalidates `w_virtcol`, which must be valid for `win_line()`.
Solution: Call `validate_virtcol()` after "line" decor provider callbacks.
(cherry picked from commit 7e8b7bba21)
2025-04-01 06:31:29 +00:00
zeertzjq
8d9b4d8c14 test(float): restore border tests (#33222)
(cherry picked from commit 04901f4ee7)
2025-04-01 00:01:17 +00:00
zeertzjq
55be20316a docs: remove duplicate news file (#33227) 2025-03-31 15:16:51 +00:00
glepnir
12da443930 feat(float): 'winborder' "bold" style #33189
(cherry picked from commit 216cc893bf)
2025-03-31 14:06:11 +00:00
Ghjuvan Lacambre
837c9d0409 fix(defaults): enable :terminal [[,]] motion in operator-pending mode #33217
This enables y]] to copy a command and its output.

(cherry picked from commit 57b4fb5c53)
2025-03-31 13:42:29 +00:00
Tan, Long
70f3e15298 fix(treesitter): don't memoize modified headings (#33186)
Problem: repeated gO in markdown etc. adds extra toc indentation

Solution: don't memoize heading table which gets modified
(cherry picked from commit 28eaec5e15)
2025-03-31 09:49:39 +00:00
neovim-backports[bot]
7e884b78bf fix(defaults): visual-mode [[,]] for :terminal shell prompts #33203
Problem:
:terminal shell prompt jump mappings ]]/[[ don't work in visual mode.

Solution:
Also define them for in visual mode.

(cherry picked from commit cb247e06f0)

Co-authored-by: msaher <77233589+msaher@users.noreply.github.com>
2025-03-30 13:17:46 -07:00
zeertzjq
89e0ea1788 fix(checkhealth): check outdated pynvim version properly #33175
Fixes #33174, a regression from #22962.

(cherry picked from commit e87d2ae383)
2025-03-30 17:01:35 +00:00
Bartłomiej Maryńczak
9056c01a95 fix(vim.lsp.inlay_hint): requesting inlay_hints even when disabled #32999
Problem:
Nvim needlessly requests inlay_hints even if they are disabled for a given buffer.

Solution:
Add the missing `enabled` check in `on_refresh`.
Rest of the code has this check already so that's the only needed one to fix this.

(cherry picked from commit 49756ebc70)
2025-03-30 16:07:09 +00:00
Phạm Bình An
d2dd403693 docs: faq, lua packages #33183
Problem:
- `health#check()` seems to have been removed for a while, but `:h faq`
  still refers to it.
- `news-0.11.txt` doesn't mention #33044
2025-03-30 17:22:52 +02:00
zeertzjq
2084cda6f9 vim-patch:9.1.1260: Hang when filtering buffer with NUL bytes (#33192)
Problem:  Hang when filtering buffer with NUL bytes (after 9.1.1050).
Solution: Don't subtract "written" from "lplen" repeatedly (zeertzjq).

related: neovim/neovim#33173
closes: vim/vim#17011

53fed23cb7
(cherry picked from commit 431c037709)
2025-03-30 14:34:33 +00:00
neovim-backports[bot]
7efb3ba6f7 feat(checkhealth): use emoji for OK/WARN/ERROR (#33195)
Problem:
Health status can be much more visually distinct.

Solution:
Use emoji next to each status.

(cherry picked from commit 75fe540500)

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
2025-03-30 07:04:01 -07:00
neovim-backports[bot]
649bce6e73 fix(provider): misleading :checkhealth if user sets g:loaded_python3_provider=1 #32696
Problem:
:checkhealth shows a confusing message if user sets
g:loaded_python3_provider=1.

Solution:
- Show a warning if that var is set to 1.
- Update provider modules to default to 0. Any user code that is
  checking for 1, is like already broken because these may be set to 2.

(cherry picked from commit b4906577c9)

Co-authored-by: Sathya Pramodh <94102031+sathya-pramodh@users.noreply.github.com>
2025-03-30 08:09:07 +08:00
Justin M. Keyes
fcee5df0fc fix(checkhealth): check g:loaded_xx_provider for all providers #33168
(cherry picked from commit 5f9f5bc04d)
2025-03-29 21:09:55 +00:00
Daniel Kusai
2a04161a35 fix(desktop): cannot open filename with spaces using OS file manager #33161
Problem:
When activated from OS "filetype handling", Nvim cannot handle filenames containing spaces.

Solution:
Quote the filename in the .desktop config.

(cherry picked from commit 6e12ef4a7b)
2025-03-29 16:17:04 +00:00
phanium
adfd4b9f4f fix(checkhealth): don't override user "q" keymap #33132
(cherry picked from commit 78d2e0b43e)
2025-03-29 13:58:03 +00:00
Gregory Anders
056dbf3ea7 fix(api): use original LHS in keymap error message #33135
When setting a keymap with "unique" that already exists the error
message contains the LHS of the keymap with termcodes replaced. In
particular this means that keys like <Tab> show as an actual tab
character, meaning the error message displays as "Mapping already exists
for ", which is hard to debug for users.

Instead, display the original LHS (without any simplification or parsed
termcodes). This rperesents exactly what the user passed to the `lhs`
argument of `nvim_set_keymap`, which makes it easier to find where the
offending keymap is.

(cherry picked from commit 0d73ec5834)
2025-03-29 13:48:06 +00:00
zeertzjq
c57a36cd59 fix(pum): fix heap-buffer-overflow with 'rightleft' (#33146)
(cherry picked from commit 2681e1fce3)
2025-03-29 02:58:38 +00:00
luukvbaal
06df3e0c0d fix(cmdline): avoid empty @: register after :<CR> (#33126)
Fix https://github.com/neovim/neovim/issues/33125

(cherry picked from commit f4ee0ab2f1)
2025-03-28 21:57:24 +00:00
Micah Halter
a9afa6b152 fix(lsp): warn on missing config in :checkhealth #33087
Problem
When calling `:checkhealth vim.lsp` after the user has enabled a language
server with `vim.lsp.enable` that has no configuration a runtime error
is hit because the code expects for a configuration to exist.

Solution:
Check if a configuration was returned before parsing it, if it isn't
returned then warn the user that the server has been enabled but a
configuration was not found.

(cherry picked from commit 5554fcc286)
2025-03-28 13:17:38 +00:00
v1nh1shungry
bbf912d72f fix(provider)!: drop Python 3.7, 3.8 support #33088
Problem: #33022 didn't update `min_version` to 3.9, therefore Python 3.7
and 3.8 are still available.

Solution: Update `min_version` to 3.9.
(cherry picked from commit ade58885c4)
2025-03-28 12:36:02 +00:00
luukvbaal
53def2a877 fix(cmdline): empty ext_cmdline block events for :<CR> #33118
Problem:  An ext_cmdline block event that should be empty after :<CR>
          re-emits the previous cmdline.
Solution: Clear `last_cmdline` even when `new_last_cmdline == NULL`.
(cherry picked from commit 95ab723995)
2025-03-28 12:11:56 +00:00
Shadman
c61e8c6e70 fix(mouse): crash with click on win-separator in statusline (#33091)
Problem: Clicking on window separator in statusline crashes Nvim due
to out of bound memory access

Solution: Check if the click location is within clicking range before
applying it.

(cherry picked from commit 18fa61049a)
2025-03-28 07:32:24 +00:00
zeertzjq
6514e2c7ba vim-patch:9.1.1247: fragile setup to get (preferred) keys from key_name_entry (#33102)
Problem:  fragile setup to get (preferred) keys from key_name_entry
          (after v9.1.1179)
Solution: refactor the code further, fix a bug with "pref_name" key
          entry introduced in v9.1.1180 (Yee Cheng Chin)

The optimization introduced for using bsearch() with key_name_entry
in vim/vim#16788 was fragile as it required synchronizing a non-obvious index
(e.g. IDX_KEYNAME_SWU) with the array that could be accidentally changed
by any one adding a key to it. Furthermore, the "pref_name" that was
introduced in that change was unnecessary, and in fact introduced a bug,
as we don't always want to use the canonical name.

The bug is triggered when the user triggers auto-complete using a
keycode, such as `:set <Scroll<Tab>`. The bug would end up showing two
copies of `<ScrollWheelUp>` because both entries end up using the
canonical name.

In this change, remove `pref_name`, and simply use a boolean to track
whether an entry is an alt name or not and modify logic to respect that.

Add test to make sure auto-complete works with alt names

closes: vim/vim#16987

7d8e7df551

In Nvim there is no `enabled` field, so put `is_alt` before `name` to
reduce the size of the struct.

Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
(cherry picked from commit ae98d0a560)
2025-03-28 00:28:03 +00:00
Eisuke Kawashima
06c2886569 fix(health): message should mention "vim.provider" #33095
(cherry picked from commit 07f048a8d7)
2025-03-27 23:49:03 +00:00
luukvbaal
2cd735d159 fix(display): scroll logic does not take into account concealed topline (#33054)
(cherry picked from commit ce0c0c31a0)
2025-03-27 13:55:34 +00:00
luukvbaal
ccb078bbb0 fix(ui): wincmd _ should not increase 'cmdheight' above 0 (#33056)
(cherry picked from commit 703f4037c4)
2025-03-27 13:08:16 +00:00
Justin M. Keyes
5e4365b83d version bump 2025-03-26 15:13:05 +01:00
343 changed files with 11170 additions and 3962 deletions

View File

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

View File

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

View File

@@ -132,27 +132,44 @@ jobs:
windows: windows:
needs: setup needs: setup
runs-on: windows-2019 strategy:
matrix:
include:
- runner: windows-2022
arch: x86_64
archive_name: nvim-win64
- runner: windows-11-arm
arch: arm64
archive_name: nvim-win-arm64
runs-on: ${{ matrix.runner }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
# Perform a full checkout #13471 # Perform a full checkout #13471
fetch-depth: 0 fetch-depth: 0
- run: .github/scripts/env.ps1 - run: .github/scripts/env.ps1
env:
BUILD_ARCH: ${{ matrix.arch }}
- name: Install Wix
run: |
Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" -OutFile "wix314-binaries.zip"
Expand-Archive -Path "wix314-binaries.zip" -DestinationPath "C:/wix"
echo "C:\wix" >> $env:GITHUB_PATH
- name: Build deps - name: Build deps
run: | run: |
cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build .deps cmake --build .deps
- name: build package - name: Build package
run: | run: |
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build build --target package cmake --build build --target package
- uses: actions/upload-artifact@v4 - name: Upload artifact
uses: actions/upload-artifact@v4
with: with:
name: nvim-win64 name: nvim-win-${{ matrix.arch }}
path: | path: |
build/nvim-win64.msi build/${{ matrix.archive_name }}.zip
build/nvim-win64.zip build/${{ matrix.archive_name }}.msi
retention-days: 1 retention-days: 1
publish: publish:
@@ -193,25 +210,14 @@ jobs:
echo 'PRERELEASE=') >> $GITHUB_ENV echo 'PRERELEASE=') >> $GITHUB_ENV
gh release delete stable --yes || true gh release delete stable --yes || true
git push origin :stable || true git push origin :stable || true
# `sha256sum` outputs <sha> <path>, so we cd into each dir to drop the
# containing folder from the output.
- run: |
for i in nvim-*; do
(
cd $i || exit
sha256sum * >> $GITHUB_WORKSPACE/shasum.txt
)
done
- name: Publish release - name: Publish release
env: env:
NVIM_VERSION: ${{ needs.linux.outputs.version }} NVIM_VERSION: ${{ needs.linux.outputs.version }}
NVIM_COMMIT: ${{ github.sha }}
DEBUG: api DEBUG: api
run: | run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md" envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
echo '```' >> "$RUNNER_TEMP/notes.md"
cat shasum.txt >> "$RUNNER_TEMP/notes.md"
echo '```' >> "$RUNNER_TEMP/notes.md"
if [ "$TAG_NAME" != "nightly" ]; then if [ "$TAG_NAME" != "nightly" ]; then
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/* shasum.txt gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win-x86_64/* nvim-win-arm64/*
fi fi
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/* shasum.txt 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

@@ -259,6 +259,29 @@ cmake --build build
- Using `ninja` is strongly recommended. - Using `ninja` is strongly recommended.
4. If treesitter parsers are not bundled, they need to be available in a `parser/` runtime directory (e.g. `/usr/share/nvim/runtime/parser/`). 4. If treesitter parsers are not bundled, they need to be available in a `parser/` runtime directory (e.g. `/usr/share/nvim/runtime/parser/`).
### How to build static binary (on Linux)
1. Use a linux distribution which uses musl C. We will use Alpine Linux but any distro with musl should work. (glibc does not support static linking)
2. Run make passing the `STATIC_BUILD` variable: `make CMAKE_EXTRA_FLAGS="-DSTATIC_BUILD=1"`
In case you are not using Alpine Linux you can use a container to do the build the binary:
```bash
podman run \
--rm \
-it \
-v "$PWD:/workdir" \
-w /workdir \
alpine:latest \
bash -c 'apk add build-base cmake coreutils curl gettext-tiny-dev && make CMAKE_EXTRA_FLAGS="-DSTATIC_BUILD=1"'
```
The resulting binary in `build/bin/nvim` will have all the dependencies statically linked:
```
build/bin/nvim: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID[sha1]=b93fa8e678d508ac0a76a2e3da20b119105f1b2d, with debug_info, not stripped
```
#### Debian 10 (Buster) example: #### Debian 10 (Buster) example:
```sh ```sh

View File

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

View File

@@ -273,7 +273,7 @@ If you need to modify or debug the documentation flow, these are the main files:
runtime/lua/vim/* => runtime/doc/lua.txt runtime/lua/vim/* => runtime/doc/lua.txt
runtime/lua/vim/lsp/ => runtime/doc/lsp.txt runtime/lua/vim/lsp/ => runtime/doc/lsp.txt
src/nvim/api/* => runtime/doc/api.txt src/nvim/api/* => runtime/doc/api.txt
src/nvim/eval.lua => runtime/doc/builtin.txt src/nvim/eval.lua => runtime/doc/vimfn.txt
src/nvim/options.lua => runtime/doc/options.txt src/nvim/options.lua => runtime/doc/options.txt
``` ```

View File

@@ -66,11 +66,13 @@ Several Neovim GUIs are available from scoop (extras): [scoop.sh/#/apps?q=neovim
You can then copy your spell files over (for English, located You can then copy your spell files over (for English, located
[here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.spl) and [here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.spl) and
[here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.sug)); [here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.sug));
- For Python plugins you need the `pynvim` module. "Virtual envs" are recommended. After activating the virtual env do `pip install pynvim` (in *both*). Edit your `init.vim` so that it contains the path to the env's Python executable: - For Python plugins you need the `pynvim` module. Installation via uv
```vim (https://docs.astral.sh/uv/) is recommended; the `--upgrade` switch ensures
let g:python3_host_prog='C:/Users/foo/Envs/neovim3/Scripts/python.exe' installation of the latest version:
``` ```
- Run `:checkhealth` and read `:help provider-python`. uv tool install --upgrade pynvim
```
- Run `:checkhealth` and read `:help provider-python` for more details.
- **init.vim ("vimrc"):** If you already have Vim installed you can copy `%userprofile%\_vimrc` to `%userprofile%\AppData\Local\nvim\init.vim` to use your Vim config with Neovim. - **init.vim ("vimrc"):** If you already have Vim installed you can copy `%userprofile%\_vimrc` to `%userprofile%\AppData\Local\nvim\init.vim` to use your Vim config with Neovim.

View File

@@ -73,7 +73,7 @@ if(HAS_OG_FLAG)
set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS}) set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS})
endif() endif()
set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1") set(DEPS_INCLUDE_FLAGS "-I\"${DEPS_INSTALL_DIR}/include\" -I\"${DEPS_INSTALL_DIR}/include/luajit-2.1\"")
# If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET), # If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET),
# fall back to local system version. Needs to be done here and in top-level CMakeLists.txt. # fall back to local system version. Needs to be done here and in top-level CMakeLists.txt.
@@ -96,10 +96,10 @@ else()
find_package(Lua 5.1 EXACT) find_package(Lua 5.1 EXACT)
if(LUAJIT_FOUND) if(LUAJIT_FOUND)
set(LUA_ENGINE LuaJit) set(LUA_ENGINE LuaJit)
string(APPEND DEPS_INCLUDE_FLAGS " -I${LUAJIT_INCLUDE_DIR}") string(APPEND DEPS_INCLUDE_FLAGS " -I\"${LUAJIT_INCLUDE_DIR}\"")
elseif(LUA_FOUND) elseif(LUA_FOUND)
set(LUA_ENGINE Lua) set(LUA_ENGINE Lua)
string(APPEND DEPS_INCLUDE_FLAGS " -I${LUA_INCLUDE_DIR}") string(APPEND DEPS_INCLUDE_FLAGS " -I\"${LUA_INCLUDE_DIR}\"")
else() else()
message(FATAL_ERROR "Could not find system lua or luajit") message(FATAL_ERROR "Could not find system lua or luajit")
endif() endif()

View File

@@ -7,5 +7,6 @@ ExternalProject_Add(wasmtime
-D WASMTIME_FASTEST_RUNTIME=ON # build with full LTO -D WASMTIME_FASTEST_RUNTIME=ON # build with full LTO
-D WASMTIME_DISABLE_ALL_FEATURES=ON # don't need all that crap... -D WASMTIME_DISABLE_ALL_FEATURES=ON # don't need all that crap...
-D WASMTIME_FEATURE_CRANELIFT=ON # ...except this one (compiles wasm to platform code) -D WASMTIME_FEATURE_CRANELIFT=ON # ...except this one (compiles wasm to platform code)
-D WASMTIME_FEATURE_GC_DRC=ON # ...and this one (needed by ts to create engines)
USES_TERMINAL_BUILD TRUE USES_TERMINAL_BUILD TRUE
${EXTERNALPROJECT_OPTIONS}) ${EXTERNALPROJECT_OPTIONS})

View File

@@ -34,20 +34,20 @@ LIBICONV_SHA256 8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313
UTF8PROC_URL https://github.com/JuliaStrings/utf8proc/archive/v2.10.0.tar.gz UTF8PROC_URL https://github.com/JuliaStrings/utf8proc/archive/v2.10.0.tar.gz
UTF8PROC_SHA256 6f4f1b639daa6dca9f80bc5db1233e9cbaa31a67790887106160b33ef743f136 UTF8PROC_SHA256 6f4f1b639daa6dca9f80bc5db1233e9cbaa31a67790887106160b33ef743f136
TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.23.4.tar.gz TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.24.1.tar.gz
TREESITTER_C_SHA256 b66c5043e26d84e5f17a059af71b157bcf202221069ed220aa1696d7d1d28a7a TREESITTER_C_SHA256 25dd4bb3dec770769a407e0fc803f424ce02c494a56ce95fedc525316dcf9b48
TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.3.0.tar.gz TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.4.0.tar.gz
TREESITTER_LUA_SHA256 a34cc70abfd8d2d4b0fabf01403ea05f848e1a4bc37d8a4bfea7164657b35d31 TREESITTER_LUA_SHA256 b0977aced4a63bb75f26725787e047b8f5f4a092712c840ea7070765d4049559
TREESITTER_VIM_URL https://github.com/tree-sitter-grammars/tree-sitter-vim/archive/v0.5.0.tar.gz TREESITTER_VIM_URL https://github.com/tree-sitter-grammars/tree-sitter-vim/archive/v0.7.0.tar.gz
TREESITTER_VIM_SHA256 90019d12d2da0751c027124f27f5335babf069a050457adaed53693b5e9cf10a TREESITTER_VIM_SHA256 44eabc31127c4feacda19f2a05a5788272128ff561ce01093a8b7a53aadcc7b2
TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v3.0.1.tar.gz TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v4.0.0.tar.gz
TREESITTER_VIMDOC_SHA256 76b65e5bee9ff78eb21256619b1995aac4d80f252c19e1c710a4839481ded09e TREESITTER_VIMDOC_SHA256 8096794c0f090b2d74b7bff94548ac1be3285b929ec74f839bd9b3ff4f4c6a0b
TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.5.1.tar.gz TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.6.2.tar.gz
TREESITTER_QUERY_SHA256 fe8c712880a529d454347cd4c58336ac2db22243bae5055bdb5844fb3ea56192 TREESITTER_QUERY_SHA256 90682e128d048fbf2a2a17edca947db71e326fa0b3dba4136e041e096538b4eb
TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.4.1.tar.gz TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.5.0.tar.gz
TREESITTER_MARKDOWN_SHA256 e0fdb2dca1eb3063940122e1475c9c2b069062a638c95939e374c5427eddee9f TREESITTER_MARKDOWN_SHA256 14c2c948ccf0e9b606eec39b09286c59dddf28307849f71b7ce2b1d1ef06937e
TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.3.tar.gz TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.6.tar.gz
TREESITTER_SHA256 862fac52653bc7bc9d2cd0630483e6bdf3d02bcd23da956ca32663c4798a93e3 TREESITTER_SHA256 ac6ed919c6d849e8553e246d5cd3fa22661f6c7b6497299264af433f3629957c
WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v29.0.1.tar.gz WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v29.0.1.tar.gz
WASMTIME_SHA256 b94b6c6fd6aebaf05d4c69c1b12b5dc217b0d42c1a95f435b33af63dddfa5304 WASMTIME_SHA256 b94b6c6fd6aebaf05d4c69c1b12b5dc217b0d42c1a95f435b33af63dddfa5304

View File

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

View File

@@ -5,11 +5,11 @@
if exists('g:loaded_clipboard_provider') if exists('g:loaded_clipboard_provider')
finish finish
endif endif
" Default to 1. provider#clipboard#Executable() may set 2. " Default to 0. provider#clipboard#Executable() may set 2.
" To force a reload: " To force a reload:
" :unlet g:loaded_clipboard_provider " :unlet g:loaded_clipboard_provider
" :runtime autoload/provider/clipboard.vim " :runtime autoload/provider/clipboard.vim
let g:loaded_clipboard_provider = 1 let g:loaded_clipboard_provider = 0
let s:copy = {} let s:copy = {}
let s:paste = {} let s:paste = {}
@@ -67,6 +67,113 @@ function! s:set_osc52() abort
return 'OSC 52' return 'OSC 52'
endfunction endfunction
function! s:set_pbcopy() abort
let s:copy['+'] = ['pbcopy']
let s:paste['+'] = ['pbpaste']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
let s:cache_enabled = 0
return 'pbcopy'
endfunction
function! s:set_wayland() abort
let s:copy['+'] = ['wl-copy', '--type', 'text/plain']
let s:paste['+'] = ['wl-paste', '--no-newline']
let s:copy['*'] = ['wl-copy', '--primary', '--type', 'text/plain']
let s:paste['*'] = ['wl-paste', '--no-newline', '--primary']
return 'wl-copy'
endfunction
function! s:set_wayclip() abort
let s:copy['+'] = ['waycopy', '-t', 'text/plain']
let s:paste['+'] = ['waypaste', '-t', 'text/plain']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'wayclip'
endfunction
function! s:set_xsel() abort
let s:copy['+'] = ['xsel', '--nodetach', '-i', '-b']
let s:paste['+'] = ['xsel', '-o', '-b']
let s:copy['*'] = ['xsel', '--nodetach', '-i', '-p']
let s:paste['*'] = ['xsel', '-o', '-p']
return 'xsel'
endfunction
function! s:set_xclip() abort
let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard']
let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard']
let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary']
let s:paste['*'] = ['xclip', '-o', '-selection', 'primary']
return 'xclip'
endfunction
function! s:set_lemonade() abort
let s:copy['+'] = ['lemonade', 'copy']
let s:paste['+'] = ['lemonade', 'paste']
let s:copy['*'] = ['lemonade', 'copy']
let s:paste['*'] = ['lemonade', 'paste']
return 'lemonade'
endfunction
function! s:set_doitclient() abort
let s:copy['+'] = ['doitclient', 'wclip']
let s:paste['+'] = ['doitclient', 'wclip', '-r']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'doitclient'
endfunction
function! s:set_win32yank() abort
if has('wsl') && getftype(exepath('win32yank.exe')) == 'link'
let win32yank = resolve(exepath('win32yank.exe'))
else
let win32yank = 'win32yank.exe'
endif
let s:copy['+'] = [win32yank, '-i', '--crlf']
let s:paste['+'] = [win32yank, '-o', '--lf']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'win32yank'
endfunction
function! s:set_putclip() abort
let s:copy['+'] = ['putclip']
let s:paste['+'] = ['getclip']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'putclip'
endfunction
function! s:set_clip() abort
let s:copy['+'] = ['clip']
let s:paste['+'] = ['powershell', '-NoProfile', '-NoLogo', '-Command', 'Get-Clipboard']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'clip'
endfunction
function! s:set_termux() abort
let s:copy['+'] = ['termux-clipboard-set']
let s:paste['+'] = ['termux-clipboard-get']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'termux-clipboard'
endfunction
function! s:set_tmux() abort
let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V']))
if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0])
let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-']
else
let s:copy['+'] = ['tmux', 'load-buffer', '-']
endif
let s:paste['+'] = ['tmux', 'save-buffer', '-']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'tmux'
endfunction
let s:cache_enabled = 1 let s:cache_enabled = 1
let s:err = '' let s:err = ''
@@ -78,9 +185,34 @@ function! provider#clipboard#Executable() abort
" Setting g:clipboard to v:false explicitly opts-in to using the "builtin" clipboard providers below " Setting g:clipboard to v:false explicitly opts-in to using the "builtin" clipboard providers below
if exists('g:clipboard') && g:clipboard isnot# v:false if exists('g:clipboard') && g:clipboard isnot# v:false
if v:t_string ==# type(g:clipboard) if v:t_string ==# type(g:clipboard)
" Handle string form of g:clipboard for all builtin providers
if 'osc52' == g:clipboard if 'osc52' == g:clipboard
" User opted-in to OSC 52 by manually setting g:clipboard. " User opted-in to OSC 52 by manually setting g:clipboard.
return s:set_osc52() return s:set_osc52()
elseif 'pbcopy' == g:clipboard
return s:set_pbcopy()
elseif 'wl-copy' == g:clipboard
return s:set_wayland()
elseif 'wayclip' == g:clipboard
return s:set_wayclip()
elseif 'xsel' == g:clipboard
return s:set_xsel()
elseif 'xclip' == g:clipboard
return s:set_xclip()
elseif 'lemonade' == g:clipboard
return s:set_lemonade()
elseif 'doitclient' == g:clipboard
return s:set_doitclient()
elseif 'win32yank' == g:clipboard
return s:set_win32yank()
elseif 'putclip' == g:clipboard
return s:set_putclip()
elseif 'clip' == g:clipboard
return s:set_clip()
elseif 'termux' == g:clipboard
return s:set_termux()
elseif 'tmux' == g:clipboard
return s:set_tmux()
endif endif
endif endif
@@ -102,88 +234,29 @@ function! provider#clipboard#Executable() abort
let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0) let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0)
return get(g:clipboard, 'name', 'g:clipboard') return get(g:clipboard, 'name', 'g:clipboard')
elseif has('mac') elseif has('mac')
let s:copy['+'] = ['pbcopy'] return s:set_pbcopy()
let s:paste['+'] = ['pbpaste']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
let s:cache_enabled = 0
return 'pbcopy'
elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste')
let s:copy['+'] = ['wl-copy', '--type', 'text/plain'] return s:set_wayland()
let s:paste['+'] = ['wl-paste', '--no-newline']
let s:copy['*'] = ['wl-copy', '--primary', '--type', 'text/plain']
let s:paste['*'] = ['wl-paste', '--no-newline', '--primary']
return 'wl-copy'
elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste') elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste')
let s:copy['+'] = ['waycopy', '-t', 'text/plain'] return s:set_wayclip()
let s:paste['+'] = ['waypaste', '-t', 'text/plain']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'wayclip'
elseif !empty($DISPLAY) && executable('xsel') && s:cmd_ok('xsel -o -b') elseif !empty($DISPLAY) && executable('xsel') && s:cmd_ok('xsel -o -b')
let s:copy['+'] = ['xsel', '--nodetach', '-i', '-b'] return s:set_xsel()
let s:paste['+'] = ['xsel', '-o', '-b']
let s:copy['*'] = ['xsel', '--nodetach', '-i', '-p']
let s:paste['*'] = ['xsel', '-o', '-p']
return 'xsel'
elseif !empty($DISPLAY) && executable('xclip') elseif !empty($DISPLAY) && executable('xclip')
let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard'] return s:set_xclip()
let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard']
let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary']
let s:paste['*'] = ['xclip', '-o', '-selection', 'primary']
return 'xclip'
elseif executable('lemonade') elseif executable('lemonade')
let s:copy['+'] = ['lemonade', 'copy'] return s:set_lemonade()
let s:paste['+'] = ['lemonade', 'paste']
let s:copy['*'] = ['lemonade', 'copy']
let s:paste['*'] = ['lemonade', 'paste']
return 'lemonade'
elseif executable('doitclient') elseif executable('doitclient')
let s:copy['+'] = ['doitclient', 'wclip'] return s:set_doitclient()
let s:paste['+'] = ['doitclient', 'wclip', '-r']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'doitclient'
elseif executable('win32yank.exe') elseif executable('win32yank.exe')
if has('wsl') && getftype(exepath('win32yank.exe')) == 'link' return s:set_win32yank()
let win32yank = resolve(exepath('win32yank.exe'))
else
let win32yank = 'win32yank.exe'
endif
let s:copy['+'] = [win32yank, '-i', '--crlf']
let s:paste['+'] = [win32yank, '-o', '--lf']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'win32yank'
elseif executable('putclip') && executable('getclip') elseif executable('putclip') && executable('getclip')
let s:copy['+'] = ['putclip'] return s:set_putclip()
let s:paste['+'] = ['getclip']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'putclip'
elseif executable('clip') && executable('powershell') elseif executable('clip') && executable('powershell')
let s:copy['+'] = ['clip'] return s:set_clip()
let s:paste['+'] = ['powershell', '-NoProfile', '-NoLogo', '-Command', 'Get-Clipboard']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'clip'
elseif executable('termux-clipboard-set') elseif executable('termux-clipboard-set')
let s:copy['+'] = ['termux-clipboard-set'] return s:set_termux()
let s:paste['+'] = ['termux-clipboard-get']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'termux-clipboard'
elseif executable('tmux') && (!empty($TMUX) || 0 == jobwait([jobstart(['tmux', 'list-buffers'])], 2000)[0]) elseif executable('tmux') && (!empty($TMUX) || 0 == jobwait([jobstart(['tmux', 'list-buffers'])], 2000)[0])
let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V'])) return s:set_tmux()
if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0])
let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-']
else
let s:copy['+'] = ['tmux', 'load-buffer', '-']
endif
let s:paste['+'] = ['tmux', 'save-buffer', '-']
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'tmux'
elseif get(get(g:, 'termfeatures', {}), 'osc52') && &clipboard ==# '' elseif get(get(g:, 'termfeatures', {}), 'osc52') && &clipboard ==# ''
" Don't use OSC 52 when 'clipboard' is set. It can be slow and cause a lot " Don't use OSC 52 when 'clipboard' is set. It can be slow and cause a lot
" of user prompts. Users can opt-in to it by setting g:clipboard manually. " of user prompts. Users can opt-in to it by setting g:clipboard manually.
@@ -195,13 +268,11 @@ function! provider#clipboard#Executable() abort
endfunction endfunction
function! s:clipboard.get(reg) abort function! s:clipboard.get(reg) abort
if type(s:paste[a:reg]) == v:t_func if s:selections[a:reg].owner > 0
return s:paste[a:reg]()
elseif s:selections[a:reg].owner > 0
return s:selections[a:reg].data return s:selections[a:reg].data
end end
let clipboard_data = s:try_cmd(s:paste[a:reg]) let clipboard_data = type(s:paste[a:reg]) == v:t_func ? s:paste[a:reg]() : s:try_cmd(s:paste[a:reg])
if match(&clipboard, '\v(unnamed|unnamedplus)') >= 0 if match(&clipboard, '\v(unnamed|unnamedplus)') >= 0
\ && type(clipboard_data) == v:t_list \ && type(clipboard_data) == v:t_list
\ && get(s:selections[a:reg].data, 0, []) ==# clipboard_data \ && get(s:selections[a:reg].data, 0, []) ==# clipboard_data
@@ -221,13 +292,12 @@ function! s:clipboard.set(lines, regtype, reg) abort
return 0 return 0
end end
if type(s:copy[a:reg]) == v:t_func if s:cache_enabled == 0 || type(s:copy[a:reg]) == v:t_func
call s:copy[a:reg](a:lines, a:regtype) if type(s:copy[a:reg]) == v:t_func
return 0 call s:copy[a:reg](a:lines, a:regtype)
end else
call s:try_cmd(s:copy[a:reg], a:lines)
if s:cache_enabled == 0 endif
call s:try_cmd(s:copy[a:reg], a:lines)
"Cache it anyway we can compare it later to get regtype of the yank "Cache it anyway we can compare it later to get regtype of the yank
let s:selections[a:reg] = copy(s:selection) let s:selections[a:reg] = copy(s:selection)
let s:selections[a:reg].data = [a:lines, a:regtype] let s:selections[a:reg].data = [a:lines, a:regtype]
@@ -284,4 +354,4 @@ function! provider#clipboard#Call(method, args) abort
endfunction endfunction
" eval_has_provider() decides based on this variable. " eval_has_provider() decides based on this variable.
let g:loaded_clipboard_provider = empty(provider#clipboard#Executable()) ? 1 : 2 let g:loaded_clipboard_provider = empty(provider#clipboard#Executable()) ? 0 : 2

View File

@@ -1,7 +1,7 @@
if exists('g:loaded_node_provider') if exists('g:loaded_node_provider')
finish finish
endif endif
let g:loaded_node_provider = 1 let g:loaded_node_provider = 0
function! s:is_minimum_version(version, min_version) abort function! s:is_minimum_version(version, min_version) abort
if empty(a:version) if empty(a:version)
@@ -152,7 +152,7 @@ endfunction
let s:err = '' let s:err = ''
let [s:prog, s:_] = provider#node#Detect() let [s:prog, s:_] = provider#node#Detect()
let g:loaded_node_provider = empty(s:prog) ? 1 : 2 let g:loaded_node_provider = empty(s:prog) ? 0 : 2
if g:loaded_node_provider != 2 if g:loaded_node_provider != 2
let s:err = 'Cannot find the "neovim" node package. Try :checkhealth' let s:err = 'Cannot find the "neovim" node package. Try :checkhealth'

View File

@@ -11,5 +11,5 @@ function! provider#perl#Require(host) abort
endfunction endfunction
let s:prog = v:lua.vim.provider.perl.detect() let s:prog = v:lua.vim.provider.perl.detect()
let g:loaded_perl_provider = empty(s:prog) ? 1 : 2 let g:loaded_perl_provider = empty(s:prog) ? 0 : 2
call v:lua.require'vim.provider.perl'.start() call v:lua.require'vim.provider.perl'.start()

View File

@@ -11,5 +11,5 @@ function! provider#python3#Require(host) abort
endfunction endfunction
let s:prog = v:lua.vim.provider.python.detect_by_module('neovim') let s:prog = v:lua.vim.provider.python.detect_by_module('neovim')
let g:loaded_python3_provider = empty(s:prog) ? 1 : 2 let g:loaded_python3_provider = empty(s:prog) ? 0 : 2
call v:lua.require'vim.provider.python'.start() call v:lua.require'vim.provider.python'.start()

View File

@@ -11,6 +11,6 @@ function! provider#ruby#Call(method, args) abort
endfunction endfunction
let s:prog = v:lua.vim.provider.ruby.detect() let s:prog = v:lua.vim.provider.ruby.detect()
let g:loaded_ruby_provider = empty(s:prog) ? 1 : 2 let g:loaded_ruby_provider = empty(s:prog) ? 0 : 2
let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb' let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb'
call v:lua.require'vim.provider.ruby'.start(s:plugin_path) call v:lua.require'vim.provider.ruby'.start(s:plugin_path)

View File

@@ -77,49 +77,11 @@ function! tutor#TutorFolds()
endif endif
endfunction endfunction
" Marks: {{{1
function! tutor#ApplyMarks()
hi! link tutorExpect Special
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let b:tutor_sign_id = 1
for expct in keys(b:tutor_metadata['expect'])
let lnum = eval(expct)
call matchaddpos('tutorExpect', [lnum])
call tutor#CheckLine(lnum)
endfor
endif
endfunction
function! tutor#ApplyMarksOnChanged()
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let lnum = line('.')
if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
call tutor#CheckLine(lnum)
endif
endif
endfunction
function! tutor#CheckLine(line)
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let bufn = bufnr('%')
let ctext = getline(a:line)
let signs = sign_getplaced(bufn, {'lnum': a:line})[0].signs
if !empty(signs)
call sign_unplace('', {'id': signs[0].id})
endif
if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)]
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn
else
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn
endif
let b:tutor_sign_id+=1
endif
endfunction
" Tutor Cmd: {{{1 " Tutor Cmd: {{{1
function! s:Locale() function! s:Locale()
" Make sure l:lang exists before returning.
let l:lang = 'en_US'
if exists('v:lang') && v:lang =~ '\a\a' if exists('v:lang') && v:lang =~ '\a\a'
let l:lang = v:lang let l:lang = v:lang
elseif $LC_ALL =~ '\a\a' elseif $LC_ALL =~ '\a\a'
@@ -132,8 +94,6 @@ function! s:Locale()
endif endif
elseif $LANG =~ '\a\a' elseif $LANG =~ '\a\a'
let l:lang = $LANG let l:lang = $LANG
else
let l:lang = 'en_US'
endif endif
return split(l:lang, '_') return split(l:lang, '_')
endfunction endfunction
@@ -167,15 +127,21 @@ function! s:Sort(a, b)
return retval return retval
endfunction endfunction
function! s:GlobTutorials(name) " returns a list of all tutor files matching the given name
function! tutor#GlobTutorials(name, locale)
let locale = a:locale
" pack/*/start/* are not reported in &rtp
let rtp = nvim_list_runtime_paths()
\ ->map({_, v -> escape(v:lua.vim.fs.normalize(v), ',')})
\ ->join(',')
" search for tutorials: " search for tutorials:
" 1. non-localized " 1. non-localized
let l:tutors = s:GlobPath(&rtp, 'tutor/'.a:name.'.tutor') let l:tutors = s:GlobPath(rtp, 'tutor/'.a:name.'.tutor')
" 2. localized for current locale " 2. localized for current locale
let l:locale_tutors = s:GlobPath(&rtp, 'tutor/'.s:Locale()[0].'/'.a:name.'.tutor') let l:locale_tutors = s:GlobPath(rtp, 'tutor/'.locale.'/'.a:name.'.tutor')
" 3. fallback to 'en' " 3. fallback to 'en'
if len(l:locale_tutors) == 0 if len(l:locale_tutors) == 0
let l:locale_tutors = s:GlobPath(&rtp, 'tutor/en/'.a:name.'.tutor') let l:locale_tutors = s:GlobPath(rtp, 'tutor/en/'.a:name.'.tutor')
endif endif
call extend(l:tutors, l:locale_tutors) call extend(l:tutors, l:locale_tutors)
return uniq(sort(l:tutors, 's:Sort'), 's:Sort') return uniq(sort(l:tutors, 's:Sort'), 's:Sort')
@@ -197,7 +163,7 @@ function! tutor#TutorCmd(tutor_name)
let l:tutor_name = fnamemodify(l:tutor_name, ':r') let l:tutor_name = fnamemodify(l:tutor_name, ':r')
endif endif
let l:tutors = s:GlobTutorials(l:tutor_name) let l:tutors = tutor#GlobTutorials(l:tutor_name, s:Locale()[0])
if len(l:tutors) == 0 if len(l:tutors) == 0
echom "No tutorial with that name found" echom "No tutorial with that name found"
@@ -220,15 +186,37 @@ function! tutor#TutorCmd(tutor_name)
call tutor#SetupVim() call tutor#SetupVim()
exe "edit ".l:to_open exe "edit ".l:to_open
call tutor#EnableInteractive(v:true)
call tutor#ApplyTransform() call tutor#ApplyTransform()
endfunction endfunction
function! tutor#TutorCmdComplete(lead,line,pos) function! tutor#TutorCmdComplete(lead,line,pos)
let l:tutors = s:GlobTutorials('*') let l:tutors = tutor#GlobTutorials('*', s:Locale()[0])
let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort')) let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort'))
return join(l:names, "\n") return join(l:names, "\n")
endfunction endfunction
" Enables/disables interactive mode.
function! tutor#EnableInteractive(enable)
let enable = a:enable
if enable
setlocal buftype=nofile
setlocal concealcursor+=inv
setlocal conceallevel=2
lua require('nvim.tutor').apply_marks()
augroup tutor_interactive
autocmd! TextChanged,TextChangedI <buffer> lua require('nvim.tutor').apply_marks_on_changed()
augroup END
else
setlocal buftype<
setlocal concealcursor<
setlocal conceallevel<
if exists('#tutor_interactive')
autocmd! tutor_interactive * <buffer>
endif
endif
endfunction
function! tutor#ApplyTransform() function! tutor#ApplyTransform()
if has('win32') if has('win32')
sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\2/g sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\2/g

View File

@@ -1183,10 +1183,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()*
ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals
like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua
vim.api.nvim_create_user_command('TermHl', function() vim.api.nvim_create_user_command('TermHl', function()
local b = vim.api.nvim_create_buf(false, true) vim.api.nvim_open_term(0, {})
local chan = vim.api.nvim_open_term(b, {})
vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
vim.api.nvim_win_set_buf(0, b)
end, { desc = 'Highlights ANSI termcodes in curbuf' }) end, { desc = 'Highlights ANSI termcodes in curbuf' })
< <
@@ -2777,8 +2774,11 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {}) vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
< <
If `end` is less than `start`, traversal works backwards. (Useful with If `end` is less than `start`, marks are returned in reverse order.
`limit`, to get the first marks prior to a given position.) (Useful with `limit`, to get the first marks prior to a given position.)
Note: For a reverse range, `limit` does not actually affect the traversed
range, just how many marks are returned
Note: when using extmark ranges (marks with a end_row/end_col position) Note: when using extmark ranges (marks with a end_row/end_col position)
the `overlap` option might be useful. Otherwise only the start position of the `overlap` option might be useful. Otherwise only the start position of
@@ -3536,7 +3536,8 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
the duration of the call. the duration of the call.
• fixed: If true when anchor is NW or SW, the float window • fixed: If true when anchor is NW or SW, the float window
would be kept fixed even if the window would be truncated. would be kept fixed even if the window would be truncated.
• hide: If true the floating window will be hidden. • hide: If true the floating window will be hidden and the
cursor will be invisible when focused on it.
• vertical: Split vertically |:vertical|. • vertical: Split vertically |:vertical|.
• split: Split direction: "left", "right", "above", "below". • split: Split direction: "left", "right", "above", "below".
@@ -3773,8 +3774,8 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()*
• callback (function|string) optional: Lua function (or • callback (function|string) optional: Lua function (or
Vimscript function name, if string) called when the Vimscript function name, if string) called when the
event(s) is triggered. Lua callback can return a truthy event(s) is triggered. Lua callback can return a truthy
value (not `false` or `nil`) to delete the autocommand. value (not `false` or `nil`) to delete the autocommand, and
Receives one argument, a table with these keys: receives one argument, a table with these keys:
*event-args* *event-args*
• id: (number) autocommand id • id: (number) autocommand id
• event: (string) name of the triggered event • event: (string) name of the triggered event

View File

@@ -311,7 +311,7 @@ Nvim's filetype detection behavior matches Vim, but is implemented as part of
|vim.filetype| (see `$VIMRUNTIME/lua/vim/filetype.lua`). The logic is encoded in |vim.filetype| (see `$VIMRUNTIME/lua/vim/filetype.lua`). The logic is encoded in
three tables, listed in order of precedence (the first match is returned): three tables, listed in order of precedence (the first match is returned):
1. `filename` for literal full path or basename lookup; 1. `filename` for literal full path or basename lookup;
2. `pattern` for matching filenames or paths against |lua-patterns|, optimized 2. `pattern` for matching filenames or paths against |lua-pattern|s, optimized
for fast lookup; for fast lookup;
3. `extension` for literal extension lookup. 3. `extension` for literal extension lookup.

View File

@@ -441,6 +441,7 @@ Use existing common {verb} names (actions) if possible:
- get: Gets things. Two variants (overloads): - get: Gets things. Two variants (overloads):
1. `get<T>(id: int): T` returns one item. 1. `get<T>(id: int): T` returns one item.
2. `get<T>(filter: dict): T[]` returns a list. 2. `get<T>(filter: dict): T[]` returns a list.
- has: Checks for the presence of an item, feature, etc.
- inspect: Presents a high-level, often interactive, view - inspect: Presents a high-level, often interactive, view
- is_enabled: Checks if functionality is enabled. - is_enabled: Checks if functionality is enabled.
- open: Opens something (a buffer, window, …) - open: Opens something (a buffer, window, …)
@@ -452,6 +453,7 @@ Use existing common {verb} names (actions) if possible:
- try_{verb}: Best-effort operation, failure returns null or error obj - try_{verb}: Best-effort operation, failure returns null or error obj
Do NOT use these deprecated verbs: Do NOT use these deprecated verbs:
- contains: Prefer "has".
- disable: Prefer `enable(enable: boolean)`. - disable: Prefer `enable(enable: boolean)`.
- exit: Prefer "cancel" (or "stop" if appropriate). - exit: Prefer "cancel" (or "stop" if appropriate).
- is_disabled: Prefer `is_enabled()`. - is_disabled: Prefer `is_enabled()`.

View File

@@ -69,6 +69,16 @@ Functions that take a severity as an optional parameter (e.g.
< <
This form allows users to filter for specific severities This form allows users to filter for specific severities
==============================================================================
DEFAULTS *diagnostic-defaults*
These diagnostic keymaps are created unconditionally when Nvim starts:
- `]d` jumps to the next diagnostic in the buffer. |]d-default|
- `[d` jumps to the previous diagnostic in the buffer. |[d-default|
- `]D` jumps to the last diagnostic in the buffer. |]D-default|
- `[D` jumps to the first diagnostic in the buffer. |[D-default|
- `<C-w>d` shows diagnostic at cursor in a floating window. |CTRL-W_d-default|
============================================================================== ==============================================================================
HANDLERS *diagnostic-handlers* HANDLERS *diagnostic-handlers*
@@ -269,6 +279,26 @@ DiagnosticVirtualTextHint
DiagnosticVirtualTextOk DiagnosticVirtualTextOk
Used for "Ok" diagnostic virtual text. Used for "Ok" diagnostic virtual text.
*hl-DiagnosticVirtualLinesError*
DiagnosticVirtualLinesError
Used for "Error" diagnostic virtual lines.
*hl-DiagnosticVirtualLinesWarn*
DiagnosticVirtualLinesWarn
Used for "Warn" diagnostic virtual lines.
*hl-DiagnosticVirtualLinesInfo*
DiagnosticVirtualLinesInfo
Used for "Info" diagnostic virtual lines.
*hl-DiagnosticVirtualLinesHint*
DiagnosticVirtualLinesHint
Used for "Hint" diagnostic virtual lines.
*hl-DiagnosticVirtualLinesOk*
DiagnosticVirtualLinesOk
Used for "Ok" diagnostic virtual lines.
*hl-DiagnosticUnderlineError* *hl-DiagnosticUnderlineError*
DiagnosticUnderlineError DiagnosticUnderlineError
Used to underline "Error" diagnostics. Used to underline "Error" diagnostics.
@@ -574,8 +604,8 @@ Lua module: vim.diagnostic *diagnostic-api*
*vim.diagnostic.Opts.Signs* *vim.diagnostic.Opts.Signs*
Fields: ~ Fields: ~
• {severity}? (`vim.diagnostic.SeverityFilter`) Only show virtual text • {severity}? (`vim.diagnostic.SeverityFilter`) Only show signs for
for diagnostics matching the given severity diagnostics matching the given severity
|diagnostic-severity| |diagnostic-severity|
• {priority}? (`integer`, default: `10`) Base priority to use for • {priority}? (`integer`, default: `10`) Base priority to use for
signs. When {severity_sort} is used, the priority of a signs. When {severity_sort} is used, the priority of a
@@ -607,6 +637,9 @@ Lua module: vim.diagnostic *diagnostic-api*
*vim.diagnostic.Opts.VirtualLines* *vim.diagnostic.Opts.VirtualLines*
Fields: ~ Fields: ~
• {severity}? (`vim.diagnostic.SeverityFilter`) Only show virtual
lines for diagnostics matching the given severity
|diagnostic-severity|
• {current_line}? (`boolean`, default: `false`) Only show diagnostics • {current_line}? (`boolean`, default: `false`) Only show diagnostics
for the current line. for the current line.
• {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A

View File

@@ -714,11 +714,14 @@ list of the current window.
omitted the current entry is used. omitted the current entry is used.
Also see |++opt| and |+cmd|. Also see |++opt| and |+cmd|.
:[count]n[ext] [++opt] [+cmd] *:n* *:ne* *:next* *]a* *E165* *E163* :[count]n[ext] [++opt] [+cmd] *:n* *:ne* *:next* *E165* *E163*
Edit [count] next file. This fails when changes have Edit [count] next file. This fails when changes have
been made and Vim does not want to |abandon| the been made and Vim does not want to |abandon| the
current buffer. Also see |++opt| and |+cmd|. current buffer. Also see |++opt| and |+cmd|.
*]a*
]a Mapped to |:next|. |default-mappings|
:[count]n[ext]! [++opt] [+cmd] :[count]n[ext]! [++opt] [+cmd]
Edit [count] next file, discard any changes to the Edit [count] next file, discard any changes to the
buffer. Also see |++opt| and |+cmd|. buffer. Also see |++opt| and |+cmd|.
@@ -740,16 +743,22 @@ list of the current window.
any changes to the buffer. Also see |++opt| and any changes to the buffer. Also see |++opt| and
|+cmd|. |+cmd|.
:[count]prev[ious] [count] [++opt] [+cmd] *:prev* *:previous* *[a* :[count]prev[ious] [count] [++opt] [+cmd] *:prev* *:previous*
Same as :Next. Also see |++opt| and |+cmd|. Same as :Next. Also see |++opt| and |+cmd|.
*:rew* *:rewind* *[A* *[a*
[a Mapped to |:previous|. |default-mappings|
*:rew* *:rewind*
:rew[ind] [++opt] [+cmd] :rew[ind] [++opt] [+cmd]
Start editing the first file in the argument list. Start editing the first file in the argument list.
This fails when changes have been made and Vim does This fails when changes have been made and Vim does
not want to |abandon| the current buffer. not want to |abandon| the current buffer.
Also see |++opt| and |+cmd|. Also see |++opt| and |+cmd|.
*[A*
[A Mapped to |:rewind|. |default-mappings|
:rew[ind]! [++opt] [+cmd] :rew[ind]! [++opt] [+cmd]
Start editing the first file in the argument list. Start editing the first file in the argument list.
Discard any changes to the buffer. Also see |++opt| Discard any changes to the buffer. Also see |++opt|
@@ -759,13 +768,16 @@ list of the current window.
:fir[st][!] [++opt] [+cmd] :fir[st][!] [++opt] [+cmd]
Other name for ":rewind". Other name for ":rewind".
*:la* *:last* *]A* *:la* *:last*
:la[st] [++opt] [+cmd] :la[st] [++opt] [+cmd]
Start editing the last file in the argument list. Start editing the last file in the argument list.
This fails when changes have been made and Vim does This fails when changes have been made and Vim does
not want to |abandon| the current buffer. not want to |abandon| the current buffer.
Also see |++opt| and |+cmd|. Also see |++opt| and |+cmd|.
*]A*
]A Mapped to |:last|. |default-mappings|
:la[st]! [++opt] [+cmd] :la[st]! [++opt] [+cmd]
Start editing the last file in the argument list. Start editing the last file in the argument list.
Discard any changes to the buffer. Also see |++opt| Discard any changes to the buffer. Also see |++opt|

View File

@@ -187,35 +187,28 @@ Run |:checkhealth| in Nvim for automatic diagnosis.
Other hints: Other hints:
- The python `neovim` module was renamed to `pynvim` (long ago). - Read |provider-python| to learn how to install `pynvim`.
- If you're using pyenv or virtualenv for the `pynvim` module
https://pypi.org/project/pynvim/, you must set `g:python3_host_prog` to
the virtualenv's interpreter path.
- Read |provider-python|.
- Be sure you have the latest version of the `pynvim` Python module: >bash - Be sure you have the latest version of the `pynvim` Python module: >bash
python -m pip install setuptools uv tool install --upgrade pynvim
python -m pip install --upgrade pynvim
python3 -m pip install --upgrade pynvim
< <
See |provider-python| for other installation options.
- If you're manually creating a Python virtual environment for the `pynvim` module
https://pypi.org/project/pynvim/, you must set `g:python3_host_prog` to
the virtualenv's interpreter path.
- Try with `nvim -u NORC` to make sure your config (|init.vim|) isn't causing a - Try with `nvim -u NORC` to make sure your config (|init.vim|) isn't causing a
problem. If you get `E117: Unknown function`, that means there's a runtime problem. If you get `E117: Unknown function`, that means there's a runtime
issue: |faq-runtime|. issue: |faq-runtime|.
- The python `neovim` module was renamed to `pynvim` (long ago).
:CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME ~ :CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME ~
This means `health#check()` couldn't load, which suggests that |$VIMRUNTIME| This means |$VIMRUNTIME| or 'runtimepath' is broken.
or 'runtimepath' is broken.
- |$VIMRUNTIME| must point to Nvim's runtime files, not Vim's. - |$VIMRUNTIME| must point to Nvim's runtime files, not Vim's.
- The |$VIMRUNTIME| directory contents should be readable by the current user. - The |$VIMRUNTIME| directory contents should be readable by the current user.
- Verify that `:echo &runtimepath` contains the $VIMRUNTIME path. - Verify that `:echo &runtimepath` contains the $VIMRUNTIME path.
- Check the output of: >vim
:call health#check()
:verbose func health#check
<
NEOVIM CAN'T FIND ITS RUNTIME ~ NEOVIM CAN'T FIND ITS RUNTIME ~

View File

@@ -9,18 +9,18 @@
============================================================================== ==============================================================================
Checkhealth *vim.health* *health* Checkhealth *vim.health* *health*
vim.health is a minimal framework to help users troubleshoot configuration and vim.health is a minimal framework to help users troubleshoot configuration and
any other environment conditions that a plugin might care about. Nvim ships any other environment conditions that a plugin might care about. Nvim ships
with healthchecks for configuration, performance, python support, ruby with healthchecks for configuration, performance, python support, ruby
support, clipboard support, and more. support, clipboard support, and more.
To run all healthchecks, use: >vim To run all healthchecks, use: >vim
:checkhealth
:checkhealth
< <
Plugin authors are encouraged to write new healthchecks. |health-dev| Plugin authors are encouraged to write new healthchecks. |health-dev|
COMMANDS *health-commands* COMMANDS *health-commands*
*:che* *:checkhealth* *:che* *:checkhealth*
@@ -56,7 +56,6 @@ Local mappings in the healthcheck buffer:
q Closes the window. q Closes the window.
Global configuration: Global configuration:
*g:health* *g:health*
g:health Dictionary with the following optional keys: g:health Dictionary with the following optional keys:
- `style` (`'float'|nil`) Set to "float" to display :checkhealth in - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
@@ -65,16 +64,26 @@ g:health Dictionary with the following optional keys:
Example: >lua Example: >lua
vim.g.health = { style = 'float' } vim.g.health = { style = 'float' }
Local configuration:
Checkhealth sets its buffer filetype to "checkhealth". You can customize the
buffer by handling the |FileType| event. For example if you don't want emojis
in the health report: >vim
autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
<
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Create a healthcheck *health-dev* Create a healthcheck *health-dev*
Healthchecks are functions that check the user environment, configuration, or Healthchecks are functions that check the user environment, configuration, or
any other prerequisites that a plugin cares about. Nvim ships with any other prerequisites that a plugin cares about. Nvim ships with
healthchecks in: healthchecks in:
- $VIMRUNTIME/autoload/health/ $VIMRUNTIME/autoload/health/
- $VIMRUNTIME/lua/vim/lsp/health.lua $VIMRUNTIME/lua/vim/lsp/health.lua
- $VIMRUNTIME/lua/vim/treesitter/health.lua $VIMRUNTIME/lua/vim/treesitter/health.lua
- and more... and more...
To add a new healthcheck for your own plugin, simply create a "health.lua" To add a new healthcheck for your own plugin, simply create a "health.lua"
module on 'runtimepath' that returns a table with a "check()" function. Then module on 'runtimepath' that returns a table with a "check()" function. Then
@@ -82,35 +91,35 @@ module on 'runtimepath' that returns a table with a "check()" function. Then
For example if your plugin is named "foo", define your healthcheck module at For example if your plugin is named "foo", define your healthcheck module at
one of these locations (on 'runtimepath'): one of these locations (on 'runtimepath'):
- lua/foo/health/init.lua lua/foo/health/init.lua
- lua/foo/health.lua lua/foo/health.lua
If your plugin also provides a submodule named "bar" for which you want If your plugin also provides a submodule named "bar" for which you want a
a separate healthcheck, define the healthcheck at one of these locations: separate healthcheck, define the healthcheck at one of these locations:
- lua/foo/bar/health/init.lua lua/foo/bar/health/init.lua
- lua/foo/bar/health.lua lua/foo/bar/health.lua
All such health modules must return a Lua table containing a `check()` All such health modules must return a Lua table containing a `check()`
function. function.
Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path
with your plugin name: >lua with your plugin name: >lua
local M = {}
local M = {} M.check = function()
vim.health.start("foo report")
-- make sure setup function parameters are ok
if check_setup() then
vim.health.ok("Setup is correct")
else
vim.health.error("Setup is incorrect")
end
-- do some more checking
-- ...
end
M.check = function() return M
vim.health.start("foo report") <
-- make sure setup function parameters are ok
if check_setup() then
vim.health.ok("Setup is correct")
else
vim.health.error("Setup is incorrect")
end
-- do some more checking
-- ...
end
return M
error({msg}, {...}) *vim.health.error()* error({msg}, {...}) *vim.health.error()*

View File

@@ -373,11 +373,17 @@ To quote a block of ex-commands verbatim, place a greater than (>) character
at the end of the line before the block and a less than (<) character as the at the end of the line before the block and a less than (<) character as the
first non-blank on a line following the block. Any line starting in column 1 first non-blank on a line following the block. Any line starting in column 1
also implicitly stops the block of ex-commands before it. E.g. > also implicitly stops the block of ex-commands before it. E.g. >
function Example_Func() function Example_Func()
echo "Example" echo "Example"
endfunction endfunction
< <
To enable syntax highlighting for a block of code, place a language name
annotation (e.g. "vim") after a greater than (>) character. E.g. >vim
function Example_Func()
echo "Example"
endfunction
<
*help-notation*
The following are highlighted differently in a Vim help file: The following are highlighted differently in a Vim help file:
- a special key name expressed either in <> notation as in <PageDown>, or - a special key name expressed either in <> notation as in <PageDown>, or
as a Ctrl character as in CTRL-X as a Ctrl character as in CTRL-X

View File

@@ -1923,7 +1923,7 @@ These commands are used to start inserting text. You can end insert mode with
<Esc>. See |mode-ins-repl| for the other special characters in Insert mode. <Esc>. See |mode-ins-repl| for the other special characters in Insert mode.
The effect of [count] takes place after Insert mode is exited. The effect of [count] takes place after Insert mode is exited.
The following commands insert text, but stay in normal mode: The following |default-mappings| insert text, but stay in normal mode:
*]<Space>* *]<Space>*
]<Space> Insert an empty line below the cursor without leaving ]<Space> Insert an empty line below the cursor without leaving

View File

@@ -28,38 +28,42 @@ Follow these steps to get LSP features:
upstream installation instructions. You can find language servers here: upstream installation instructions. You can find language servers here:
https://microsoft.github.io/language-server-protocol/implementors/servers/ https://microsoft.github.io/language-server-protocol/implementors/servers/
2. Use |vim.lsp.config()| to define a configuration for an LSP client. 2. Use |vim.lsp.config()| to define a configuration for an LSP client
Example: >lua (see https://github.com/neovim/nvim-lspconfig for examples).
vim.lsp.config['luals'] = { Example: >lua
-- Command and arguments to start the server. vim.lsp.config['luals'] = {
cmd = { 'lua-language-server' }, -- Command and arguments to start the server.
cmd = { 'lua-language-server' },
-- Filetypes to automatically attach to. -- Filetypes to automatically attach to.
filetypes = { 'lua' }, filetypes = { 'lua' },
-- Sets the "root directory" to the parent directory of the file in the -- Sets the "root directory" to the parent directory of the file in the
-- current buffer that contains either a ".luarc.json" or a -- current buffer that contains either a ".luarc.json" or a
-- ".luarc.jsonc" file. Files that share a root directory will reuse -- ".luarc.jsonc" file. Files that share a root directory will reuse
-- the connection to the same LSP server. -- the connection to the same LSP server.
root_markers = { '.luarc.json', '.luarc.jsonc' }, -- Nested lists indicate equal priority, see |vim.lsp.Config|.
root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
-- Specific settings to send to the server. The schema for this is
-- defined by the server. For example the schema for lua-language-server
-- can be found here https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json
settings = {
Lua = {
runtime = {
version = 'LuaJIT',
}
}
}
}
-- Specific settings to send to the server. The schema for this is
-- defined by the server. For example the schema for lua-language-server
-- can be found here https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json
settings = {
Lua = {
runtime = {
version = 'LuaJIT',
}
}
}
}
<
3. Use |vim.lsp.enable()| to enable a configuration. 3. Use |vim.lsp.enable()| to enable a configuration.
Example: >lua Example: >lua
vim.lsp.enable('luals') vim.lsp.enable('luals')
< <
4. Restart Nvim, or use ":edit" to reload the buffer. 4. Open a code file matching one of the `filetypes` specified in the config.
Note: Depending on the LSP server, you may need to ensure your project has
a |lsp-root_markers| file so the workspace can be recognized.
5. Check that LSP is active ("attached") for the buffer: >vim 5. Check that LSP is active ("attached") for the buffer: >vim
:checkhealth vim.lsp :checkhealth vim.lsp
@@ -76,6 +80,18 @@ listed below, if (1) the language server supports the functionality and (2)
the options are empty or were set by the builtin runtime (ftplugin) files. The the options are empty or were set by the builtin runtime (ftplugin) files. The
options are not restored when the LSP client is stopped or detached. options are not restored when the LSP client is stopped or detached.
GLOBAL DEFAULTS
*grr* *gra* *grn* *gri* *grt* *i_CTRL-S*
These GLOBAL keymaps are created unconditionally when Nvim starts:
- "grn" is mapped in Normal mode to |vim.lsp.buf.rename()|
- "gra" is mapped in Normal and Visual mode to |vim.lsp.buf.code_action()|
- "grr" is mapped in Normal mode to |vim.lsp.buf.references()|
- "gri" is mapped in Normal mode to |vim.lsp.buf.implementation()|
- "grt" is mapped in Normal mode to |vim.lsp.buf.type_definition()|
- "gO" is mapped in Normal mode to |vim.lsp.buf.document_symbol()|
- CTRL-S is mapped in Insert mode to |vim.lsp.buf.signature_help()|
BUFFER-LOCAL DEFAULTS
- 'omnifunc' is set to |vim.lsp.omnifunc()|, use |i_CTRL-X_CTRL-O| to trigger - 'omnifunc' is set to |vim.lsp.omnifunc()|, use |i_CTRL-X_CTRL-O| to trigger
completion. completion.
- 'tagfunc' is set to |vim.lsp.tagfunc()|. This enables features like - 'tagfunc' is set to |vim.lsp.tagfunc()|. This enables features like
@@ -87,21 +103,11 @@ options are not restored when the LSP client is stopped or detached.
- |K| is mapped to |vim.lsp.buf.hover()| unless |'keywordprg'| is customized or - |K| is mapped to |vim.lsp.buf.hover()| unless |'keywordprg'| is customized or
a custom keymap for `K` exists. a custom keymap for `K` exists.
*grr* *gra* *grn* *gri* *i_CTRL-S* DISABLING DEFAULTS *lsp-defaults-disable*
Some keymaps are created unconditionally when Nvim starts: You can remove GLOBAL keymaps at any time using |vim.keymap.del()| or
- "grn" is mapped in Normal mode to |vim.lsp.buf.rename()| |:unmap|. See also |gr-default|.
- "gra" is mapped in Normal and Visual mode to |vim.lsp.buf.code_action()|
- "grr" is mapped in Normal mode to |vim.lsp.buf.references()|
- "gri" is mapped in Normal mode to |vim.lsp.buf.implementation()|
- "gO" is mapped in Normal mode to |vim.lsp.buf.document_symbol()|
- CTRL-S is mapped in Insert mode to |vim.lsp.buf.signature_help()|
If not wanted, these keymaps can be removed at any time using To remove or override BUFFER-LOCAL defaults, define a |LspAttach| handler: >lua
|vim.keymap.del()| or |:unmap| (see also |gr-default|).
*lsp-defaults-disable*
To override or delete any of the above defaults, set or unset the options on
|LspAttach|: >lua
vim.api.nvim_create_autocmd('LspAttach', { vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args) callback = function(args)
@@ -129,7 +135,7 @@ following (in increasing priority):
1. Configuration defined for the `'*'` name. 1. Configuration defined for the `'*'` name.
2. Configuration from the result of merging all tables returned by 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. 3. Configurations defined anywhere else.
Note: The merge semantics of configurations follow the behaviour of Note: The merge semantics of configurations follow the behaviour of
@@ -250,8 +256,14 @@ FAQ *lsp-faq*
" (async = false is the default for format) " (async = false is the default for format)
autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false }) 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* *lsp-vs-treesitter*
- Q: How do LSP and Treesitter compare? - Q: How do LSP, Treesitter and Ctags compare?
- A: LSP requires a client and language server. The language server uses - A: LSP requires a client and language server. The language server uses
semantic analysis to understand code at a project level. This provides semantic analysis to understand code at a project level. This provides
language servers with the ability to rename across files, find language servers with the ability to rename across files, find
@@ -263,6 +275,11 @@ FAQ *lsp-faq*
like syntax highlighting, simple goto-definitions, scope analysis and like syntax highlighting, simple goto-definitions, scope analysis and
more. more.
A |ctags|-like program can generate a |tags| file that allows Nvim to
jump to definitions, provide simple completions via |i_CTRL-X_CTRL-]|
command. It is not as featureful and doesn't have semantic understanding,
but it is fast, lightweight and useful for navigating polyglot projects.
================================================================================ ================================================================================
LSP API *lsp-api* LSP API *lsp-api*
@@ -286,38 +303,33 @@ They are also listed below.
- `'callHierarchy/incomingCalls'` - `'callHierarchy/incomingCalls'`
- `'callHierarchy/outgoingCalls'` - `'callHierarchy/outgoingCalls'`
- `'textDocument/codeAction'` - `'client/registerCapability'`
- `'client/unregisterCapability'`
- `'signature_help'`
- `'textDocument/codeLens'`
- `'textDocument/completion'` - `'textDocument/completion'`
- `'textDocument/declaration'`
- `'textDocument/definition'`
- `'textDocument/diagnostic'` - `'textDocument/diagnostic'`
- `'textDocument/documentHighlight'` - `'textDocument/documentHighlight'`
- `'textDocument/documentSymbol'` - `'textDocument/documentSymbol'`
- `'textDocument/foldingRange'`
- `'textDocument/formatting'` - `'textDocument/formatting'`
- `'textDocument/hover'` - `'textDocument/hover'`
- `'textDocument/implementation'`
- `'textDocument/inlayHint'` - `'textDocument/inlayHint'`
- `'textDocument/prepareTypeHierarchy'`
- `'textDocument/publishDiagnostics'` - `'textDocument/publishDiagnostics'`
- `'textDocument/rangeFormatting'` - `'textDocument/rangeFormatting'`
- `'textDocument/rangesFormatting'`
- `'textDocument/references'`
- `'textDocument/rename'` - `'textDocument/rename'`
- `'textDocument/semanticTokens/full'`
- `'textDocument/semanticTokens/full/delta'`
- `'textDocument/signatureHelp'` - `'textDocument/signatureHelp'`
- `'textDocument/typeDefinition*'`
- `'typeHierarchy/subtypes'` - `'typeHierarchy/subtypes'`
- `'typeHierarchy/supertypes'` - `'typeHierarchy/supertypes'`
- `'window/logMessage'` - `'window/logMessage'`
- `'window/showMessage'`
- `'window/showDocument'` - `'window/showDocument'`
- `'window/showMessage'`
- `'window/showMessageRequest'` - `'window/showMessageRequest'`
- `'window/workDoneProgress/create'`
- `'workspace/applyEdit'` - `'workspace/applyEdit'`
- `'workspace/configuration'` - `'workspace/configuration'`
- `'workspace/executeCommand'` - `'workspace/executeCommand'`
- `'workspace/inlayHint/refresh'` - `'workspace/inlayHint/refresh'`
- `'workspace/semanticTokens/refresh'`
- `'workspace/symbol'` - `'workspace/symbol'`
- `'workspace/workspaceFolders'` - `'workspace/workspaceFolders'`
@@ -552,10 +564,19 @@ LspAttach *LspAttach*
|autocmd-pattern| is the buffer name. The client ID is passed in the |autocmd-pattern| is the buffer name. The client ID is passed in the
Lua handler |event-data| argument. Lua handler |event-data| argument.
Example: >lua
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(ev)
local client = vim.lsp.get_client_by_id(ev.data.client_id)
-- ...
end
})
<
Note: If the LSP server performs dynamic registration, capabilities may be Note: If the LSP server performs dynamic registration, capabilities may be
registered any time _after_ LspAttach. In that case you may want to handle registered any time _after_ LspAttach. In that case you may want to handle
the "registerCapability" event. Example: >lua the "registerCapability" event.
Example: >lua
vim.lsp.handlers['client/registerCapability'] = (function(overridden) vim.lsp.handlers['client/registerCapability'] = (function(overridden)
return function(err, res, ctx) return function(err, res, ctx)
local result = overridden(err, res, ctx) local result = overridden(err, res, ctx)
@@ -563,8 +584,10 @@ LspAttach *LspAttach*
if not client then if not client then
return return
end end
-- Call your custom on_attach logic... for bufnr, _ in pairs(client.attached_buffers) do
-- my_on_attach(client, vim.api.nvim_get_current_buf()) -- Call your custom on_attach logic...
-- my_on_attach(client, bufnr)
end
return result return result
end end
end)(vim.lsp.handlers['client/registerCapability']) end)(vim.lsp.handlers['client/registerCapability'])
@@ -572,8 +595,9 @@ LspAttach *LspAttach*
LspDetach *LspDetach* LspDetach *LspDetach*
Just before an LSP client detaches from a buffer. The |autocmd-pattern| is Just before an LSP client detaches from a buffer. The |autocmd-pattern| is
the buffer name. The client ID is passed in the Lua handler |event-data| the buffer name. The client ID is passed in the Lua handler |event-data|
argument. Example: >lua argument.
Example: >lua
vim.api.nvim_create_autocmd('LspDetach', { vim.api.nvim_create_autocmd('LspDetach', {
callback = function(args) callback = function(args)
-- Get the detaching client -- Get the detaching client
@@ -595,8 +619,9 @@ LspNotify *LspNotify*
LSP server. LSP server.
The client_id, LSP method, and parameters are sent in the Lua handler The client_id, LSP method, and parameters are sent in the Lua handler
|event-data| table argument. Example: >lua |event-data| table argument.
Example: >lua
vim.api.nvim_create_autocmd('LspNotify', { vim.api.nvim_create_autocmd('LspNotify', {
callback = function(args) callback = function(args)
local bufnr = args.buf local bufnr = args.buf
@@ -642,8 +667,9 @@ LspRequest *LspRequest*
The Lua handler |event-data| argument has the client ID, request ID, and The Lua handler |event-data| argument has the client ID, request ID, and
request (described at |vim.lsp.Client|, {requests} field). If the request request (described at |vim.lsp.Client|, {requests} field). If the request
type is `complete`, the request will be deleted from the client's pending type is `complete`, the request will be deleted from the client's pending
requests table after processing the event handlers. Example: >lua requests table after processing the event handlers.
Example: >lua
vim.api.nvim_create_autocmd('LspRequest', { vim.api.nvim_create_autocmd('LspRequest', {
callback = function(args) callback = function(args)
local bufnr = args.buf local bufnr = args.buf
@@ -670,8 +696,9 @@ LspTokenUpdate *LspTokenUpdate*
when an existing token becomes visible for the first time. The when an existing token becomes visible for the first time. The
|autocmd-pattern| is the buffer name. The Lua handler |event-data| |autocmd-pattern| is the buffer name. The Lua handler |event-data|
argument has the client ID and token (see argument has the client ID and token (see
|vim.lsp.semantic_tokens.get_at_pos()|). Example: >lua |vim.lsp.semantic_tokens.get_at_pos()|).
Example: >lua
vim.api.nvim_create_autocmd('LspTokenUpdate', { vim.api.nvim_create_autocmd('LspTokenUpdate', {
callback = function(args) callback = function(args)
local token = args.data.token local token = args.data.token
@@ -694,28 +721,52 @@ Lua module: vim.lsp *lsp-core*
Fields: ~ Fields: ~
• {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) • {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient`)
See `cmd` in |vim.lsp.ClientConfig|. See `cmd` in |vim.lsp.ClientConfig|. See also
`reuse_client` to dynamically decide (per-buffer)
when `cmd` should be re-invoked.
• {filetypes}? (`string[]`) Filetypes the client will attach to, if • {filetypes}? (`string[]`) Filetypes the client will attach to, if
activated by `vim.lsp.enable()`. If not provided, activated by `vim.lsp.enable()`. If not provided, the
then the client will attach to all filetypes. client will attach to all filetypes.
• {root_markers}? (`string[]`) Directory markers (.e.g. '.git/') where
the LSP server will base its workspaceFolders,
rootUri, and rootPath on initialization. Unused if
`root_dir` is provided.
• {root_dir}? (`string|fun(bufnr: integer, cb:fun(root_dir?:string))`)
Directory where the LSP server will base its
workspaceFolders, rootUri, and rootPath on
initialization. If a function, it is passed the
buffer number and a callback argument which must be
called with the value of root_dir to use. The LSP
server will not be started until the callback is
called.
• {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) • {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
Predicate used to decide if a client should be Predicate which decides if a client should be
re-used. Used on all running clients. The default re-used. Used on all running clients. The default
implementation re-uses a client if name and root_dir implementation re-uses a client if name and root_dir
matches. matches.
• {root_dir}? (`string|fun(bufnr: integer, on_dir:fun(root_dir?:string))`)
*lsp-root_dir()* Decides the workspace root: the
directory where the LSP server will base its
workspaceFolders, rootUri, and rootPath on
initialization. The function form must call the
`on_dir` callback to provide the root dir, or LSP
will not be activated for the buffer. Thus a
`root_dir()` function can dynamically decide
per-buffer whether to activate (or skip) LSP. See
example at |vim.lsp.enable()|.
• {root_markers}? (`(string|string[])[]`) *lsp-root_markers*
Filename(s) (".git/", "package.json", …) used to
decide the workspace root. Unused if `root_dir` is
defined. The list order decides priority. To indicate
"equal priority", specify names in a nested list
`{ { 'a.txt', 'b.lua' }, ... }`.
For each item, Nvim will search upwards (from the
buffer file) for that marker, or list of markers;
search stops at the first directory containing that
marker, and the directory is used as the root dir
(workspace folder).
Example: Find the first ancestor directory containing
file or directory "stylua.toml"; if not found, find
the first ancestor containing ".git": >lua
root_markers = { 'stylua.toml', '.git' }
<
Example: Find the first ancestor directory containing
EITHER "stylua.toml" or ".luarc.json"; if not found,
find the first ancestor containing ".git": >lua
root_markers = { { 'stylua.toml', '.luarc.json' }, '.git' }
<
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
@@ -871,19 +922,36 @@ config({name}, {cfg}) *vim.lsp.config()*
• {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|. • {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|.
enable({name}, {enable}) *vim.lsp.enable()* enable({name}, {enable}) *vim.lsp.enable()*
Enable an LSP server to automatically start when opening a buffer. Auto-starts LSP when a buffer is opened, based on the |lsp-config|
`filetypes`, `root_markers`, and `root_dir` fields.
Uses configuration defined with `vim.lsp.config`.
Examples: >lua Examples: >lua
vim.lsp.enable('clangd') vim.lsp.enable('clangd')
vim.lsp.enable({'luals', 'pyright'})
<
vim.lsp.enable({'luals', 'pyright'}) Example: *lsp-restart* Passing `false` stops and detaches the client(s).
Thus you can "restart" LSP by disabling and re-enabling a given config: >lua
vim.lsp.enable('clangd', false)
vim.lsp.enable('clangd', true)
<
Example: To dynamically decide whether LSP is activated, define a
|lsp-root_dir()| function which calls `on_dir()` only when you want that
config to activate: >lua
vim.lsp.config('lua_ls', {
root_dir = function(bufnr, on_dir)
if not vim.fn.bufname(bufnr):match('%.txt$') then
on_dir(vim.fn.getcwd())
end
end
})
< <
Parameters: ~ Parameters: ~
• {name} (`string|string[]`) Name(s) of client(s) to enable. • {name} (`string|string[]`) Name(s) of client(s) to enable.
• {enable} (`boolean?`) `true|nil` to enable, `false` to disable. • {enable} (`boolean?`) `true|nil` to enable, `false` to disable
(actively stops and detaches clients as needed)
foldclose({kind}, {winid}) *vim.lsp.foldclose()* foldclose({kind}, {winid}) *vim.lsp.foldclose()*
Close all {kind} of folds in the the window with {winid}. Close all {kind} of folds in the the window with {winid}.
@@ -993,6 +1061,18 @@ get_log_path() *vim.lsp.get_log_path()*
Return: ~ Return: ~
(`string`) path to log file (`string`) path to log file
is_enabled({name}) *vim.lsp.is_enabled()*
Checks if the given LSP config is enabled (globally, not per-buffer).
Unlike `vim.lsp.config['…']`, this does not have the side-effect of
resolving the config.
Parameters: ~
• {name} (`string`) Config name
Return: ~
(`boolean`)
omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
Implements 'omnifunc' compatible LSP completion. Implements 'omnifunc' compatible LSP completion.
@@ -1128,65 +1208,17 @@ Lua module: vim.lsp.client *lsp-client*
*vim.lsp.Client* *vim.lsp.Client*
Fields: ~ Fields: ~
• {id} (`integer`) The id allocated to the client.
• {name} (`string`) If a name is specified on creation,
that will be used. Otherwise it is just the
client id. This is used for logs and messages.
• {rpc} (`vim.lsp.rpc.PublicClient`) RPC client
object, for low level interaction with the
client. See |vim.lsp.rpc.start()|.
• {offset_encoding} (`string`) Called "position encoding" in LSP
spec, the encoding used for communicating with
the server. You can modify this in the
`config`'s `on_init` method before text is
sent to the server.
• {handlers} (`table<string,lsp.Handler>`) The handlers
used by the client as described in
|lsp-handler|.
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
The current pending requests in flight to the
server. Entries are key-value pairs with the
key being the request id while the value is a
table with `type`, `bufnr`, and `method`
key-value pairs. `type` is either "pending"
for an active request, or "cancel" for a
cancel request. It will be "complete"
ephemerally while executing |LspRequest|
autocmds when replies are received from the
server.
• {config} (`vim.lsp.ClientConfig`) copy of the table
that was passed by the user to
|vim.lsp.start()|. See |vim.lsp.ClientConfig|.
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
server sent on `initialize` describing the
server's capabilities.
• {server_info} (`lsp.ServerInfo?`) Response from the server
sent on `initialize` describing information
about the server.
• {progress} (`vim.lsp.Client.Progress`) A ring buffer
(|vim.ringbuf()|) containing progress messages
sent by the server. See
|vim.lsp.Client.Progress|.
• {initialized} (`true?`)
• {workspace_folders} (`lsp.WorkspaceFolder[]?`) The workspace
folders configured in the client when the
server starts. This property is only available
if the client supports workspace folders. It
can be `null` if the client supports workspace
folders but none are configured.
• {root_dir} (`string?`)
• {attached_buffers} (`table<integer,true>`) • {attached_buffers} (`table<integer,true>`)
• {capabilities} (`lsp.ClientCapabilities`) Capabilities
provided by the client (editor or tool), at
startup.
• {commands} (`table<string,fun(command: lsp.Command, ctx: table)>`) • {commands} (`table<string,fun(command: lsp.Command, ctx: table)>`)
Table of command name to function which is Client commands. See |vim.lsp.ClientConfig|.
called if any LSP action (code action, code • {config} (`vim.lsp.ClientConfig`) Copy of the config
lenses, ...) triggers the command. Client passed to |vim.lsp.start()|. See
commands take precedence over the global |vim.lsp.ClientConfig|.
command registry. • {dynamic_capabilities} (`lsp.DynamicCapabilities`) Capabilities
• {settings} (`lsp.LSPObject`) Map with language server provided at runtime (after startup).
specific settings. These are returned to the
language server if requested via
`workspace/configuration`. Keys are
case-sensitive.
• {flags} (`table`) A table with flags for the client. • {flags} (`table`) A table with flags for the client.
The current (experimental) flags are: The current (experimental) flags are:
• {allow_incremental_sync}? (`boolean`, • {allow_incremental_sync}? (`boolean`,
@@ -1203,9 +1235,41 @@ Lua module: vim.lsp.client *lsp-client*
false, nvim exits immediately after sending false, nvim exits immediately after sending
the "shutdown" request to the server. the "shutdown" request to the server.
• {get_language_id} (`fun(bufnr: integer, filetype: string): string`) • {get_language_id} (`fun(bufnr: integer, filetype: string): string`)
• {capabilities} (`lsp.ClientCapabilities`) The capabilities See |vim.lsp.ClientConfig|.
provided by the client (editor or tool) • {handlers} (`table<string,lsp.Handler>`) See
• {dynamic_capabilities} (`lsp.DynamicCapabilities`) |vim.lsp.ClientConfig|.
• {id} (`integer`) The id allocated to the client.
• {initialized} (`true?`)
• {name} (`string`) See |vim.lsp.ClientConfig|.
• {offset_encoding} (`string`) See |vim.lsp.ClientConfig|.
• {progress} (`vim.lsp.Client.Progress`) A ring buffer
(|vim.ringbuf()|) containing progress messages
sent by the server. See
|vim.lsp.Client.Progress|.
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
The current pending requests in flight to the
server. Entries are key-value pairs with the
key being the request id while the value is a
table with `type`, `bufnr`, and `method`
key-value pairs. `type` is either "pending"
for an active request, or "cancel" for a
cancel request. It will be "complete"
ephemerally while executing |LspRequest|
autocmds when replies are received from the
server.
• {root_dir} (`string?`) See |vim.lsp.ClientConfig|.
• {rpc} (`vim.lsp.rpc.PublicClient`) RPC client
object, for low level interaction with the
client. See |vim.lsp.rpc.start()|.
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
server sent on `initialize` describing the
server's capabilities.
• {server_info} (`lsp.ServerInfo?`) Response from the server
sent on `initialize` describing server
information (e.g. version).
• {settings} (`lsp.LSPObject`) See |vim.lsp.ClientConfig|.
• {workspace_folders} (`lsp.WorkspaceFolder[]?`) See
|vim.lsp.ClientConfig|.
• {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) • {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`)
See |Client:request()|. See |Client:request()|.
• {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`) • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`)
@@ -1235,125 +1299,138 @@ Lua module: vim.lsp.client *lsp-client*
*vim.lsp.ClientConfig* *vim.lsp.ClientConfig*
Fields: ~ Fields: ~
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) • {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`)
command string[] that launches the language Callback invoked before the LSP "initialize"
server (treated as in |jobstart()|, must be phase, where `params` contains the parameters
absolute or on `$PATH`, shell constructs like being sent to the server and `config` is the
"~" are not expanded), or function that creates config that was passed to |vim.lsp.start()|.
an RPC client. Function receives a `dispatchers` You can use this to modify parameters before
table and returns a table with member functions they are sent.
`request`, `notify`, `is_closing` and • {capabilities}? (`lsp.ClientCapabilities`) Map overriding the
`terminate`. See |vim.lsp.rpc.request()|, default capabilities defined by
|vim.lsp.rpc.notify()|. For TCP there is a |vim.lsp.protocol.make_client_capabilities()|,
builtin RPC client factory: passed to the language server on
|vim.lsp.rpc.connect()| initialization. Hint: use
• {cmd_cwd}? (`string`, default: cwd) Directory to launch the make_client_capabilities() and modify its
`cmd` process. Not related to `root_dir`. result.
• {cmd_env}? (`table`) Environment flags to pass to the LSP • Note: To send an empty dictionary use
on spawn. Must be specified using a table. |vim.empty_dict()|, else it will be encoded
Non-string values are coerced to string. as an array.
Example: >lua • {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient`)
{ PORT = 8080; HOST = "0.0.0.0"; } Command `string[]` that launches the language
server (treated as in |jobstart()|, must be
absolute or on `$PATH`, shell constructs like
"~" are not expanded), or function that creates
an RPC client. Function receives a
`dispatchers` table and the resolved `config`,
and must return a table with member functions
`request`, `notify`, `is_closing` and
`terminate`. See |vim.lsp.rpc.request()|,
|vim.lsp.rpc.notify()|. For TCP there is a
builtin RPC client factory:
|vim.lsp.rpc.connect()|
• {cmd_cwd}? (`string`, default: cwd) Directory to launch
the `cmd` process. Not related to `root_dir`.
• {cmd_env}? (`table`) Environment variables passed to the
LSP process on spawn. Non-string values are
coerced to string. Example: >lua
{ PORT = 8080; HOST = '0.0.0.0'; }
< <
• {detached}? (`boolean`, default: true) Daemonize the server • {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
process so that it runs in a separate process Client commands. Map of command names to
group from Nvim. Nvim will shutdown the process user-defined functions. Commands passed to
on exit, but if Nvim fails to exit cleanly this `start()` take precedence over the global
could leave behind orphaned server processes. command registry. Each key must be a unique
• {workspace_folders}? (`lsp.WorkspaceFolder[]`) List of workspace command name, and the value is a function which
folders passed to the language server. For is called if any LSP action (code action, code
backwards compatibility rootUri and rootPath lenses, …) triggers the command.
will be derived from the first workspace folder • {detached}? (`boolean`, default: `true`) Daemonize the
in this list. See `workspaceFolders` in the LSP server process so that it runs in a separate
spec. process group from Nvim. Nvim will shutdown the
• {capabilities}? (`lsp.ClientCapabilities`) Map overriding the process on exit, but if Nvim fails to exit
default capabilities defined by cleanly this could leave behind orphaned server
|vim.lsp.protocol.make_client_capabilities()|, processes.
passed to the language server on initialization. • {flags}? (`table`) A table with flags for the client.
Hint: use make_client_capabilities() and modify The current (experimental) flags are:
its result. • {allow_incremental_sync}? (`boolean`,
• Note: To send an empty dictionary use default: `true`) Allow using incremental sync
|vim.empty_dict()|, else it will be encoded as for buffer edits
an array. • {debounce_text_changes} (`integer`, default:
• {handlers}? (`table<string,function>`) Map of language `150`) Debounce `didChange` notifications to
server method names to |lsp-handler| the server by the given number in
• {settings}? (`lsp.LSPObject`) Map with language server milliseconds. No debounce occurs if `nil`.
specific settings. See the {settings} in • {exit_timeout} (`integer|false`, default:
|vim.lsp.Client|. `false`) Milliseconds to wait for server to
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`) exit cleanly after sending the "shutdown"
Table that maps string of clientside commands to request before sending kill -15. If set to
user-defined functions. Commands passed to false, nvim exits immediately after sending
`start()` take precedence over the global the "shutdown" request to the server.
command registry. Each key must be a unique • {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
command name, and the value is a function which Language ID as string. Defaults to the buffer
is called if any LSP action (code action, code filetype.
lenses, ...) triggers the command. • {handlers}? (`table<string,function>`) Map of LSP method
• {init_options}? (`lsp.LSPObject`) Values to pass in the names to |lsp-handler|s.
initialization request as • {init_options}? (`lsp.LSPObject`) Values to pass in the
`initializationOptions`. See `initialize` in the initialization request as
LSP spec. `initializationOptions`. See `initialize` in
• {name}? (`string`, default: client-id) Name in log the LSP spec.
messages. • {name}? (`string`, default: client-id) Name in logs and
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`) user messages.
Language ID as string. Defaults to the buffer • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position
filetype. encoding" in LSP spec. The encoding that the
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position LSP server expects, used for communication. Not
encoding" in LSP spec, the encoding that the LSP validated. Can be modified in `on_init` before
server expects. Client does not verify this is text is sent to the server.
correct. • {on_attach}? (`elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>`)
• {on_error}? (`fun(code: integer, err: string)`) Callback Callback invoked when client attaches to a
invoked when the client operation throws an buffer.
error. `code` is a number describing the error. • {on_error}? (`fun(code: integer, err: string)`) Callback
Other arguments may be passed depending on the invoked when the client operation throws an
error kind. See `vim.lsp.rpc.client_errors` for error. `code` is a number describing the error.
possible errors. Use Other arguments may be passed depending on the
`vim.lsp.rpc.client_errors[code]` to get error kind. See `vim.lsp.rpc.client_errors` for
human-friendly name. possible errors. Use
• {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`) `vim.lsp.rpc.client_errors[code]` to get
Callback invoked before the LSP "initialize" human-friendly name.
phase, where `params` contains the parameters • {on_exit}? (`elem_or_list<fun(code: integer, signal: integer, client_id: integer)>`)
being sent to the server and `config` is the Callback invoked on client exit.
config that was passed to |vim.lsp.start()|. You • code: exit code of the process
can use this to modify parameters before they • signal: number describing the signal used to
are sent. terminate (if any)
• {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>`) • client_id: client handle
Callback invoked after LSP "initialize", where • {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>`)
`result` is a table of `capabilities` and Callback invoked after LSP "initialize", where
anything else the server may send. For example, `result` is a table of `capabilities` and
clangd sends `init_result.offsetEncoding` if anything else the server may send. For example,
`capabilities.offsetEncoding` was sent to it. clangd sends `init_result.offsetEncoding` if
You can only modify the `client.offset_encoding` `capabilities.offsetEncoding` was sent to it.
here before any notifications are sent. You can only modify the
• {on_exit}? (`elem_or_list<fun(code: integer, signal: integer, client_id: integer)>`) `client.offset_encoding` here before any
Callback invoked on client exit. notifications are sent.
• code: exit code of the process • {root_dir}? (`string`) Directory where the LSP server will
• signal: number describing the signal used to base its workspaceFolders, rootUri, and
terminate (if any) rootPath on initialization.
• client_id: client handle • {settings}? (`lsp.LSPObject`) Map of language
• {on_attach}? (`elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>`) server-specific settings, decided by the
Callback invoked when client attaches to a client. Sent to the LS if requested via
buffer. `workspace/configuration`. Keys are
• {trace}? (`'off'|'messages'|'verbose'`, default: "off") case-sensitive.
Passed directly to the language server in the • {trace}? (`'off'|'messages'|'verbose'`, default: "off")
initialize request. Invalid/empty values will Passed directly to the language server in the
• {flags}? (`table`) A table with flags for the client. The initialize request. Invalid/empty values will
current (experimental) flags are: • {workspace_folders}? (`lsp.WorkspaceFolder[]`) List of workspace
• {allow_incremental_sync}? (`boolean`, default: folders passed to the language server. For
`true`) Allow using incremental sync for backwards compatibility rootUri and rootPath
buffer edits are derived from the first workspace folder in
• {debounce_text_changes} (`integer`, default: this list. Can be `null` if the client supports
`150`) Debounce `didChange` notifications to workspace folders but none are configured. See
the server by the given number in `workspaceFolders` in LSP spec.
milliseconds. No debounce occurs if `nil`. • {workspace_required}? (`boolean`, default: `false`) Server requires a
• {exit_timeout} (`integer|false`, default: workspace (no "single file" support). Note:
`false`) Milliseconds to wait for server to Without a workspace, cross-file features
exit cleanly after sending the "shutdown" (navigation, hover) may or may not work
request before sending kill -15. If set to depending on the language server, even if the
false, nvim exits immediately after sending server doesn't require a workspace.
the "shutdown" request to the server.
• {root_dir}? (`string`) Directory where the LSP server will
base its workspaceFolders, rootUri, and rootPath
on initialization.
Client:cancel_request({id}) *Client:cancel_request()* Client:cancel_request({id}) *Client:cancel_request()*
@@ -1539,7 +1616,8 @@ clear_references() *vim.lsp.buf.clear_references()*
Removes document highlights from current buffer. Removes document highlights from current buffer.
code_action({opts}) *vim.lsp.buf.code_action()* code_action({opts}) *vim.lsp.buf.code_action()*
Selects a code action available at the current cursor position. Selects a code action (LSP: "textDocument/codeAction" request) available
at cursor position.
Parameters: ~ Parameters: ~
• {opts} (`table?`) A table with the following fields: • {opts} (`table?`) A table with the following fields:
@@ -2502,6 +2580,27 @@ symbols_to_items({symbols}, {bufnr}, {position_encoding})
============================================================================== ==============================================================================
Lua module: vim.lsp.log *lsp-log* Lua module: vim.lsp.log *lsp-log*
The `vim.lsp.log` module provides logging for the Nvim LSP client.
When debugging language servers, it is helpful to enable extra-verbose logging
of the LSP client RPC events. Example: >lua
vim.lsp.set_log_level 'trace'
require('vim.lsp.log').set_format_func(vim.inspect)
<
Then try to run the language server, and open the log with: >vim
:lua vim.cmd('tabnew ' .. vim.lsp.get_log_path())
<
(Or use `:LspLog` if you have nvim-lspconfig installed.)
Note:
• Remember to DISABLE verbose logging ("debug" or "trace" level), else you may
encounter performance issues.
• "ERROR" messages containing "stderr" only indicate that the log was sent to
stderr. Many servers send harmless messages via stderr.
get_filename() *vim.lsp.log.get_filename()* get_filename() *vim.lsp.log.get_filename()*
Returns the log filename. Returns the log filename.

View File

@@ -10,19 +10,18 @@
============================================================================== ==============================================================================
Introduction *lua-guide* Introduction *lua-guide*
This guide will go through the basics of using Lua in Nvim. It is not meant This guide introduces the basics of everyday usage of Lua to configure and
to be a comprehensive encyclopedia of all available features, nor will it operate Nvim. It assumes some familiarity with the (non-Lua) basics of Nvim
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 (commands, options, mappings, autocommands), which are covered in the
|user-manual|. |user-manual|.
This is not a comprehensive encyclopedia of all available features. Think of
it as a survival kit: the bare minimum needed to comfortably get started on
using Lua in Nvim.
See |lua-plugin| for guidance on developing Lua plugins.
See |luaref| and |lua-concepts| for details on the Lua programming language.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
Some words on the API *lua-guide-api* Some words on the API *lua-guide-api*
@@ -93,10 +92,9 @@ Finally, you can include Lua code in a Vimscript file by putting it inside a
Using Lua files on startup *lua-guide-config* Using Lua files on startup *lua-guide-config*
Nvim supports using `init.vim` or `init.lua` as the configuration file, but Nvim supports using `init.vim` or `init.lua` as the configuration file, but
not both at the same time. This should be placed in your |config| directory, not both at the same time. This should be placed in your |config| directory
which is typically `~/.config/nvim` for Linux, BSD, or macOS, and (run `:echo stdpath('config')` to see where it is). Note that you can also use
`~/AppData/Local/nvim/` for Windows. Note that you can use Lua in `init.vim` Lua in `init.vim` and Vimscript in `init.lua`, which will be covered below.
and Vimscript in `init.lua`, which will be covered below.
If you'd like to run any other Lua script on |startup| automatically, then you If you'd like to run any other Lua script on |startup| automatically, then you
can simply put it in `plugin/` in your |'runtimepath'|. can simply put it in `plugin/` in your |'runtimepath'|.

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

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

View File

@@ -171,7 +171,7 @@ added. But visually, this small bit of sugar gets reasonably close to a
*lua-regex* *lua-regex*
Lua intentionally does not support regular expressions, instead it has limited Lua intentionally does not support regular expressions, instead it has limited
|lua-patterns| which avoid the performance pitfalls of extended regex. Lua |lua-pattern|s which avoid the performance pitfalls of extended regex. Lua
scripts can also use Vim regex via |vim.regex()|. scripts can also use Vim regex via |vim.regex()|.
Examples: >lua Examples: >lua
@@ -188,15 +188,19 @@ Examples: >lua
============================================================================== ==============================================================================
IMPORTING LUA MODULES *lua-module-load* IMPORTING LUA MODULES *lua-module-load*
Modules are searched for under the directories specified in 'runtimepath', in Modules are searched for under the directories specified in 'runtimepath' and
the order they appear. Any "." in the module name is treated as a directory |packages-runtimepath|, in the order they appear in the output of this command
separator when searching. For a module `foo.bar`, each directory is searched >vim
for `lua/foo/bar.lua`, then `lua/foo/bar/init.lua`. If no files are found, :echo nvim_list_runtime_paths()
the directories are searched again for a shared library with a name matching <
`lua/foo/bar.?`, where `?` is a list of suffixes (such as `so` or `dll`) derived from Any "." in the module name is treated as a directory separator when searching.
the initial value of |package.cpath|. If still no files are found, Nvim falls For a module `foo.bar`, each directory is searched for `lua/foo/bar.lua`, then
back to Lua's default search mechanism. The first script found is run and `lua/foo/bar/init.lua`. If no files are found, the directories are searched
`require()` returns the value returned by the script if any, else `true`. again for a shared library with a name matching `lua/foo/bar.?`, where `?` is
a list of suffixes (such as `so` or `dll`) derived from the initial value of
|package.cpath|. If still no files are found, Nvim falls back to Lua's default
search mechanism. The first script found is run and `require()` returns the
value returned by the script if any, else `true`.
The return value is cached after the first call to `require()` for each module, The return value is cached after the first call to `require()` for each module,
with subsequent calls returning the cached value without searching for, or with subsequent calls returning the cached value without searching for, or
@@ -214,56 +218,9 @@ and loads the first module found ("first wins"): >
bar/lua/mod.so bar/lua/mod.so
bar/lua/mod.dll bar/lua/mod.dll
< <
*lua-package-path*
Nvim automatically adjusts |package.path| and |package.cpath| according to the
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the
first character of `package.config`).
Similarly to |package.path|, modified directories from 'runtimepath' are also
added to |package.cpath|. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing |package.cpath| are used. Example:
- 1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial |package.cpath| (defined at compile-time or derived from
`$LUA_CPATH` / `$LUA_INIT`) contains `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
- 2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
order: parts of the path starting from the first path component containing
question mark and preceding path separator.
- 3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
as the suffix of the first path from |package.path| (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
- 4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
second one contains a semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
- 5. The cartesian product of paths from 4. and suffixes from 3. is taken,
giving four variants. In each variant a `/lua` path segment is inserted
between path and suffix, leaving:
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
- 6. New paths are prepended to the original |package.cpath|.
The result will look like this: >
/foo/bar,/xxx;yyy/baz,/abc ('runtimepath')
× ./?.so;/def/ghi/a?d/j/g.elf;/def/?.so (package.cpath)
= /foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so
Note: Note:
- To track 'runtimepath' updates, paths added at previous update are - Although 'runtimepath' is tracked, Nvim does not track current
remembered and removed at the next update, while all paths derived from the
new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering |package.path|/|package.cpath| content if 'runtimepath'
was reordered.
- Although adjustments happen automatically, Nvim does not track current
values of |package.path| or |package.cpath|. If you happen to delete some values of |package.path| or |package.cpath|. If you happen to delete some
paths from there you can set 'runtimepath' to trigger an update: >vim paths from there you can set 'runtimepath' to trigger an update: >vim
let &runtimepath = &runtimepath let &runtimepath = &runtimepath
@@ -683,6 +640,12 @@ vim.hl.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {opts})
• {timeout}? (`integer`, default: -1 no timeout) Time in ms • {timeout}? (`integer`, default: -1 no timeout) Time in ms
before highlight is cleared before highlight is cleared
Return (multiple): ~
(`uv.uv_timer_t?`) range_timer A timer which manages how much time the
highlight has left
(`fun()?`) range_clear A function which allows clearing the highlight
manually. nil is returned if timeout is not specified
============================================================================== ==============================================================================
VIM.DIFF *vim.diff* VIM.DIFF *vim.diff*
@@ -1503,20 +1466,21 @@ Lua module: vim *lua-vim*
vim.cmd({command}) *vim.cmd()* vim.cmd({command}) *vim.cmd()*
Executes Vimscript (|Ex-commands|). Executes Vimscript (|Ex-commands|).
Note that `vim.cmd` can be indexed with a command name to return a Can be indexed with a command name to get a function, thus you can write
callable function to the command. `vim.cmd.echo(…)` instead of `vim.cmd{cmd='echo',…}`.
Example: >lua Examples: >lua
-- Single command:
vim.cmd('echo 42') vim.cmd('echo 42')
-- Multiline script:
vim.cmd([[ vim.cmd([[
augroup My_group augroup my.group
autocmd! autocmd!
autocmd FileType c setlocal cindent autocmd FileType c setlocal cindent
augroup END augroup END
]]) ]])
-- Ex command :echo "foo" -- Ex command :echo "foo". Note: string literals must be double-quoted.
-- Note string literals need to be double quoted.
vim.cmd('echo "foo"') vim.cmd('echo "foo"')
vim.cmd { cmd = 'echo', args = { '"foo"' } } vim.cmd { cmd = 'echo', args = { '"foo"' } }
vim.cmd.echo({ args = { '"foo"' } }) vim.cmd.echo({ args = { '"foo"' } })
@@ -1524,22 +1488,20 @@ vim.cmd({command}) *vim.cmd()*
-- Ex command :write! myfile.txt -- Ex command :write! myfile.txt
vim.cmd('write! myfile.txt') vim.cmd('write! myfile.txt')
vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true } vim.cmd { cmd = 'write', args = { 'myfile.txt' }, bang = true }
vim.cmd.write { args = { "myfile.txt" }, bang = true } vim.cmd.write { args = { 'myfile.txt' }, bang = true }
vim.cmd.write { "myfile.txt", bang = true } vim.cmd.write { 'myfile.txt', bang = true }
-- Ex command :colorscheme blue -- Ex command :vertical resize +2
vim.cmd('colorscheme blue') vim.cmd.resize({ '+2', mods = { vertical = true } })
vim.cmd.colorscheme('blue')
< <
Parameters: ~ Parameters: ~
• {command} (`string|table`) Command(s) to execute. If a string, • {command} (`string|table`) Command(s) to execute.
executes multiple lines of Vimscript at once. In this case, • The string form supports multiline Vimscript (alias to
it is an alias to |nvim_exec2()|, where `opts.output` is |nvim_exec2()|, behaves like |:source|).
set to false. Thus it works identical to |:source|. If a • The table form executes a single command (alias to
table, executes a single command. In this case, it is an |nvim_cmd()|).
alias to |nvim_cmd()| where `opts` is empty.
See also: ~ See also: ~
• |ex-cmd-index| • |ex-cmd-index|
@@ -1816,7 +1778,9 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
the new process. Inherits the current environment with the new process. Inherits the current environment with
`NVIM` set to |v:servername|. `NVIM` set to |v:servername|.
• clear_env: (boolean) `env` defines the job environment • clear_env: (boolean) `env` defines the job environment
exactly, instead of merging current environment. exactly, instead of merging current environment. Note: if
`env` is `nil`, the current environment is used but
without `NVIM` set.
• stdin: (string|string[]|boolean) If `true`, then a pipe • stdin: (string|string[]|boolean) If `true`, then a pipe
to stdin is opened and can be written to via the to stdin is opened and can be written to via the
`write()` method to SystemObj. If string or string[] then `write()` method to SystemObj. If string or string[] then
@@ -2059,7 +2023,7 @@ vim.gsplit({s}, {sep}, {opts}) *vim.gsplit()*
See also: ~ See also: ~
• |string.gmatch()| • |string.gmatch()|
• |vim.split()| • |vim.split()|
• |lua-patterns| • |lua-pattern|s
• https://www.lua.org/pil/20.2.html • https://www.lua.org/pil/20.2.html
• http://lua-users.org/wiki/StringLibraryTutorial • http://lua-users.org/wiki/StringLibraryTutorial
@@ -2152,7 +2116,7 @@ vim.list_slice({list}, {start}, {finish}) *vim.list_slice()*
(`any[]`) Copy of table sliced from start to finish (inclusive) (`any[]`) Copy of table sliced from start to finish (inclusive)
vim.pesc({s}) *vim.pesc()* vim.pesc({s}) *vim.pesc()*
Escapes magic chars in |lua-patterns|. Escapes magic chars in |lua-pattern|s.
Parameters: ~ Parameters: ~
• {s} (`string`) String to escape • {s} (`string`) String to escape
@@ -2407,7 +2371,7 @@ vim.trim({s}) *vim.trim()*
(`string`) String with whitespace removed from its beginning and end (`string`) String with whitespace removed from its beginning and end
See also: ~ See also: ~
• |lua-patterns| • |lua-pattern|s
• https://www.lua.org/pil/20.2.html • https://www.lua.org/pil/20.2.html
*vim.validate()* *vim.validate()*
@@ -2707,7 +2671,7 @@ vim.filetype.add({filetypes}) *vim.filetype.add()*
Filetype mappings can be added either by extension or by filename (either Filetype mappings can be added either by extension or by filename (either
the "tail" or the full file path). The full file path is checked first, the "tail" or the full file path). The full file path is checked first,
followed by the file name. If a match is not found using the filename, followed by the file name. If a match is not found using the filename,
then the filename is matched against the list of |lua-patterns| (sorted by then the filename is matched against the list of |lua-pattern|s (sorted by
priority) until a match is found. Lastly, if pattern matching does not priority) until a match is found. Lastly, if pattern matching does not
find a filetype, then the file extension is used. find a filetype, then the file extension is used.
@@ -2935,7 +2899,7 @@ Use |uv.fs_stat()| to check a file's type, and whether it exists.
Example: >lua Example: >lua
if vim.uv.fs_stat(file) then if vim.uv.fs_stat(file) then
vim.print("file exists") vim.print('file exists')
end end
< <
@@ -2977,11 +2941,13 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
iterate over. The path is first normalized iterate over. The path is first normalized
|vim.fs.normalize()|. |vim.fs.normalize()|.
• {opts} (`table?`) Optional keyword arguments: • {opts} (`table?`) Optional keyword arguments:
• depth: integer|nil How deep the traverse (default 1) {depth}? (`integer`, default: `1`) How deep the traverse.
• skip: (fun(dir_name: string): boolean)|nil Predicate to {skip}? (`fun(dir_name: string): boolean`) Predicate to
control traversal. Return false to stop searching the control traversal. Return false to stop searching the
current directory. Only useful when depth > 1 current directory. Only useful when depth > 1 Return an
• follow: boolean|nil Follow symbolic links. (default: false) iterator over the items located in {path}
• {follow}? (`boolean`, default: `false`) Follow symbolic
links.
Return: ~ Return: ~
(`Iterator`) over items in {path}. Each iteration yields two values: (`Iterator`) over items in {path}. Each iteration yields two values:
@@ -2990,7 +2956,8 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
"fifo", "socket", "char", "block", "unknown". "fifo", "socket", "char", "block", "unknown".
vim.fs.dirname({file}) *vim.fs.dirname()* vim.fs.dirname({file}) *vim.fs.dirname()*
Return the parent directory of the given path Gets the parent directory of the given path (not expanded/resolved, the
caller must do that).
Attributes: ~ Attributes: ~
Since: 0.8.0 Since: 0.8.0
@@ -3015,16 +2982,17 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
narrow the search to find only that type. narrow the search to find only that type.
Examples: >lua Examples: >lua
-- list all test directories under the runtime directory -- List all test directories under the runtime directory.
local test_dirs = vim.fs.find( local dirs = vim.fs.find(
{'test', 'tst', 'testdir'}, { 'test', 'tst', 'testdir' },
{limit = math.huge, type = 'directory', path = './runtime/'} { limit = math.huge, type = 'directory', path = './runtime/' }
) )
-- get all files ending with .cpp or .hpp inside lib/ -- Get all "lib/*.cpp" and "lib/*.hpp" files, using Lua patterns.
local cpp_hpp = vim.fs.find(function(name, path) -- Or use `vim.glob.to_lpeg(…):match(…)` for glob/wildcard matching.
local files = vim.fs.find(function(name, path)
return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
end, {limit = math.huge, type = 'file'}) end, { limit = math.huge, type = 'file' })
< <
Attributes: ~ Attributes: ~
@@ -3041,7 +3009,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
The function should return `true` if the given item is The function should return `true` if the given item is
considered a match. considered a match.
• {opts} (`table`) Optional keyword arguments: • {opts} (`table?`) Optional keyword arguments:
• {path}? (`string`) Path to begin searching from. If • {path}? (`string`) Path to begin searching from. If
omitted, the |current-directory| is used. omitted, the |current-directory| is used.
• {upward}? (`boolean`, default: `false`) Search upward • {upward}? (`boolean`, default: `false`) Search upward
@@ -3124,19 +3092,20 @@ vim.fs.normalize({path}, {opts}) *vim.fs.normalize()*
(`string`) Normalized path (`string`) Normalized path
vim.fs.parents({start}) *vim.fs.parents()* vim.fs.parents({start}) *vim.fs.parents()*
Iterate over all the parents of the given path. Iterate over all the parents of the given path (not expanded/resolved, the
caller must do that).
Example: >lua Example: >lua
local root_dir local root_dir
for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
if vim.fn.isdirectory(dir .. "/.git") == 1 then if vim.fn.isdirectory(dir .. '/.git') == 1 then
root_dir = dir root_dir = dir
break break
end end
end end
if root_dir then if root_dir then
print("Found git repository at", root_dir) print('Found git repository at', root_dir)
end end
< <
@@ -3188,7 +3157,7 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
If the buffer is unnamed (has no backing file) or has a non-empty If the buffer is unnamed (has no backing file) or has a non-empty
'buftype' then the search begins from Nvim's |current-directory|. 'buftype' then the search begins from Nvim's |current-directory|.
Example: >lua Examples: >lua
-- Find the root of a Python project, starting from file 'main.py' -- Find the root of a Python project, starting from file 'main.py'
vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' }) vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' })
@@ -3199,6 +3168,10 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
vim.fs.root(0, function(name, path) vim.fs.root(0, function(name, path)
return name:match('%.csproj$') ~= nil return name:match('%.csproj$') ~= nil
end) end)
-- Find the first ancestor directory containing EITHER "stylua.toml" or ".luarc.json"; if
-- not found, find the first ancestor containing ".git":
vim.fs.root(0, { { 'stylua.toml', '.luarc.json' }, '.git' })
< <
Attributes: ~ Attributes: ~
@@ -3208,10 +3181,13 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
• {source} (`integer|string`) Buffer number (0 for current buffer) or • {source} (`integer|string`) Buffer number (0 for current buffer) or
file path (absolute or relative to the |current-directory|) file path (absolute or relative to the |current-directory|)
to begin the search from. to begin the search from.
• {marker} (`string|string[]|fun(name: string, path: string): boolean`) • {marker} (`(string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean`)
A marker, or list of markers, to search for. If a function, Filename, function, or list thereof, that decides how to
the function is called for each evaluated item and should find the root. To indicate "equal priority", specify items
return true if {name} and {path} are a match. in a nested list `{ { 'a.txt', 'b.lua' }, … }`. A function
item must return true if `name` and `path` are a match. Each
item (which may itself be a nested list) is evaluated
in-order against all ancestors, until a match is found.
Return: ~ Return: ~
(`string?`) Directory path containing one of the given markers, or nil (`string?`) Directory path containing one of the given markers, or nil
@@ -3774,19 +3750,25 @@ vim.regex({re}) *vim.regex()*
Lua module: vim.secure *vim.secure* Lua module: vim.secure *vim.secure*
vim.secure.read({path}) *vim.secure.read()* vim.secure.read({path}) *vim.secure.read()*
Attempt to read the file at {path} prompting the user if the file should If {path} is a file: attempt to read the file, prompting the user if the
be trusted. The user's choice is persisted in a trust database at file should be trusted.
If {path} is a directory: return true if the directory is trusted
(non-recursive), prompting the user as necessary.
The user's choice is persisted in a trust database at
$XDG_STATE_HOME/nvim/trust. $XDG_STATE_HOME/nvim/trust.
Attributes: ~ Attributes: ~
Since: 0.9.0 Since: 0.9.0
Parameters: ~ Parameters: ~
• {path} (`string`) Path to a file to read. • {path} (`string`) Path to a file or directory to read.
Return: ~ Return: ~
(`string?`) The contents of the given file if it exists and is (`boolean|string?`) If {path} is not trusted or does not exist,
trusted, or nil otherwise. returns `nil`. Otherwise, returns the contents of {path} if it is a
file, or true if {path} is a directory.
See also: ~ See also: ~
• |:trust| • |:trust|

View File

@@ -4150,7 +4150,7 @@ string.upper({s}) *string.upper()*
locale. locale.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
5.4.1 Patterns *lua-patterns* 5.4.1 Patterns *lua-pattern*
A character class is used to represent a set of characters. The following A character class is used to represent a set of characters. The following
combinations are allowed in describing a character class: combinations are allowed in describing a character class:
@@ -4207,6 +4207,7 @@ A pattern item may be
- a single character class followed by `+`, which matches 1 or more - a single character class followed by `+`, which matches 1 or more
repetitions of characters in the class. These repetition items will repetitions of characters in the class. These repetition items will
always match the longest possible sequence; always match the longest possible sequence;
*lua-nongreedy*
- a single character class followed by `-`, which also matches 0 or - a single character class followed by `-`, which also matches 0 or
more repetitions of characters in the class. Unlike `*`, these more repetitions of characters in the class. Unlike `*`, these
repetition items will always match the shortest possible sequence; repetition items will always match the shortest possible sequence;
@@ -4221,7 +4222,7 @@ A pattern item may be
`y` where the count reaches 0. For instance, the item `%b()` matches `y` where the count reaches 0. For instance, the item `%b()` matches
expressions with balanced parentheses. expressions with balanced parentheses.
PATTERN *lua-pattern* PATTERN
A pattern is a sequence of pattern items. A `^` at the beginning of a pattern A pattern is a sequence of pattern items. A `^` at the beginning of a pattern
anchors the match at the beginning of the subject string. A `$` at the end of anchors the match at the beginning of the subject string. A `$` at the end of

View File

@@ -19,12 +19,7 @@ For changing the language of messages and menus see |mlang.txt|.
============================================================================== ==============================================================================
Getting started *mbyte-first* Getting started *mbyte-first*
This is a summary of the multibyte features in Vim. If you are lucky it works This is a summary of the multibyte features in Nvim.
as described and you can start using Vim without much trouble. If something
doesn't work you will have to read the rest. Don't be surprised if it takes
quite a bit of work and experimenting to make Vim use all the multibyte
features. Unfortunately, every system has its own way to deal with multibyte
languages and it is quite complicated.
LOCALE LOCALE
@@ -54,14 +49,14 @@ See |mbyte-locale| for details.
ENCODING ENCODING
Nvim always uses UTF-8 internally. Thus 'encoding' option is always set Nvim always uses UTF-8 internally. Thus 'encoding' is always set to "utf-8"
to "utf-8" and cannot be changed. and cannot be changed.
All the text that is used inside Vim will be in UTF-8. Not only the text in All the text that is used inside Vim will be in UTF-8. Not only the text in
the buffers, but also in registers, variables, etc. the buffers, but also in registers, variables, etc.
You can edit files in different encodings than UTF-8. Nvim You can edit files in different encodings than UTF-8. Nvim will convert the
will convert the file when you read it and convert it back when you write it. file when you read it and convert it back when you write it.
See 'fileencoding', 'fileencodings' and |++enc|. See 'fileencoding', 'fileencodings' and |++enc|.
@@ -184,9 +179,9 @@ You could make a small shell script for this.
============================================================================== ==============================================================================
Encoding *mbyte-encoding* Encoding *mbyte-encoding*
In Nvim UTF-8 is always used internally to encode characters. UTF-8 is always used internally to encode characters. This applies to all the
This applies to all the places where text is used, including buffers (files places where text is used, including buffers (files loaded into memory),
loaded into memory), registers and variables. registers and variables.
*charset* *codeset* *charset* *codeset*
Charset is another name for encoding. There are subtle differences, but these Charset is another name for encoding. There are subtle differences, but these
@@ -609,25 +604,25 @@ Combining forms:
Using UTF-8 *mbyte-utf8* *UTF-8* *utf-8* *utf8* Using UTF-8 *mbyte-utf8* *UTF-8* *utf-8* *utf8*
*Unicode* *unicode* *Unicode* *unicode*
The Unicode character set was designed to include all characters from other The Unicode character set was designed to include all characters from other
character sets. Therefore it is possible to write text in any language using character sets. Therefore it is possible to write text in (almost) any
Unicode (with a few rarely used languages excluded). And it's mostly possible language using Unicode. And it's mostly possible to mix these languages in
to mix these languages in one file, which is impossible with other encodings. one file, which is impossible with other encodings.
Unicode can be encoded in several ways. The most popular one is UTF-8, which Unicode can be encoded in several ways. The most popular one is UTF-8, which
uses one or more bytes for each character and is backwards compatible with uses one or more bytes for each character and is backwards compatible with
ASCII. On MS-Windows UTF-16 is also used (previously UCS-2), which uses ASCII. On MS-Windows UTF-16 is also used (previously UCS-2), which uses
16-bit words. Vim can support all of these encodings, but always uses UTF-8 16-bit words. Nvim supports all of these encodings, but always uses UTF-8
internally. internally.
Vim has comprehensive UTF-8 support. It works well in: Nvim supports double-width characters; works best with 'guifontwide'. When
- xterm with UTF-8 support enabled
- MS-Windows GUI
- several other platforms
Double-width characters are supported. Works best with 'guifontwide'. When
using only 'guifont' the wide characters are drawn in the normal width and using only 'guifont' the wide characters are drawn in the normal width and
a space to fill the gap. a space to fill the gap.
EMOJI *emoji*
You can list emoji characters using this script: >vim
:source $VIMRUNTIME/scripts/emoji_list.lua
<
*bom-bytes* *bom-bytes*
When reading a file a BOM (Byte Order Mark) can be used to recognize the When reading a file a BOM (Byte Order Mark) can be used to recognize the
Unicode encoding: Unicode encoding:
@@ -698,7 +693,7 @@ You need to specify a font to be used. For double-wide characters another
font is required, which is exactly twice as wide. There are three ways to do font is required, which is exactly twice as wide. There are three ways to do
this: this:
1. Set 'guifont' and let Vim find a matching 'guifontwide' 1. Set 'guifont' and let Nvim find a matching 'guifontwide'
2. Set 'guifont' and 'guifontwide' 2. Set 'guifont' and 'guifontwide'
See the documentation for each option for details. Example: > See the documentation for each option for details. Example: >
@@ -730,7 +725,7 @@ COMMAND ARGUMENTS *utf-8-char-arg*
Commands like |f|, |F|, |t| and |r| take an argument of one character. For Commands like |f|, |F|, |t| and |r| take an argument of one character. For
UTF-8 this argument may include one or two composing characters. These need UTF-8 this argument may include one or two composing characters. These need
to be produced together with the base character, Vim doesn't wait for the next to be produced together with the base character, Nvim doesn't wait for the next
character to be typed to find out if it is a composing character or not. character to be typed to find out if it is a composing character or not.
Using 'keymap' or |:lmap| is a nice way to type these characters. Using 'keymap' or |:lmap| is a nice way to type these characters.
@@ -741,7 +736,6 @@ searching for a character with a composing character, this will only find
matches with that composing character. It was implemented this way, because matches with that composing character. It was implemented this way, because
not everybody is able to type a composing character. not everybody is able to type a composing character.
============================================================================== ==============================================================================
Overview of options *mbyte-options* Overview of options *mbyte-options*

View File

@@ -119,10 +119,6 @@ OPTIONS
• Setting |hidden-options| now gives an error. In particular, setting • Setting |hidden-options| now gives an error. In particular, setting
'noshellslash' is now only allowed on Windows. 'noshellslash' is now only allowed on Windows.
PLUGINS
• TODO
TREESITTER TREESITTER
• |Query:iter_matches()| correctly returns all matching nodes in a match • |Query:iter_matches()| correctly returns all matching nodes in a match
@@ -136,12 +132,11 @@ TREESITTER
if no languages are explicitly registered. if no languages are explicitly registered.
• |vim.treesitter.language.add()| returns `true` if a parser was loaded • |vim.treesitter.language.add()| returns `true` if a parser was loaded
successfully and `nil,errmsg` otherwise instead of throwing an error. successfully and `nil,errmsg` otherwise instead of throwing an error.
• |vim.treesitter.get_parser()| and |vim.treesitter.start()| no longer parse • |vim.treesitter.get_parser()| and |vim.treesitter.start()| no longer parse the
the tree before returning. Scripts must call |LanguageTree:parse()| explicitly. >lua tree before returning. Scripts must call |LanguageTree:parse()| explicitly. >lua
local p = vim.treesitter.get_parser(0, 'c') local p = vim.treesitter.get_parser(0, 'c')
p:parse() p:parse()
• |vim.treesitter.get_parser()| expects its buffer to be loaded. • |vim.treesitter.get_parser()| expects its buffer to be loaded.
<
TUI TUI
@@ -176,7 +171,11 @@ API
aligned text that truncates before covering up buffer text. aligned text that truncates before covering up buffer text.
• `virt_lines_overflow` field accepts value `scroll` to enable horizontal • `virt_lines_overflow` field accepts value `scroll` to enable horizontal
scrolling for virtual lines with 'nowrap'. scrolling for virtual lines with 'nowrap'.
• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight • |vim.secure.read()| now returns `true` for trusted directories. Previously
it would return `nil`, which made it impossible to tell if the directory was
actually trusted.
• Added |vim.lsp.is_enabled()| to check if a given LSP config has been enabled
by |vim.lsp.enable()|.
DEFAULTS DEFAULTS
@@ -189,6 +188,7 @@ DEFAULTS
• |gri| in Normal mode maps to |vim.lsp.buf.implementation()| • |gri| in Normal mode maps to |vim.lsp.buf.implementation()|
• |gO| in Normal mode maps to |vim.lsp.buf.document_symbol()| • |gO| in Normal mode maps to |vim.lsp.buf.document_symbol()|
• |gra| in Normal and Visual mode maps to |vim.lsp.buf.code_action()| • |gra| in Normal and Visual mode maps to |vim.lsp.buf.code_action()|
• |grt| in Normal mode maps to |vim.lsp.buf.type_definition()|
• CTRL-S in Insert and Select mode maps to |vim.lsp.buf.signature_help()| • CTRL-S in Insert and Select mode maps to |vim.lsp.buf.signature_help()|
• Mouse |popup-menu| includes an "Open in web browser" item when you right-click • Mouse |popup-menu| includes an "Open in web browser" item when you right-click
on a URL. on a URL.
@@ -242,8 +242,7 @@ EDITOR
• |hl-ComplMatchIns| shows matched text of the currently inserted completion. • |hl-ComplMatchIns| shows matched text of the currently inserted completion.
• |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup. • |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup.
• |gO| now works in `help`, `checkhealth`, and `markdown` buffers. • |gO| now works in `help`, `checkhealth`, and `markdown` buffers.
• Jump between sections in `help` and `checkhealth` buffers with `[[` and • Jump between sections in `help` and `checkhealth` buffers with `[[` and `]]`.
`]]`.
EVENTS EVENTS
@@ -283,12 +282,16 @@ LSP
`codeAction/resolve` request. `codeAction/resolve` request.
• The `textDocument/completion` request now includes the completion context in • The `textDocument/completion` request now includes the completion context in
its parameters. its parameters.
• |vim.lsp.Config| gained `workspace_required`.
• `root_markers` in |vim.lsp.Config| can now be ordered by priority.
• The function form of `cmd` in a vim.lsp.Config or vim.lsp.ClientConfig
receives the resolved config as the second arg: `cmd(dispatchers, config)`.
LUA LUA
• Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`, • Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`,
`vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`, `vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`,
and `vim.fn`. `vim.env` and `vim.fn`.
• Documentation for |lua-bit|. • Documentation for |lua-bit|.
• |gf| in Lua buffers can go to module in same repo, |runtime-search-path| and • |gf| in Lua buffers can go to module in same repo, |runtime-search-path| and
|package.path|. |package.path|.
@@ -302,7 +305,10 @@ LUA
• |vim.fs.relpath()| gets relative path compared to base path. • |vim.fs.relpath()| gets relative path compared to base path.
• |vim.fs.dir()| and |vim.fs.find()| can now follow symbolic links, • |vim.fs.dir()| and |vim.fs.find()| can now follow symbolic links,
the behavior can be turn on using the new `follow` option. the behavior can be turn on using the new `follow` option.
• |vim.hl.range()| now has a optional `timeout` field which allows for multiple
timed highlights.
• |vim.text.indent()| indents/dedents text. • |vim.text.indent()| indents/dedents text.
• |vim.fs.root()| can define "equal priority" via nested lists.
OPTIONS OPTIONS
@@ -312,7 +318,9 @@ OPTIONS
• 'messagesopt' configures |:messages| and |hit-enter| prompt. • 'messagesopt' configures |:messages| and |hit-enter| prompt.
• 'tabclose' controls which tab page to focus when closing a tab page. • 'tabclose' controls which tab page to focus when closing a tab page.
• 'eventignorewin' to persistently ignore events in a window. • 'eventignorewin' to persistently ignore events in a window.
• 'winborder' sets the default border for |floating-windows| • 'winborder' sets the default border for |floating-windows|.
• 'winborder' "bold" style.
• |g:clipboard| accepts a string name to force any builtin clipboard tool.
PERFORMANCE PERFORMANCE
@@ -345,6 +353,13 @@ PLUGINS
• 'commentstring' values can now be specified in a Treesitter capture's • 'commentstring' values can now be specified in a Treesitter capture's
`bo.commentstring` metadata field, providing finer grained support for `bo.commentstring` metadata field, providing finer grained support for
languages like `JSX`. languages like `JSX`.
• Customize :checkhealth by handling a `FileType checkhealth` event.
|health-usage|
• Simplify Python provider setup to a single step: `uv tool install pynvim`
Nvim will detect the plugin's location without user configuration, even if
unrelated Python virtual environments are activated.
|provider-python|
STARTUP STARTUP
@@ -439,6 +454,7 @@ UI
• |ui-messages| content chunks now also contain the highlight group ID. • |ui-messages| content chunks now also contain the highlight group ID.
• |:checkhealth| can display in a floating window, controlled by the • |:checkhealth| can display in a floating window, controlled by the
|g:health| variable. |g:health| variable.
• |:checkhealth| shows a summary in the header for every healthcheck.
VIMSCRIPT VIMSCRIPT

View File

@@ -11,15 +11,43 @@ not a clone: compatibility with Vim (especially editor and Vimscript features,
except |Vim9script|) is maintained where possible. See |vim-differences| for except |Vim9script|) is maintained where possible. See |vim-differences| for
the complete reference. the complete reference.
If you already use Vim, see |nvim-from-vim| for a quickstart. If you just - If you already use Vim, see |nvim-from-vim| for a quickstart.
installed Nvim and have never used it before, watch this 10-minute - If you have never used Vim or Nvim before, see below.
video: https://youtu.be/TQn2hJeHQbM .
Type |gO| to see the table of contents.
==============================================================================
What now? *nvim-quickstart*
To learn how to use Vim in 30 minutes, try the tutorial: >vim To learn how to use Vim in 30 minutes, try the tutorial: >vim
:Tutor<Enter> :Tutor<Enter>
< <
Type |gO| to see the table of contents. Or watch this 10-minute video: https://youtu.be/TQn2hJeHQbM .
To customize Nvim, you will need a config file. Create your |init.lua| by
copying the "example_init.lua" file: >vim
:exe 'edit' stdpath('config') .. '/init.lua'
:read $VIMRUNTIME/example_init.lua
<
See |lua-guide| for practical notes on using Lua to configure Nvim.
"IDE" features in Nvim are provided by |LSP|.
If you are just trying out Nvim for a few minutes, and want to see the
extremes of what it can do, try one of these popular "extension packs" or
"distributions" (Note: Nvim is not affiliated with these projects, and does
not support them):
- *lazyvim* https://www.lazyvim.org/
- *nvchad* https://nvchad.com/
- *kickstart* https://github.com/nvim-lua/kickstart.nvim
- Not recommended; use `$VIMRUNTIME/example_init.lua` instead.
However, we recommend (eventually) taking time to learn Nvim from its stock
configuration, and incrementally setting options and adding plugins to your
|config| as you discover a need.
============================================================================== ==============================================================================
Transitioning from Vim *nvim-from-vim* Transitioning from Vim *nvim-from-vim*
@@ -72,21 +100,5 @@ the same Nvim configuration on all of your machines, by creating
~/AppData/Local/nvim/init.vim containing just this line: >vim ~/AppData/Local/nvim/init.vim containing just this line: >vim
source ~/.config/nvim/init.vim source ~/.config/nvim/init.vim
==============================================================================
What next? *nvim-quickstart*
If you are just trying out Nvim for a few minutes, and want to see the
extremes of what it can do, try one of these popular "extension packs" or
"distributions" (Note: Nvim is not affiliated with these projects, and does
not support them):
- *kickstart* https://github.com/nvim-lua/kickstart.nvim
- *lazyvim* https://www.lazyvim.org/
- *nvchad* https://nvchad.com/
However, in general, we recommend (eventually) taking time to learn Nvim from
its stock configuration, and incrementally setting options and adding plugins
to your |config| as you find an explicit need to do so.
============================================================================== ==============================================================================
vim:tw=78:ts=8:et:ft=help:norl: vim:tw=78:ts=8:et:ft=help:norl:

View File

@@ -2357,10 +2357,18 @@ A jump table for the options with a short description can be found at |Q_op|.
in the |trust| list. Use |:trust| to manage trusted files. See also in the |trust| list. Use |:trust| to manage trusted files. See also
|vim.secure.read()|. |vim.secure.read()|.
To get its own location, Lua exrc files can use |debug.getinfo()|.
Compare 'exrc' to |editorconfig|: Compare 'exrc' to |editorconfig|:
- 'exrc' can execute any code; editorconfig only specifies settings. - 'exrc' can execute any code; editorconfig only specifies settings.
- 'exrc' is Nvim-specific; editorconfig works in other editors. - 'exrc' is Nvim-specific; editorconfig works in other editors.
To achieve project-local LSP configuration:
1. Enable 'exrc'.
2. Place LSP configs at ".nvim/lsp/*.lua" in your project root.
3. Create ".nvim.lua" in your project root directory with this line: >lua
vim.cmd[[set runtimepath+=.nvim]]
<
This option cannot be set from a |modeline| or in the |sandbox|, for This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons. security reasons.
@@ -5004,9 +5012,14 @@ A jump table for the options with a short description can be found at |Q_op|.
the end of line the line break still isn't included. the end of line the line break still isn't included.
When "exclusive" is used, cursor position in visual mode will be When "exclusive" is used, cursor position in visual mode will be
adjusted for inclusive motions |inclusive-motion-selection-exclusive|. adjusted for inclusive motions |inclusive-motion-selection-exclusive|.
Note that when "exclusive" is used and selecting from the end
backwards, you cannot include the last character of a line, when Note:
starting in Normal mode and 'virtualedit' empty. - When "exclusive" is used and selecting from the end backwards, you
cannot include the last character of a line, when starting in Normal
mode and 'virtualedit' empty.
- when "exclusive" is used with a single character visual selection,
Vim will behave as if the 'selection' is inclusive (in other words,
you cannot visually select an empty region).
*'selectmode'* *'slm'* *'selectmode'* *'slm'*
'selectmode' 'slm' string (default "") 'selectmode' 'slm' string (default "")
@@ -5946,8 +5959,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'statusline'* *'stl'* *E540* *E542* *'statusline'* *'stl'* *E540* *E542*
'statusline' 'stl' string (default "") 'statusline' 'stl' string (default "")
global or local to window |global-local| global or local to window |global-local|
When non-empty, this option determines the content of the status line. Sets the |status-line|.
Also see |status-line|.
The option consists of printf style '%' items interspersed with The option consists of printf style '%' items interspersed with
normal text. Each status line item is of the form: normal text. Each status line item is of the form:
@@ -7155,12 +7167,13 @@ A jump table for the options with a short description can be found at |Q_op|.
global global
Defines the default border style of floating windows. The default value Defines the default border style of floating windows. The default value
is empty, which is equivalent to "none". Valid values include: is empty, which is equivalent to "none". Valid values include:
- "bold": Bold line box.
- "double": Double-line box.
- "none": No border. - "none": No border.
- "single": A single line box.
- "double": A double line box.
- "rounded": Like "single", but with rounded corners ("╭" etc.). - "rounded": Like "single", but with rounded corners ("╭" etc.).
- "shadow": Drop shadow effect, by blending with the background.
- "single": Single-line box.
- "solid": Adds padding by a single whitespace cell. - "solid": Adds padding by a single whitespace cell.
- "shadow": A drop shadow effect by blending with the background.
*'window'* *'wi'* *'window'* *'wi'*
'window' 'wi' number (default screen height - 1) 'window' 'wi' number (default screen height - 1)

View File

@@ -4,6 +4,8 @@
vim-tutor-mode provides a system to follow and create interactive tutorials vim-tutor-mode provides a system to follow and create interactive tutorials
for vim and third party plugins. It replaces the venerable `vimtutor` system. for vim and third party plugins. It replaces the venerable `vimtutor` system.
Original Author: Felipe Morales <https://github.com/fmoralesc>
============================================================================== ==============================================================================
1. Usage *vim-tutor-usage* 1. Usage *vim-tutor-usage*
@@ -39,12 +41,5 @@ to be detected by the :Tutor command.
It is recommended to use a less formal style when writing tutorials than in It is recommended to use a less formal style when writing tutorials than in
regular documentation (unless the content requires it). regular documentation (unless the content requires it).
============================================================================ =============================================================================
3. Contributing vim:tw=78:ts=8:noet:ft=help:norl:
Development of the plugin is done over at github [1]. Feel free to report
issues and make suggestions.
[1]: https://github.com/fmoralesc/vim-tutor-mode
" vim: set ft=help :

View File

@@ -11,7 +11,7 @@ the providers and how to install them.
*E319* *E319*
Use of a feature requiring a missing provider is an error: > Use of a feature requiring a missing provider is an error: >
E319: No "foo" provider found. Run ":checkhealth provider" E319: No "foo" provider found. Run ":checkhealth vim.provider"
Run the |:checkhealth| command, and review the sections below. Run the |:checkhealth| command, and review the sections below.
@@ -35,22 +35,19 @@ if you already have it (some package managers install the module with Nvim
itself). itself).
For Python 3 plugins: For Python 3 plugins:
1. Make sure Python 3.4+ is available in your $PATH. 1. Make sure Python 3.9+ is available in your $PATH.
2. Install the module (try "python" if "python3" is missing): >bash 2. Install either uv (https://docs.astral.sh/uv/) or pipx
python3 -m pip install --user --upgrade pynvim (https://pipx.pypa.io/stable/).
3. Install the module: >bash
uv tool install --upgrade pynvim
# or:
pipx install --upgrade pynvim
The pip `--upgrade` flag ensures that you get the latest version even if The `--upgrade` flag ensures that you get the latest version even if
a previous version was already installed. a previous version was already installed.
See also |python-virtualenv|. See also |python-virtualenv|.
Note: The old "neovim" module was renamed to "pynvim".
https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
If you run into problems, uninstall _both_ then install "pynvim" again: >bash
python -m pip uninstall neovim pynvim
python -m pip install --user --upgrade pynvim
PYTHON PROVIDER CONFIGURATION ~ PYTHON PROVIDER CONFIGURATION ~
*g:python3_host_prog* *g:python3_host_prog*
Command to start Python 3 (executable, not directory). Setting this makes Command to start Python 3 (executable, not directory). Setting this makes
@@ -65,20 +62,18 @@ To disable Python 3 support: >vim
PYTHON VIRTUALENVS ~ PYTHON VIRTUALENVS ~
*python-virtualenv* *python-virtualenv*
If you plan to use per-project virtualenvs often, you should assign one Using pynvim 0.6.0+ installed via uv or pipx, Nvim will automatically detect
virtualenv for Nvim and hard-code the interpreter path via pynvim even if other Python virtual environments are activated (technical
|g:python3_host_prog| so that the "pynvim" package is not required note: via the "pynvim-python" global python tool). For older pynvim (or older
for each virtualenv. Neovim), where detection involved finding the first Python interpreter and
checking if it could import pynvim, automatic detection would fail when
Example using pyenv: >bash another virtual environment is active. Upgrading to the latest pynvim is the
pyenv install 3.4.4 recommended solution to this; but if that's not an option, then you can set
pyenv virtualenv 3.4.4 py3nvim the variable |g:python3_host_prog| in `init.vim` to point to the full path to
pyenv activate py3nvim the Python interpreter where `pynvim` is installed, e.g.: >vim
python3 -m pip install pynvim
pyenv which python # Note the path
The last command reports the interpreter path, add it to your init.vim: >vim
let g:python3_host_prog = '/path/to/py3nvim/bin/python'
let g:python3_host_prog = '/path/to/pynvim-venv/bin/python'
<
See also: https://github.com/zchee/deoplete-jedi/wiki/Setting-up-Python-for-Neovim See also: https://github.com/zchee/deoplete-jedi/wiki/Setting-up-Python-for-Neovim
============================================================================== ==============================================================================
@@ -182,100 +177,112 @@ the "+" and/or "*" registers explicitly): >vim
See 'clipboard' for details and options. See 'clipboard' for details and options.
*clipboard-tool* *clipboard-tool*
The presence of a working clipboard tool implicitly enables the '+' and "*" The presence of a working clipboard tool implicitly enables the "+" and "*"
registers. Nvim looks for these clipboard tools, in order of priority: registers. Nvim supports these clipboard tools, in order of priority:
- |g:clipboard| (unless unset or `false`) - |g:clipboard| : User override (if set to a dict or any string "name" below;
- pbcopy, pbpaste (macOS) e.g. `g:clipboard="tmux"` forces tmux clipboard and skips auto-detection).
- wl-copy, wl-paste (if $WAYLAND_DISPLAY is set) - "pbcopy" : pbcopy, pbpaste (macOS)
- waycopy, waypaste (if $WAYLAND_DISPLAY is set) - "wl-copy" : wl-copy, wl-paste (if $WAYLAND_DISPLAY is set)
- xsel (if $DISPLAY is set) - "wayclip" : waycopy, waypaste (if $WAYLAND_DISPLAY is set)
- xclip (if $DISPLAY is set) - "xsel" : xsel (if $DISPLAY is set)
- lemonade (for SSH) https://github.com/pocke/lemonade - "xclip" : xclip (if $DISPLAY is set)
- doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/ - "lemonade" : lemonade (for SSH) https://github.com/pocke/lemonade
- *win32yank* (Windows) - "doitclient": doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/
- putclip, getclip (Windows) https://cygwin.com/packages/summary/cygutils.html - "win32yank" : *win32yank* (Windows)
- clip, powershell (Windows) https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/clip - "putclip" : putclip, getclip (Windows) https://cygwin.com/packages/summary/cygutils.html
- termux (via termux-clipboard-set, termux-clipboard-set) - "clip" : clip, powershell (Windows) https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/clip
- tmux (if $TMUX is set) - "termux" : termux (via termux-clipboard-set, termux-clipboard-set)
- "tmux" : tmux (if $TMUX is set)
- "osc52" : |clipboard-osc52| (if supported by your terminal)
*g:clipboard* *g:clipboard*
To configure a custom clipboard tool, set g:clipboard to a dictionary. To configure a custom clipboard tool, set `g:clipboard` to a string name (from
For example this configuration integrates the tmux clipboard: >vim the above |clipboard-tool| list), or dict (to explicitly specify the shell
commands or |lambda| functions).
let g:clipboard = {
\ 'name': 'myClipboard',
\ 'copy': {
\ '+': ['tmux', 'load-buffer', '-'],
\ '*': ['tmux', 'load-buffer', '-'],
\ },
\ 'paste': {
\ '+': ['tmux', 'save-buffer', '-'],
\ '*': ['tmux', 'save-buffer', '-'],
\ },
\ 'cache_enabled': 1,
\ }
If "cache_enabled" is |TRUE| then when a selection is copied Nvim will cache If "cache_enabled" is |TRUE| then when a selection is copied Nvim will cache
the selection until the copy command process dies. When pasting, if the copy the selection until the copy command process dies. When pasting, if the copy
process has not died the cached selection is applied. process has not died the cached selection is applied.
g:clipboard can also use functions (see |lambda|) instead of strings.
For example this configuration uses the g:foo variable as a fake clipboard:
>vim
let g:clipboard = {
\ 'name': 'myClipboard',
\ 'copy': {
\ '+': {lines, regtype -> extend(g:, {'foo': [lines, regtype]}) },
\ '*': {lines, regtype -> extend(g:, {'foo': [lines, regtype]}) },
\ },
\ 'paste': {
\ '+': {-> get(g:, 'foo', [])},
\ '*': {-> get(g:, 'foo', [])},
\ },
\ }
The "copy" function stores a list of lines and the register type. The "paste" The "copy" function stores a list of lines and the register type. The "paste"
function returns the clipboard as a `[lines, regtype]` list, where `lines` is function returns the clipboard as a `[lines, regtype]` list, where `lines` is
a list of lines and `regtype` is a register type conforming to |setreg()|. a list of lines and `regtype` is a register type conforming to |setreg()|.
Example: set to "osc52" to force OSC52, skipping auto-detection of terminal
support: >vim
let g:clipboard = 'osc52'
Example: set to "wayclip" to force waycopy/waypaste: >vim
let g:clipboard = 'wayclip'
Example: set to a dict which integrates the tmux clipboard: >vim
let g:clipboard = {
\ 'name': 'myClipboard',
\ 'copy': {
\ '+': ['tmux', 'load-buffer', '-'],
\ '*': ['tmux', 'load-buffer', '-'],
\ },
\ 'paste': {
\ '+': ['tmux', 'save-buffer', '-'],
\ '*': ['tmux', 'save-buffer', '-'],
\ },
\ 'cache_enabled': 1,
\ }
Example: set to a dict which uses g:foo as a fake clipboard: >vim
let g:clipboard = {
\ 'name': 'myClipboard',
\ 'copy': {
\ '+': {lines, regtype -> extend(g:, {'foo': [lines, regtype]}) },
\ '*': {lines, regtype -> extend(g:, {'foo': [lines, regtype]}) },
\ },
\ 'paste': {
\ '+': {-> get(g:, 'foo', [])},
\ '*': {-> get(g:, 'foo', [])},
\ },
\ }
<
*clipboard-wsl* *clipboard-wsl*
For Windows WSL, try this g:clipboard definition: For Windows WSL, try this g:clipboard definition:
>vim >vim
let g:clipboard = { let g:clipboard = {
\ 'name': 'WslClipboard', \ 'name': 'WslClipboard',
\ 'copy': { \ 'copy': {
\ '+': 'clip.exe', \ '+': 'clip.exe',
\ '*': 'clip.exe', \ '*': 'clip.exe',
\ }, \ },
\ 'paste': { \ 'paste': {
\ '+': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))', \ '+': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
\ '*': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))', \ '*': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
\ }, \ },
\ 'cache_enabled': 0, \ 'cache_enabled': 0,
\ } \ }
< <
*clipboard-osc52* *clipboard-osc52*
Nvim bundles a clipboard provider that allows copying to the system clipboard Nvim bundles a clipboard provider that allows copying to the system clipboard
using OSC 52. OSC 52 is an Operating System Command control sequence that using OSC 52, an "Operating System Command" control-sequence that causes the
causes the terminal emulator to write to or read from the system clipboard. terminal emulator to write to or read from the system clipboard.
When Nvim is running in the |TUI|, it will automatically attempt to determine if When Nvim is running in the |TUI|, it automatically detects host terminal
the host terminal emulator supports OSC 52. If it does, then Nvim will use OSC support for OSC 52. If successful, then Nvim will use OSC 52 for copying and
52 for copying and pasting if no other |clipboard-tool| is found and when pasting if no other |clipboard-tool| is found and when 'clipboard' is unset.
'clipboard' is unset. NOTE: Using a terminal multiplexer (e.g. tmux) may NOTE: Using a terminal multiplexer (e.g. tmux) may inhibit automatic OSC 52
inhibit automatic OSC 52 support detection. support detection.
*g:termfeatures* *g:termfeatures*
To disable the automatic detection, set the "osc52" key of |g:termfeatures| to To disable the automatic detection, set the "osc52" key of |g:termfeatures| to
|v:false| in the |config| file. Example: >lua false early in your |config|. Example: >lua
local termfeatures = vim.g.termfeatures or {} local termfeatures = vim.g.termfeatures or {}
termfeatures.osc52 = false termfeatures.osc52 = false
vim.g.termfeatures = termfeatures vim.g.termfeatures = termfeatures
< <
To force Nvim to always use the OSC 52 provider you can use the following To force Nvim to use the OSC 52 provider you can set |g:clipboard|: >lua
|g:clipboard| definition: >lua
vim.g.clipboard = 'osc52' vim.g.clipboard = 'osc52'
@@ -292,7 +299,7 @@ Which is equivalent to: >lua
}, },
} }
< <
Note that not all terminal emulators support reading from the system clipboard Note: not all terminal emulators support reading from the system clipboard
(and even for those that do, users should be aware of the security (and even for those that do, users should be aware of the security
implications), so using OSC 52 for pasting may not be possible (and not implications), so using OSC 52 for pasting may not be possible (and not
necessary, because you can |paste| instead using your system paste function). necessary, because you can |paste| instead using your system paste function).

View File

@@ -91,28 +91,40 @@ processing a quickfix or location list command, it will be aborted.
:ll[!] [nr] Same as ":cc", except the location list for the :ll[!] [nr] Same as ":cc", except the location list for the
:[nr]ll[!] current window is used instead of the quickfix list. :[nr]ll[!] current window is used instead of the quickfix list.
*:cn* *:cne* *:cnext* *E553* *]q* *:cn* *:cne* *:cnext* *E553*
:[count]cn[ext][!] Display the [count] next error in the list that :[count]cn[ext][!] Display the [count] next error in the list that
includes a file name. If there are no file names at includes a file name. If there are no file names at
all, go to the [count] next error. See |:cc| for all, go to the [count] next error. See |:cc| for
[!] and 'switchbuf'. [!] and 'switchbuf'.
*:lne* *:lnext* *]l* *]q*
]q Mapped to |:cnext|. |default-mappings|
*:lne* *:lnext*
:[count]lne[xt][!] Same as ":cnext", except the location list for the :[count]lne[xt][!] Same as ":cnext", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
:[count]cN[ext][!] *:cp* *:cprevious* *:cprev* *:cN* *:cNext* *[q* *]l*
]l Mapped to |:lnext|. |default-mappings|
:[count]cN[ext][!] *:cp* *:cprevious* *:cprev* *:cN* *:cNext*
:[count]cp[revious][!] Display the [count] previous error in the list that :[count]cp[revious][!] Display the [count] previous error in the list that
includes a file name. If there are no file names at includes a file name. If there are no file names at
all, go to the [count] previous error. See |:cc| for all, go to the [count] previous error. See |:cc| for
[!] and 'switchbuf'. [!] and 'switchbuf'.
*[q*
[q Mapped to |:cprevious|. |default-mappings|
:[count]lN[ext][!] *:lp* *:lprevious* *:lprev* *:lN* *:lNext* *[l*
:[count]lN[ext][!] *:lp* *:lprevious* *:lprev* *:lN* *:lNext*
:[count]lp[revious][!] Same as ":cNext" and ":cprevious", except the location :[count]lp[revious][!] Same as ":cNext" and ":cprevious", except the location
list for the current window is used instead of the list for the current window is used instead of the
quickfix list. quickfix list.
*[l*
[l Mapped to |:lprevious|. |default-mappings|
*:cabo* *:cabove* *:cabo* *:cabove*
:[count]cabo[ve] Go to the [count] error above the current line in the :[count]cabo[ve] Go to the [count] error above the current line in the
current buffer. If [count] is omitted, then 1 is current buffer. If [count] is omitted, then 1 is
@@ -171,52 +183,76 @@ processing a quickfix or location list command, it will be aborted.
:[count]laf[ter] Same as ":cafter", except the location list for the :[count]laf[ter] Same as ":cafter", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*:cnf* *:cnfile* *]CTRL-Q* *:cnf* *:cnfile*
:[count]cnf[ile][!] Display the first error in the [count] next file in :[count]cnf[ile][!] Display the first error in the [count] next file in
the list that includes a file name. If there are no the list that includes a file name. If there are no
file names at all or if there is no next file, go to file names at all or if there is no next file, go to
the [count] next error. See |:cc| for [!] and the [count] next error. See |:cc| for [!] and
'switchbuf'. 'switchbuf'.
*:lnf* *:lnfile* *]CTRL-L* *]CTRL-Q*
]CTRL-Q Mapped to |:cnfile|. |default-mappings|
*:lnf* *:lnfile*
:[count]lnf[ile][!] Same as ":cnfile", except the location list for the :[count]lnf[ile][!] Same as ":cnfile", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
:[count]cNf[ile][!] *:cpf* *:cpfile* *:cNf* *:cNfile* *[CTRL-Q* *]CTRL-L*
]CTRL-L Mapped to |:lnfile|. |default-mappings|
:[count]cNf[ile][!] *:cpf* *:cpfile* *:cNf* *:cNfile*
:[count]cpf[ile][!] Display the last error in the [count] previous file in :[count]cpf[ile][!] Display the last error in the [count] previous file in
the list that includes a file name. If there are no the list that includes a file name. If there are no
file names at all or if there is no next file, go to file names at all or if there is no next file, go to
the [count] previous error. See |:cc| for [!] and the [count] previous error. See |:cc| for [!] and
'switchbuf'. 'switchbuf'.
*[CTRL-Q*
[CTRL-Q Mapped to |:cpfile|. |default-mappings|
:[count]lNf[ile][!] *:lpf* *:lpfile* *:lNf* *:lNfile* *[CTRL-L*
:[count]lNf[ile][!] *:lpf* *:lpfile* *:lNf* *:lNfile*
:[count]lpf[ile][!] Same as ":cNfile" and ":cpfile", except the location :[count]lpf[ile][!] Same as ":cNfile" and ":cpfile", except the location
list for the current window is used instead of the list for the current window is used instead of the
quickfix list. quickfix list.
*:crewind* *:cr* *[Q* *[CTRL-L*
[CTRL-L Mapped to |:lpfile|. |default-mappings|
*:crewind* *:cr*
:cr[ewind][!] [nr] Display error [nr]. If [nr] is omitted, the FIRST :cr[ewind][!] [nr] Display error [nr]. If [nr] is omitted, the FIRST
error is displayed. See |:cc|. error is displayed. See |:cc|.
*:lrewind* *:lr* *[L* *[Q*
[Q Mapped to |:crewind|. |default-mappings|
*:lrewind* *:lr*
:lr[ewind][!] [nr] Same as ":crewind", except the location list for the :lr[ewind][!] [nr] Same as ":crewind", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*[L*
[L Mapped to |:lrewind|. |default-mappings|
*:cfirst* *:cfir* *:cfirst* *:cfir*
:cfir[st][!] [nr] Same as ":crewind". :cfir[st][!] [nr] Same as ":crewind".
*:lfirst* *:lfir* *:lfirst* *:lfir*
:lfir[st][!] [nr] Same as ":lrewind". :lfir[st][!] [nr] Same as ":lrewind".
*:clast* *:cla* *]Q* *:clast* *:cla*
:cla[st][!] [nr] Display error [nr]. If [nr] is omitted, the LAST :cla[st][!] [nr] Display error [nr]. If [nr] is omitted, the LAST
error is displayed. See |:cc|. error is displayed. See |:cc|.
*:llast* *:lla* *]L* *]Q*
]Q Mapped to |:clast|.
*:llast* *:lla*
:lla[st][!] [nr] Same as ":clast", except the location list for the :lla[st][!] [nr] Same as ":clast", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*]L*
]L Mapped to |:llast|.
*:cq* *:cquit* *:cq* *:cquit*
:cq[uit][!] :cq[uit][!]
:{N}cq[uit][!] :{N}cq[uit][!]

View File

@@ -1382,8 +1382,8 @@ The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, $XDG_STATE_HOME,
$XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables $XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables
are used if defined, else default values (listed below) are used. are used if defined, else default values (listed below) are used.
Throughout the help pages these defaults are used as placeholders, e.g. Note: In the help these defaults are used as placeholders, e.g. "~/.config" is
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config". understood as "$XDG_CONFIG_HOME or ~/.config".
CONFIG DIRECTORY (DEFAULT) ~ CONFIG DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_HOME* Nvim: stdpath("config") *$XDG_CONFIG_HOME* Nvim: stdpath("config")
@@ -1437,12 +1437,17 @@ configuration files in `$XDG_CONFIG_HOME/foo` instead of
`$XDG_CONFIG_HOME/nvim`. `$NVIM_APPNAME` must be a name, such as "foo", or a `$XDG_CONFIG_HOME/nvim`. `$NVIM_APPNAME` must be a name, such as "foo", or a
relative path, such as "foo/bar". relative path, such as "foo/bar".
Note: In the help wherever `$XDG_CONFIG_…/nvim` is mentioned it is understood
as `$XDG_CONFIG_…/$NVIM_APPNAME`.
*state-isolation*
One use-case for $NVIM_APPNAME is to "isolate" Nvim applications. One use-case for $NVIM_APPNAME is to "isolate" Nvim applications.
Alternatively, for true isolation, on Linux you can use cgroups namespaces: > Alternatively, for true isolation, on Linux you can use cgroups namespaces: >
systemd-run --user -qt -p PrivateUsers=yes -p BindPaths=/home/user/profile_xy:/home/user/.config/nvim nvim systemd-run --user -qt -p PrivateUsers=yes -p BindPaths=/home/user/profile_xy:/home/user/.config/nvim nvim
<
Note: Throughout the help pages, wherever `$XDG_CONFIG_…/nvim` is mentioned it *stateless*
is understood to mean `$XDG_CONFIG_…/$NVIM_APPNAME`. To run Nvim without creating any directories or data files: >
NVIM_LOG_FILE=/dev/null nvim -n -i NONE
LOG FILE *log* *$NVIM_LOG_FILE* *E5430* LOG FILE *log* *$NVIM_LOG_FILE* *E5430*
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal Besides 'debug' and 'verbose', Nvim keeps a general log file for internal

View File

@@ -274,27 +274,39 @@ g CTRL-] Like CTRL-], but use ":tjump" instead of ":tag".
{Visual}g CTRL-] Same as "g CTRL-]", but use the highlighted text as {Visual}g CTRL-] Same as "g CTRL-]", but use the highlighted text as
the identifier. the identifier.
*:tn* *:tnext* *]t* *:tn* *:tnext*
:[count]tn[ext][!] Jump to [count] next matching tag (default 1). See :[count]tn[ext][!] Jump to [count] next matching tag (default 1). See
|tag-!| for [!]. |tag-!| for [!].
*:tp* *:tprevious* *[t* *]t*
]t Mapped to |:tnext|. |default-mappings|
*:tp* *:tprevious*
:[count]tp[revious][!] Jump to [count] previous matching tag (default 1). :[count]tp[revious][!] Jump to [count] previous matching tag (default 1).
See |tag-!| for [!]. See |tag-!| for [!].
*[t*
[t Mapped to |:tprevious|. |default-mappings|
*:tN* *:tNext* *:tN* *:tNext*
:[count]tN[ext][!] Same as ":tprevious". :[count]tN[ext][!] Same as ":tprevious".
*:tr* *:trewind* *[T* *:tr* *:trewind*
:[count]tr[ewind][!] Jump to first matching tag. If [count] is given, jump :[count]tr[ewind][!] Jump to first matching tag. If [count] is given, jump
to [count]th matching tag. See |tag-!| for [!]. to [count]th matching tag. See |tag-!| for [!].
*[T*
[T Mapped to |:trewind|. |default-mappings|
*:tf* *:tfirst* *:tf* *:tfirst*
:[count]tf[irst][!] Same as ":trewind". :[count]tf[irst][!] Same as ":trewind".
*:tl* *:tlast* *]T* *:tl* *:tlast*
:tl[ast][!] Jump to last matching tag. See |tag-!| for [!]. :tl[ast][!] Jump to last matching tag. See |tag-!| for [!].
*]T*
]T Mapped to |:tlast|. |default-mappings|
*:lt* *:ltag* *:lt* *:ltag*
:lt[ag][!] [name] Jump to tag [name] and add the matching tags to a new :lt[ag][!] [name] Jump to tag [name] and add the matching tags to a new
location list for the current window. [name] can be location list for the current window. [name] can be
@@ -335,12 +347,18 @@ the same as above, with a "p" prepended.
:ptj[ump][!] [name] Does ":tjump[!] [name]" and shows the new tag in a :ptj[ump][!] [name] Does ":tjump[!] [name]" and shows the new tag in a
"Preview" window. See |:ptag| for more info. "Preview" window. See |:ptag| for more info.
*:ptn* *:ptnext* *]CTRL-T* *:ptn* *:ptnext*
:[count]ptn[ext][!] ":tnext" in the preview window. See |:ptag|. :[count]ptn[ext][!] ":tnext" in the preview window. See |:ptag|.
*:ptp* *:ptprevious* *[CTRL-T* *]CTRL-T*
]CTRL-T Mapped to |:ptnext|. |default-mappings|
*:ptp* *:ptprevious*
:[count]ptp[revious][!] ":tprevious" in the preview window. See |:ptag|. :[count]ptp[revious][!] ":tprevious" in the preview window. See |:ptag|.
*[CTRL-T*
[CTRL-T Mapped to |:ptprevious|. |default-mappings|
*:ptN* *:ptNext* *:ptN* *:ptNext*
:[count]ptN[ext][!] Same as ":ptprevious". :[count]ptN[ext][!] Same as ":ptprevious".

View File

@@ -120,7 +120,7 @@ The following predicates are built in:
match. match.
`lua-match?` *treesitter-predicate-lua-match?* `lua-match?` *treesitter-predicate-lua-match?*
Match |lua-patterns| against the text corresponding to a node, Match |lua-pattern|s against the text corresponding to a node,
similar to `match?` similar to `match?`
`any-lua-match?` *treesitter-predicate-any-lua-match?* `any-lua-match?` *treesitter-predicate-any-lua-match?*
@@ -573,7 +573,10 @@ parent tree. The language injection query allows you to specify these
“injections” using the following captures: “injections” using the following captures:
• `@injection.content` - indicates that the captured node should have its • `@injection.content` - indicates that the captured node should have its
contents re-parsed using another language. contents re-parsed using another language. If there are multiple
`@injection.content` captures in one pattern, all ranges will be
collected and parsed as one tree. This allows query authors to create
"scoped" injections with injection query quantifiers.
• `@injection.language` - indicates that the captured nodes text may • `@injection.language` - indicates that the captured nodes text may
contain the name of a language that should be used to re-parse the contain the name of a language that should be used to re-parse the
`@injection.content`. `@injection.content`.

View File

@@ -25,9 +25,9 @@ Table of contents: |usr_toc.txt|
============================================================================== ==============================================================================
*02.1* Running Vim for the First Time *02.1* Running Vim for the First Time
To start Vim, enter this command: > To start Nvim, enter this command: >
gvim file.txt nvim file.txt
On Unix you can type this at any command prompt. If you are running Microsoft On Unix you can type this at any command prompt. If you are running Microsoft
Windows, open a Command Prompt and enter the command. In either case, Vim Windows, open a Command Prompt and enter the command. In either case, Vim
@@ -50,20 +50,6 @@ screen, a message line indicates the file is named file.txt and shows that you
are creating a new file. The message information is temporary and other are creating a new file. The message information is temporary and other
information overwrites it. information overwrites it.
THE VIM COMMAND
The gvim command causes the editor to create a new window for editing. If you
use this command: >
vim file.txt
the editing occurs inside your command window. In other words, if you are
running inside an xterm, the editor uses your xterm window. If you are using
the command prompt under Microsoft Windows, the editing occurs inside this
window. The text in the window will look the same for both versions, but with
gvim you have extra features, like a menu bar. More about that later.
============================================================================== ==============================================================================
*02.2* Inserting text *02.2* Inserting text
@@ -580,7 +566,7 @@ Summary: *help-summary* >
:help quote: :help quote:
13) Vim Script is available at > 13) Vim Script is available at >
:help eval.txt :help vimeval.txt
< Certain aspects of the language are available at :h expr-X where "X" is a < Certain aspects of the language are available at :h expr-X where "X" is a
single letter. E.g. > single letter. E.g. >
:help expr-! :help expr-!
@@ -660,10 +646,13 @@ Summary: *help-summary* >
command switch of Vim use: > command switch of Vim use: >
:help -f :help -f
24) Optional features always start with "+". To find out about the 24) Lua language and Nvim's Lua standard library are available at >vim
conceal feature use: > :help lua.txt
:help +conceal < Guide to using Lua in Nvim is available at >vim
:help lua-guide.txt
< Lua 5.1 reference manual is available at >vim
:help luaref.txt
<
25) Documentation for included filetype specific functionality is usually 25) Documentation for included filetype specific functionality is usually
available in the form ft-<filetype>-<functionality>. So > available in the form ft-<filetype>-<functionality>. So >
:help ft-c-syntax :help ft-c-syntax

View File

@@ -103,11 +103,11 @@ g8 Print the hex values of the bytes used in the
*gx* *gx*
gx Opens the current filepath or URL (decided by gx Opens the current filepath or URL (decided by
|<cfile>|, 'isfname') at cursor using the system |<cfile>|, 'isfname') at cursor using the system
default handler, by calling |vim.ui.open()|. default handler. Mapped to |vim.ui.open()|.
*v_gx* *v_gx*
{Visual}gx Opens the selected text using the system default {Visual}gx Opens the selected text using the system default
handler, by calling |vim.ui.open()|. handler. Mapped to |vim.ui.open()|.
*:p* *:pr* *:print* *E749* *:p* *:pr* *:print* *E749*
:[range]p[rint] [flags] :[range]p[rint] [flags]
@@ -611,6 +611,8 @@ to look up the value of 'commentstring' corresponding to the cursor position.
(This can be different from the buffer's 'commentstring' in case of (This can be different from the buffer's 'commentstring' in case of
|treesitter-language-injections|.) |treesitter-language-injections|.)
The following |default-mappings| are defined:
*gc* *gc-default* *gc* *gc-default*
gc{motion} Comment or uncomment lines covered by {motion}. gc{motion} Comment or uncomment lines covered by {motion}.

View File

@@ -134,7 +134,8 @@ To remove only the "How-to disable mouse" menu item (and its separator): >vim
DEFAULT MAPPINGS DEFAULT MAPPINGS
*default-mappings* *default-mappings*
Nvim creates the following default mappings at |startup|. You can disable any Nvim creates the following default mappings at |startup|. You can disable any
of these in your config by simply removing the mapping, e.g. ":unmap Y". of these in your config by simply removing the mapping, e.g. ":unmap Y". If
you never want any default mappings, call |:mapclear| early in your config.
- Y |Y-default| - Y |Y-default|
- <C-U> |i_CTRL-U-default| - <C-U> |i_CTRL-U-default|
@@ -152,6 +153,7 @@ of these in your config by simply removing the mapping, e.g. ":unmap Y".
- |grr| - |grr|
- |gra| - |gra|
- |gri| - |gri|
- |grt|
- |gO| - |gO|
- <C-S> |i_CTRL-S| - <C-S> |i_CTRL-S|
- ]d |]d-default| - ]d |]d-default|
@@ -383,6 +385,7 @@ Options:
- 'showcmdloc' cannot be set to empty. - 'showcmdloc' cannot be set to empty.
- 'signcolumn' can show multiple signs (dynamic or fixed columns) - 'signcolumn' can show multiple signs (dynamic or fixed columns)
- 'statuscolumn' full control of columns using 'statusline' format - 'statuscolumn' full control of columns using 'statusline' format
- 'statusline' default is exposed as a statusline expression.
- 'splitkeep' cannot be set to empty. - 'splitkeep' cannot be set to empty.
- 'tabline' middle-click on tabpage label closes tabpage, - 'tabline' middle-click on tabpage label closes tabpage,
and %@Func@foo%X can call any function on mouse-click and %@Func@foo%X can call any function on mouse-click
@@ -854,9 +857,8 @@ Startup:
Test functions: Test functions:
- test_alloc_fail() - test_alloc_fail()
- test_autochdir() - test_autochdir()
- test_disable_char_avail()
- test_feedinput() - test_feedinput()
- test_garbagecollect_soon - test_garbagecollect_soon()
- test_getvalue() - test_getvalue()
- test_ignore_error() - test_ignore_error()
- test_null_blob() - test_null_blob()
@@ -874,6 +876,8 @@ Test functions:
- test_setmouse() - test_setmouse()
- test_settime() - test_settime()
- test_srand_seed() - test_srand_seed()
- test_unknown()
- test_void()
TUI: TUI:
*t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI* *t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI*

View File

@@ -1,10 +1,10 @@
*eval.txt* Nvim *vimeval.txt* Nvim
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
Expression evaluation *vimscript* *expression* *expr* *E15* *eval* Expression evaluation *vimscript* *expression* *expr* *E15* *eval* *eval.txt*
Using expressions is introduced in chapter 41 of the user manual |usr_41.txt|. Using expressions is introduced in chapter 41 of the user manual |usr_41.txt|.

View File

@@ -1,10 +1,10 @@
*builtin.txt* Nvim *vimfn.txt* Nvim
NVIM REFERENCE MANUAL NVIM REFERENCE MANUAL
Builtin functions *vimscript-functions* *builtin-functions* Vimscript functions *vimscript-functions* *builtin-functions* *builtin.txt*
For functions grouped by what they are used for see |function-list|. For functions grouped by what they are used for see |function-list|.
@@ -110,7 +110,7 @@ append({lnum}, {text}) *append()*
< <
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {text} (`string|string[]`) • {text} (`string|string[]`)
Return: ~ Return: ~
@@ -1047,7 +1047,7 @@ cindent({lnum}) *cindent()*
To get or set indent of lines in a string, see |vim.text.indent()|. To get or set indent of lines in a string, see |vim.text.indent()|.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -1637,7 +1637,7 @@ diff_filler({lnum}) *diff_filler()*
Returns 0 if the current window is not in diff mode. Returns 0 if the current window is not in diff mode.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -1654,7 +1654,7 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
syntax information about the highlighting. syntax information about the highlighting.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {col} (`integer`) • {col} (`integer`)
Return: ~ Return: ~
@@ -2040,7 +2040,8 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
<SID> "<SNR>123_" where "123" is the <SID> "<SNR>123_" where "123" is the
current script ID |<SID>| current script ID |<SID>|
<script> sourced script file, or script file <script> sourced script file, or script file
where the current function was defined where the current function was defined.
Use |debug.getinfo()| in Lua scripts.
<stack> call stack <stack> call stack
<cword> word under the cursor <cword> word under the cursor
<cWORD> WORD under the cursor <cWORD> WORD under the cursor
@@ -2563,7 +2564,7 @@ foldclosed({lnum}) *foldclosed()*
line, "'m" mark m, etc. line, "'m" mark m, etc.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -2576,7 +2577,7 @@ foldclosedend({lnum}) *foldclosedend()*
line, "'m" mark m, etc. line, "'m" mark m, etc.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -2594,7 +2595,7 @@ foldlevel({lnum}) *foldlevel()*
line, "'m" mark m, etc. line, "'m" mark m, etc.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -2629,7 +2630,7 @@ foldtextresult({lnum}) *foldtextresult()*
Useful when exporting folded text, e.g., to HTML. Useful when exporting folded text, e.g., to HTML.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`string`) (`string`)
@@ -3244,7 +3245,7 @@ getcharsearch() *getcharsearch()*
Return: ~ Return: ~
(`table`) (`table`)
getcharstr([{expr}]) *getcharstr()* getcharstr([{expr} [, {opts}]]) *getcharstr()*
The same as |getchar()|, except that this always returns a The same as |getchar()|, except that this always returns a
String, and "number" isn't allowed in {opts}. String, and "number" isn't allowed in {opts}.
@@ -4093,6 +4094,10 @@ getregion({pos1}, {pos2} [, {opts}]) *getregion()*
- It is evaluated in current window context, which makes a - It is evaluated in current window context, which makes a
difference if the buffer is displayed in a window with difference if the buffer is displayed in a window with
different 'virtualedit' or 'list' values. different 'virtualedit' or 'list' values.
- When specifying an exclusive selection and {pos1} and {pos2}
are equal, the returned list contains a single character as
if selection is inclusive, to match the behavior of an empty
exclusive selection in Visual mode.
Examples: >vim Examples: >vim
xnoremap <CR> xnoremap <CR>
@@ -5706,7 +5711,7 @@ line2byte({lnum}) *line2byte()*
Also see |byte2line()|, |go| and |:goto|. Also see |byte2line()|, |go| and |:goto|.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -5719,7 +5724,7 @@ lispindent({lnum}) *lispindent()*
When {lnum} is invalid, -1 is returned. When {lnum} is invalid, -1 is returned.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -6090,7 +6095,7 @@ mapset({dict})
< <
Parameters: ~ Parameters: ~
• {dict} (`boolean`) • {dict} (`table<string,any>`)
Return: ~ Return: ~
(`any`) (`any`)
@@ -6990,7 +6995,7 @@ nextnonblank({lnum}) *nextnonblank()*
See also |prevnonblank()|. See also |prevnonblank()|.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -7124,7 +7129,7 @@ prevnonblank({lnum}) *prevnonblank()*
Also see |nextnonblank()|. Also see |nextnonblank()|.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
Return: ~ Return: ~
(`integer`) (`integer`)
@@ -7392,7 +7397,8 @@ printf({fmt}, {expr1} ...) *printf()*
< 1.41 < 1.41
You will get an overflow error |E1510|, when the field-width You will get an overflow error |E1510|, when the field-width
or precision will result in a string longer than 6400 chars. or precision will result in a string longer than 1 MB
(1024*1024 = 1048576) chars.
*E1500* *E1500*
You cannot mix positional and non-positional arguments: >vim You cannot mix positional and non-positional arguments: >vim
@@ -8937,7 +8943,7 @@ setline({lnum}, {text}) *setline()*
< Note: The '[ and '] marks are not set. < Note: The '[ and '] marks are not set.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {text} (`any`) • {text} (`any`)
Return: ~ Return: ~
@@ -10851,7 +10857,7 @@ synID({lnum}, {col}, {trans}) *synID()*
< <
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {col} (`integer`) • {col} (`integer`)
• {trans} (`0|1`) • {trans} (`0|1`)
@@ -10956,7 +10962,7 @@ synconcealed({lnum}, {col}) *synconcealed()*
mechanisms |syntax-vs-match|. mechanisms |syntax-vs-match|.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {col} (`integer`) • {col} (`integer`)
Return: ~ Return: ~
@@ -10982,7 +10988,7 @@ synstack({lnum}, {col}) *synstack()*
valid positions. valid positions.
Parameters: ~ Parameters: ~
• {lnum} (`integer`) • {lnum} (`integer|string`)
• {col} (`integer`) • {col} (`integer`)
Return: ~ Return: ~
@@ -11972,14 +11978,13 @@ windowsversion() *windowsversion()*
(`string`) (`string`)
winheight({nr}) *winheight()* winheight({nr}) *winheight()*
The result is a Number, which is the height of window {nr}. Gets the height of |window-ID| {nr} (zero for "current
{nr} can be the window number or the |window-ID|. window"), excluding any 'winbar' and 'statusline'. Returns -1
When {nr} is zero, the height of the current window is if window {nr} doesn't exist. An existing window always has
returned. When window {nr} doesn't exist, -1 is returned. a height of zero or more.
An existing window always has a height of zero or more.
This excludes any window toolbar line.
Examples: >vim Examples: >vim
echo "The current window has " .. winheight(0) .. " lines." echo "Current window has " .. winheight(0) .. " lines."
< <
Parameters: ~ Parameters: ~
@@ -12029,7 +12034,7 @@ winlayout([{tabnr}]) *winlayout()*
• {tabnr} (`integer?`) • {tabnr} (`integer?`)
Return: ~ Return: ~
(`any[]`) (`vim.fn.winlayout.ret`)
winline() *winline()* winline() *winline()*
The result is a Number, which is the screen line of the cursor The result is a Number, which is the screen line of the cursor
@@ -12146,18 +12151,21 @@ winsaveview() *winsaveview()*
(`vim.fn.winsaveview.ret`) (`vim.fn.winsaveview.ret`)
winwidth({nr}) *winwidth()* winwidth({nr}) *winwidth()*
The result is a Number, which is the width of window {nr}. Gets the width of |window-ID| {nr} (zero for "current
{nr} can be the window number or the |window-ID|. window"), including columns (|sign-column|, 'statuscolumn',
When {nr} is zero, the width of the current window is etc.). Returns -1 if window {nr} doesn't exist. An existing
returned. When window {nr} doesn't exist, -1 is returned. window always has a width of zero or more.
An existing window always has a width of zero or more.
Examples: >vim Example: >vim
echo "The current window has " .. winwidth(0) .. " columns." echo "Current window has " .. winwidth(0) .. " columns."
if winwidth(0) <= 50 if winwidth(0) <= 50
50 wincmd | 50 wincmd |
endif endif
< For getting the terminal or screen size, see the 'columns' <
option. To get the buffer "viewport", use |getwininfo()|: >vim
:echo getwininfo(win_getid())[0].width - getwininfo(win_getid())[0].textoff
<
To get the Nvim screen size, see the 'columns' option.
Parameters: ~ Parameters: ~
• {nr} (`integer`) • {nr} (`integer`)

View File

@@ -69,8 +69,9 @@ If a window is focusable, it is part of the "navigation stack", that is,
editor commands such as :windo, |CTRL-W|, etc., will consider the window as editor commands such as :windo, |CTRL-W|, etc., will consider the window as
one that can be made the "current window". A non-focusable window will be one that can be made the "current window". A non-focusable window will be
skipped by such commands (though it can be explicitly focused by skipped by such commands (though it can be explicitly focused by
|nvim_set_current_win()|). Non-focusable windows are not listed by |:tabs|, and |nvim_set_current_win()|). Non-focusable windows are not listed by |:tabs|,
are not counted by the default 'tabline'. or counted by the default 'tabline'. Their buffer content is not included
in 'complete' "w" completion.
Windows (especially floating windows) can have many other |api-win_config| Windows (especially floating windows) can have many other |api-win_config|
properties such as "hide" and "fixed" which also affect behavior. properties such as "hide" and "fixed" which also affect behavior.
@@ -438,18 +439,17 @@ CTRL-W l Move cursor to Nth window right of current one. Uses the
CTRL-W w *CTRL-W_w* *CTRL-W_CTRL-W* CTRL-W w *CTRL-W_w* *CTRL-W_CTRL-W*
CTRL-W CTRL-W Without count: move cursor to the |focusable| window CTRL-W CTRL-W Without count: move cursor to the |focusable| window
below/right of the current one. If there is no (focusable) below/right of the current one. If none, go to the top-left
window below or right, go to top-left window. With count: go window. With count: go to Nth window (numbered top-left to
to Nth window (windows are numbered from top-left to bottom-right), skipping unfocusable windows. To obtain the
bottom-right). To obtain the window number see |bufwinnr()| window number see |bufwinnr()| and |winnr()|. When N is
and |winnr()|. When N is larger than the number of windows go larger than the number of windows go to the last focusable
to the last window. window.
*CTRL-W_W* *CTRL-W_W*
CTRL-W W Without count: move cursor to the |focusable| window CTRL-W W Without count: move cursor to the |focusable| window
above/left of current one. If there is no window above or above/left of current one. If none, go to the bottom-right
left, go to bottom-right window. With count: go to Nth window. With count: go to Nth window, like CTRL-W w.
window, like with CTRL-W w.
CTRL-W t *CTRL-W_t* *CTRL-W_CTRL-T* CTRL-W t *CTRL-W_t* *CTRL-W_CTRL-T*
CTRL-W CTRL-T Move cursor to top-left window. CTRL-W CTRL-T Move cursor to top-left window.
@@ -1272,7 +1272,7 @@ list of buffers. |unlisted-buffer|
:w foobar | sp # :w foobar | sp #
< Also see |+cmd|. < Also see |+cmd|.
:[N]bn[ext][!] [+cmd] [N] *:bn* *:bnext* *]b* *E87* :[N]bn[ext][!] [+cmd] [N] *:bn* *:bnext* *E87*
Go to [N]th next buffer in buffer list. [N] defaults to one. Go to [N]th next buffer in buffer list. [N] defaults to one.
Wraps around the end of the buffer list. Wraps around the end of the buffer list.
See |:buffer-!| for [!]. See |:buffer-!| for [!].
@@ -1284,13 +1284,20 @@ list of buffers. |unlisted-buffer|
the way when you're browsing code/text buffers. The next three the way when you're browsing code/text buffers. The next three
commands also work like this. commands also work like this.
*]b*
]b Mapped to |:bnext|. |default-mappings|
*:sbn* *:sbnext* *:sbn* *:sbnext*
:[N]sbn[ext] [+cmd] [N] :[N]sbn[ext] [+cmd] [N]
Split window and go to [N]th next buffer in buffer list. Split window and go to [N]th next buffer in buffer list.
Wraps around the end of the buffer list. Uses 'switchbuf' Wraps around the end of the buffer list. Uses 'switchbuf'
Also see |+cmd|. Also see |+cmd|.
:[N]bN[ext][!] [+cmd] [N] *:bN* *:bNext* *:bp* *:bprevious* *[b* *E88* :[N]bN[ext][!] [+cmd] [N] *:bN* *:bNext* *:bp* *:bprevious* *E88*
*[b*
[b Mapped to |:bprevious|. |default-mappings|
:[N]bp[revious][!] [+cmd] [N] :[N]bp[revious][!] [+cmd] [N]
Go to [N]th previous buffer in buffer list. [N] defaults to Go to [N]th previous buffer in buffer list. [N] defaults to
one. Wraps around the start of the buffer list. one. Wraps around the start of the buffer list.
@@ -1304,11 +1311,14 @@ list of buffers. |unlisted-buffer|
Uses 'switchbuf'. Uses 'switchbuf'.
Also see |+cmd|. Also see |+cmd|.
:br[ewind][!] [+cmd] *:br* *:bre* *:brewind* *[B* :br[ewind][!] [+cmd] *:br* *:bre* *:brewind*
Go to first buffer in buffer list. If the buffer list is Go to first buffer in buffer list. If the buffer list is
empty, go to the first unlisted buffer. empty, go to the first unlisted buffer.
See |:buffer-!| for [!]. See |:buffer-!| for [!].
*[B*
[B Mapped to |:brewind|. |default-mappings|
:bf[irst] [+cmd] *:bf* *:bfirst* :bf[irst] [+cmd] *:bf* *:bfirst*
Same as |:brewind|. Same as |:brewind|.
Also see |+cmd|. Also see |+cmd|.
@@ -1322,11 +1332,14 @@ list of buffers. |unlisted-buffer|
:sbf[irst] [+cmd] *:sbf* *:sbfirst* :sbf[irst] [+cmd] *:sbf* *:sbfirst*
Same as ":sbrewind". Same as ":sbrewind".
:bl[ast][!] [+cmd] *:bl* *:blast* *]B* :bl[ast][!] [+cmd] *:bl* *:blast*
Go to last buffer in buffer list. If the buffer list is Go to last buffer in buffer list. If the buffer list is
empty, go to the last unlisted buffer. empty, go to the last unlisted buffer.
See |:buffer-!| for [!]. See |:buffer-!| for [!].
*]B*
]B Mapped to |:blast|. |default-mappings|
:sbl[ast] [+cmd] *:sbl* *:sblast* :sbl[ast] [+cmd] *:sbl* *:sblast*
Split window and go to last buffer in buffer list. If the Split window and go to last buffer in buffer list. If the
buffer list is empty, go to the last unlisted buffer. buffer list is empty, go to the last unlisted buffer.

88
runtime/example_init.lua Normal file
View File

@@ -0,0 +1,88 @@
-- Set <space> as the leader key
-- See `:help mapleader`
-- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
vim.g.mapleader = ' '
-- [[ Setting options ]] See `:h vim.o`
-- NOTE: You can change these options as you wish!
-- For more options, you can see `:help option-list`
-- To see documentation for an option, you can use `:h 'optionname'`, for example `:h 'number'`
-- (Note the single quotes)
-- Print the line number in front of each line
vim.o.number = true
-- Use relative line numbers, so that it is easier to jump with j, k. This will affect the 'number'
-- option above, see `:h number_relativenumber`
vim.o.relativenumber = true
-- Sync clipboard between OS and Neovim. Schedule the setting after `UiEnter` because it can
-- increase startup-time. Remove this option if you want your OS clipboard to remain independent.
-- See `:help 'clipboard'`
vim.api.nvim_create_autocmd('UIEnter', {
callback = function()
vim.o.clipboard = 'unnamedplus'
end,
})
-- Case-insensitive searching UNLESS \C or one or more capital letters in the search term
vim.o.ignorecase = true
vim.o.smartcase = true
-- Highlight the line where the cursor is on
vim.o.cursorline = true
-- Minimal number of screen lines to keep above and below the cursor.
vim.o.scrolloff = 10
-- Show <tab> and trailing spaces
vim.o.list = true
-- if performing an operation that would fail due to unsaved changes in the buffer (like `:q`),
-- instead raise a dialog asking if you wish to save the current file(s) See `:help 'confirm'`
vim.o.confirm = true
-- [[ Set up keymaps ]] See `:h vim.keymap.set()`, `:h mapping`, `:h keycodes`
-- Use <Esc> to exit terminal mode
vim.keymap.set('t', '<Esc>', '<C-\\><C-n>')
-- Map <A-j>, <A-k>, <A-h>, <A-l> to navigate between windows in any modes
vim.keymap.set({ 't', 'i' }, '<A-h>', '<C-\\><C-n><C-w>h')
vim.keymap.set({ 't', 'i' }, '<A-j>', '<C-\\><C-n><C-w>j')
vim.keymap.set({ 't', 'i' }, '<A-k>', '<C-\\><C-n><C-w>k')
vim.keymap.set({ 't', 'i' }, '<A-l>', '<C-\\><C-n><C-w>l')
vim.keymap.set({ 'n' }, '<A-h>', '<C-w>h')
vim.keymap.set({ 'n' }, '<A-j>', '<C-w>j')
vim.keymap.set({ 'n' }, '<A-k>', '<C-w>k')
vim.keymap.set({ 'n' }, '<A-l>', '<C-w>l')
-- [[ Basic Autocommands ]].
-- See `:h lua-guide-autocommands`, `:h autocmd`, `:h nvim_create_autocmd()`
-- Highlight when yanking (copying) text.
-- Try it with `yap` in normal mode. See `:h vim.hl.on_yank()`
vim.api.nvim_create_autocmd('TextYankPost', {
desc = 'Highlight when yanking (copying) text',
callback = function()
vim.hl.on_yank()
end,
})
-- [[ Create user commands ]]
-- See `:h nvim_create_user_command()` and `:h user-commands`
-- Create a command `:GitBlameLine` that print the git blame for the current line
vim.api.nvim_create_user_command('GitBlameLine', function()
local line_number = vim.fn.line('.') -- Get the current line number. See `:h line()`
local filename = vim.api.nvim_buf_get_name(0)
print(vim.fn.system({ 'git', 'blame', '-L', line_number .. ',+1', filename }))
end, { desc = 'Print the git blame for the current line' })
-- [[ Add optional packages ]]
-- Nvim comes bundled with a set of packages that are not enabled by
-- default. You can enable any of them by using the `:packadd` command.
-- For example, to add the "nohlsearch" package to automatically turn off search highlighting after
-- 'updatetime' and when going to insert mode
vim.cmd('packadd! nohlsearch')

View File

@@ -3,6 +3,7 @@
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
" Last Change: 2024 Apr 21 " Last Change: 2024 Apr 21
" 2024 May 24 by Riley Bruins <ribru17@gmail.com> ('commentstring') " 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") if exists("b:did_ftplugin")
finish finish
@@ -52,13 +53,19 @@ function! s:CollectPathsFromConfig() abort
endif endif
endif endif
let paths_from_config = config_json try
let paths_from_config = config_json
\ ->readfile() \ ->readfile()
\ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' }) \ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' })
\ ->join() \ ->join()
\ ->json_decode() \ ->json_decode()
\ ->get('compilerOptions', {}) \ ->get('compilerOptions', {})
\ ->get('paths', {}) \ ->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) if !empty(paths_from_config)
let b:astro_paths = paths_from_config let b:astro_paths = paths_from_config

View File

@@ -1,5 +1,5 @@
vim.keymap.set('n', 'gO', function() vim.keymap.set('n', 'gO', function()
require('vim.treesitter._headings').show_toc() require('vim.treesitter._headings').show_toc(6)
end, { buffer = 0, silent = true, desc = 'Show an Outline of the current buffer' }) end, { buffer = 0, silent = true, desc = 'Show an Outline of the current buffer' })
vim.keymap.set('n', ']]', function() vim.keymap.set('n', ']]', function()

View File

@@ -1,7 +1,8 @@
" Vim filetype plugin file " Vim filetype plugin file
" Language: gpg(1) configuration file " Language: gpg(1) configuration file
" Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-19 (simplify keywordprg #15696) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -17,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 GpgKeywordPrg command -buffer -nargs=1 GpgKeywordPrg
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+--' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'gpg' \ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+--' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'gpg'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:GpgKeywordPrg setlocal keywordprg=:GpgKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer GpgKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer GpgKeywordPrg'

View File

@@ -5,7 +5,9 @@
" Contributor: Dorai Sitaram <ds26@gte.com> " Contributor: Dorai Sitaram <ds26@gte.com>
" C.D. MacEachern <craig.daniel.maceachern@gmail.com> " C.D. MacEachern <craig.daniel.maceachern@gmail.com>
" Phạm Bình An <phambinhanctb2004@gmail.com> " Phạm Bình An <phambinhanctb2004@gmail.com>
" Last Change: 2025 Feb 27 " @konfekt
" Last Change: 2025 Apr 04
" 2025 May 06 by Vim Project update 'path' setting #17267
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -28,6 +30,7 @@ set cpo&vim
setlocal comments=:---,:-- setlocal comments=:---,:--
setlocal commentstring=--\ %s setlocal commentstring=--\ %s
setlocal formatoptions-=t formatoptions+=croql setlocal formatoptions-=t formatoptions+=croql
setlocal path-=. " Lua doesn't support importing module in path related to current file like JS
let &l:define = '\<function\|\<local\%(\s\+function\)\=' let &l:define = '\<function\|\<local\%(\s\+function\)\='
@@ -35,7 +38,7 @@ let &l:include = '\<\%(\%(do\|load\)file\|require\)\s*('
setlocal includeexpr=s:LuaInclude(v:fname) setlocal includeexpr=s:LuaInclude(v:fname)
setlocal suffixesadd=.lua setlocal suffixesadd=.lua
let b:undo_ftplugin = "setl cms< com< def< fo< inc< inex< sua<" let b:undo_ftplugin = "setl cms< com< def< fo< inc< inex< sua< pa<"
if exists("loaded_matchit") && !exists("b:match_words") if exists("loaded_matchit") && !exists("b:match_words")
let b:match_ignorecase = 0 let b:match_ignorecase = 0
@@ -78,4 +81,7 @@ function s:LuaInclude(fname) abort
return fname return fname
endfunction endfunction
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: nowrap sw=2 sts=2 ts=8 noet: " vim: nowrap sw=2 sts=2 ts=8 noet:

View File

@@ -2,7 +2,7 @@
" Language: modules.conf(5) configuration file " Language: modules.conf(5) configuration file
" Maintainer: This runtime file is looking for a new maintainer. " Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-20 (remove erroneous endif) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -19,7 +19,7 @@ setlocal formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 ModconfKeywordPrg command -buffer -nargs=1 ModconfKeywordPrg
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'modprobe.d' \ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'modprobe.d'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:ModconfKeywordPrg setlocal keywordprg=:ModconfKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ModconfKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ModconfKeywordPrg'

View File

@@ -1,7 +1,8 @@
" Vim filetype plugin file " Vim filetype plugin file
" Language: mutt RC File " Language: mutt RC File
" Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-19 (simplify keywordprg #15696) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -20,7 +21,7 @@ let &l:include = '^\s*source\>'
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 MuttrcKeywordPrg command -buffer -nargs=1 MuttrcKeywordPrg
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'muttrc' \ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'muttrc'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:MuttrcKeywordPrg setlocal keywordprg=:MuttrcKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer MuttrcKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer MuttrcKeywordPrg'

View File

@@ -5,6 +5,7 @@
" 2024 Jan 14 by Vim Project (browsefilter) " 2024 Jan 14 by Vim Project (browsefilter)
" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring') " 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
" 2024 Sep 19 by Konfekt (simplify keywordprg #15696) " 2024 Sep 19 by Konfekt (simplify keywordprg #15696)
" 2025 Jul 22 by phanium (use :hor term #17822)
" Only do this when not done yet for this buffer " Only do this when not done yet for this buffer
if exists("b:did_ftplugin") | finish | endif if exists("b:did_ftplugin") | finish | endif
@@ -51,7 +52,7 @@ endif
if exists('s:pwsh_cmd') if exists('s:pwsh_cmd')
if exists(':terminal') == 2 if exists(':terminal') == 2
command! -buffer -nargs=1 GetHelp silent exe 'term ' . s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full "<args>"' . (executable('less') ? ' | less' : '') command! -buffer -nargs=1 GetHelp silent exe 'hor term ' . s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full "<args>"' . (executable('less') ? ' | less' : '')
else else
command! -buffer -nargs=1 GetHelp echo system(s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full <args>') command! -buffer -nargs=1 GetHelp echo system(s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full <args>')
endif endif

View File

@@ -3,6 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com> " Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Last Change: 2024 Sep 19 (simplify keywordprg #15696) " Last Change: 2024 Sep 19 (simplify keywordprg #15696)
" 2024 Jul 22 by Vim project (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -36,7 +37,7 @@ endif
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 ReadlineKeywordPrg command -buffer -nargs=1 ReadlineKeywordPrg
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . '3 readline' \ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . '3 readline'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:ReadlineKeywordPrg setlocal keywordprg=:ReadlineKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ReadlineKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ReadlineKeywordPrg'

View File

@@ -7,6 +7,7 @@
" Last Change: 2024 Sep 19 by Vim Project (compiler shellcheck) " Last Change: 2024 Sep 19 by Vim Project (compiler shellcheck)
" 2024 Dec 29 by Vim Project (improve setting shellcheck compiler) " 2024 Dec 29 by Vim Project (improve setting shellcheck compiler)
" 2025 Mar 09 by Vim Project (set b:match_skip) " 2025 Mar 09 by Vim Project (set b:match_skip)
" 2025 Jul 22 by phanium (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -53,7 +54,7 @@ let s:is_kornshell = get(b:, "is_kornshell", get(g:, "is_kornshell", 0))
if s:is_bash if s:is_bash
if exists(':terminal') == 2 if exists(':terminal') == 2
command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""' command! -buffer -nargs=1 ShKeywordPrg silent exe ':hor term bash -c "help "<args>" 2>/dev/null || man "<args>""'
else else
command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || MANPAGER= man "<args>"') command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || MANPAGER= man "<args>"')
endif endif

View File

@@ -2,7 +2,7 @@
" Language: OpenSSH client configuration file " Language: OpenSSH client configuration file
" Maintainer: This runtime file is looking for a new maintainer. " Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-19 (simplify keywordprg #15696) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -17,7 +17,7 @@ let b:undo_ftplugin = 'setlocal com< cms< fo<'
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 SshconfigKeywordPrg command -buffer -nargs=1 SshconfigKeywordPrg
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '$', '\') . ''' --hilite-search" man ' . 'ssh_config' \ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '$', '\') . ''' --hilite-search" man ' . 'ssh_config'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:SshconfigKeywordPrg setlocal keywordprg=:SshconfigKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SshconfigKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SshconfigKeywordPrg'

View File

@@ -2,7 +2,7 @@
" Language: sudoers(5) configuration files " Language: sudoers(5) configuration files
" Maintainer: This runtime file is looking for a new maintainer. " Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-19 (simplify keywordprg #15696) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 SudoersKeywordPrg command -buffer -nargs=1 SudoersKeywordPrg
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('\b' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'sudoers' \ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('\b' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'sudoers'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:SudoersKeywordPrg setlocal keywordprg=:SudoersKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SudoersKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SudoersKeywordPrg'

View File

@@ -1,19 +1,18 @@
" vim: fdm=marker " Tutor filetype plugin
" Language: Tutor (the new tutor plugin)
" Maintainer: This runtime file is looking for a new maintainer.
" Last Change: 2025 May 10
" Contributors: Phạm Bình An <phambinhanctb2004@gmail.com>
" Original Author: Felipe Morales <hel.sheep@gmail.com>
" Last Change:
" 2025 May 10 set b:undo_ftplugin
" 2025 May 12 update b:undo_ftplugin
" Base: {{{1 " Base: {{{1
call tutor#SetupVim() call tutor#SetupVim()
" Buffer Settings: {{{1 " Buffer Settings: {{{1
setlocal noreadonly setlocal noreadonly
if !exists('g:tutor_debug') || g:tutor_debug == 0
setlocal buftype=nofile
setlocal concealcursor+=inv
setlocal conceallevel=2
else
setlocal buftype=
setlocal concealcursor&
setlocal conceallevel=0
endif
setlocal noundofile setlocal noundofile
setlocal keywordprg=:help setlocal keywordprg=:help
@@ -39,7 +38,7 @@ call tutor#SetNormalMappings()
sign define tutorok text=texthl=tutorOK sign define tutorok text=texthl=tutorOK
sign define tutorbad text=texthl=tutorX sign define tutorbad text=texthl=tutorX
if !exists('g:tutor_debug') || g:tutor_debug == 0 let b:undo_ftplugin = "setl foldmethod< foldexpr< foldlevel< undofile< keywordprg< iskeyword< |"
call tutor#ApplyMarks() \ . "call tutor#EnableInteractive(v:false) |"
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
endif " vim: fdm=marker

View File

@@ -2,7 +2,7 @@
" Language: udev(8) rules file " Language: udev(8) rules file
" Maintainer: This runtime file is looking for a new maintainer. " Maintainer: This runtime file is looking for a new maintainer.
" Previous Maintainer: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Revision: 2024-09-19 (simplify keywordprg #15696) " Latest Revision: 2025-07-22 (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2 if has('unix') && executable('less') && exists(':terminal') == 2
command -buffer -nargs=1 UdevrulesKeywordPrg command -buffer -nargs=1 UdevrulesKeywordPrg
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'udev' \ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'udev'
setlocal iskeyword+=- setlocal iskeyword+=-
setlocal keywordprg=:UdevrulesKeywordPrg setlocal keywordprg=:UdevrulesKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer UdevrulesKeywordPrg' let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer UdevrulesKeywordPrg'

View File

@@ -5,6 +5,8 @@
" Latest Revision: 2024 Sep 19 " Latest Revision: 2024 Sep 19
" License: Vim (see :h license) " License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-zsh " Repository: https://github.com/chrisbra/vim-zsh
" Last Change:
" 2025 Jul 23 by Vim Project (use :hor term #17822)
if exists("b:did_ftplugin") if exists("b:did_ftplugin")
finish finish
@@ -20,7 +22,7 @@ let b:undo_ftplugin = "setl com< cms< fo< "
if executable('zsh') && &shell !~# '/\%(nologin\|false\)$' if executable('zsh') && &shell !~# '/\%(nologin\|false\)$'
if exists(':terminal') == 2 if exists(':terminal') == 2
command! -buffer -nargs=1 ZshKeywordPrg silent exe ':term zsh -c "autoload -Uz run-help; run-help <args>"' command! -buffer -nargs=1 ZshKeywordPrg silent exe ':hor term zsh -c "autoload -Uz run-help; run-help <args>"'
else else
command! -buffer -nargs=1 ZshKeywordPrg echo system('MANPAGER= zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"') command! -buffer -nargs=1 ZshKeywordPrg echo system('MANPAGER= zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"')
endif endif

View File

@@ -3,6 +3,7 @@
" Maintainer: See https://github.com/HerringtonDarkholme/yats.vim " Maintainer: See https://github.com/HerringtonDarkholme/yats.vim
" Last Change: 2019 Oct 18 " Last Change: 2019 Oct 18
" 2023 Aug 28 by Vim Project (undo_indent) " 2023 Aug 28 by Vim Project (undo_indent)
" 2025 Jun 05 by Vim Project (remove Fixedgq() formatexp, #17452)
" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org " Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org
" 0. Initialization {{{1 " 0. Initialization {{{1
@@ -18,10 +19,9 @@ setlocal nosmartindent
" Now, set up our indentation expression and keys that trigger it. " Now, set up our indentation expression and keys that trigger it.
setlocal indentexpr=GetTypescriptIndent() setlocal indentexpr=GetTypescriptIndent()
setlocal formatexpr=Fixedgq(v:lnum,v:count)
setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e
let b:undo_indent = "setlocal formatexpr< indentexpr< indentkeys< smartindent<" let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<"
" Only define the function once. " Only define the function once.
if exists("*GetTypescriptIndent") if exists("*GetTypescriptIndent")
@@ -443,64 +443,3 @@ endfunction
let &cpo = s:cpo_save let &cpo = s:cpo_save
unlet s:cpo_save unlet s:cpo_save
function! Fixedgq(lnum, count)
let l:tw = &tw ? &tw : 80
let l:count = a:count
let l:first_char = indent(a:lnum) + 1
if mode() == 'i' " gq was not pressed, but tw was set
return 1
endif
" This gq is only meant to do code with strings, not comments
if s:IsLineComment(a:lnum, l:first_char) || s:IsInMultilineComment(a:lnum, l:first_char)
return 1
endif
if len(getline(a:lnum)) < l:tw && l:count == 1 " No need for gq
return 1
endif
" Put all the lines on one line and do normal splitting after that
if l:count > 1
while l:count > 1
let l:count -= 1
normal J
endwhile
endif
let l:winview = winsaveview()
call cursor(a:lnum, l:tw + 1)
let orig_breakpoint = searchpairpos(' ', '', '\.', 'bcW', '', a:lnum)
call cursor(a:lnum, l:tw + 1)
let breakpoint = searchpairpos(' ', '', '\.', 'bcW', s:skip_expr, a:lnum)
" No need for special treatment, normal gq handles edgecases better
if breakpoint[1] == orig_breakpoint[1]
call winrestview(l:winview)
return 1
endif
" Try breaking after string
if breakpoint[1] <= indent(a:lnum)
call cursor(a:lnum, l:tw + 1)
let breakpoint = searchpairpos('\.', '', ' ', 'cW', s:skip_expr, a:lnum)
endif
if breakpoint[1] != 0
call feedkeys("r\<CR>")
else
let l:count = l:count - 1
endif
" run gq on new lines
if l:count == 1
call feedkeys("gqq")
endif
return 0
endfunction

View File

@@ -245,7 +245,7 @@ local function parse_line(line)
end end
--- @type string? --- @type string?
local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$') local glob = line:match('^%s*%[(.*)%]%s*$')
if glob then if glob then
return glob return glob
end end

View File

@@ -8,6 +8,10 @@ local M = {}
--- @param env? table<string,string|number> --- @param env? table<string,string|number>
--- @return string --- @return string
local function system(cmd, silent, env) local function system(cmd, silent, env)
if vim.fn.executable(cmd[1]) == 0 then
error(string.format('executable not found: "%s"', cmd[1]), 0)
end
local r = vim.system(cmd, { env = env, timeout = 10000 }):wait() local r = vim.system(cmd, { env = env, timeout = 10000 }):wait()
if not silent then if not silent then
@@ -577,7 +581,10 @@ function M.man_complete(arg_lead, cmd_line)
return {} return {}
end end
local pages = get_paths(name, sect) local ok, pages = pcall(get_paths, name, sect)
if not ok then
return nil
end
-- We check for duplicates in case the same manpage in different languages -- We check for duplicates in case the same manpage in different languages
-- was found. -- was found.
@@ -803,6 +810,8 @@ function M.show_toc()
fn.setloclist(0, {}, 'a', { title = 'Table of contents' }) fn.setloclist(0, {}, 'a', { title = 'Table of contents' })
vim.cmd.lopen() vim.cmd.lopen()
vim.w.qf_toc = bufname vim.w.qf_toc = bufname
-- reload syntax file after setting qf_toc variable
vim.bo.filetype = 'qf'
end end
return M return M

View File

@@ -0,0 +1,86 @@
---@class nvim.TutorMetadata
---@field expect table<string, string|-1>
---@alias nvim.TutorExtmarks table<string, string>
---@type nvim.TutorExtmarks?
vim.b.tutor_extmarks = vim.b.tutor_extmarks
---@type nvim.TutorMetadata?
vim.b.tutor_metadata = vim.b.tutor_metadata
local sign_text_correct = ''
local sign_text_incorrect = ''
local tutor_mark_ns = vim.api.nvim_create_namespace('nvim.tutor.mark')
local tutor_hl_ns = vim.api.nvim_create_namespace('nvim.tutor.hl')
local M = {}
---@param line integer 1-based
local function check_line(line)
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
local ctext = vim.fn.getline(line)
---@type vim.api.keyset.get_extmark_item[]
local extmarks = vim
.iter(vim.api.nvim_buf_get_extmarks(
0,
tutor_mark_ns,
{ line - 1, 0 },
{ line - 1, -1 }, -- the extmark can move to col > 0 if users insert text there
{ details = true }
))
:filter(function(extmark)
return not extmark[4].invalid
end)
:totable()
for _, extmark in ipairs(extmarks) do
local mark_id = extmark[1]
local expct = vim.b.tutor_extmarks[tostring(mark_id)]
local expect = vim.b.tutor_metadata.expect[expct]
local is_correct = expect == -1 or ctext == expect
vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, line - 1, 0, {
id = mark_id,
sign_text = is_correct and sign_text_correct or sign_text_incorrect,
sign_hl_group = is_correct and 'tutorOK' or 'tutorX',
invalidate = true,
})
end
end
end
function M.apply_marks()
vim.cmd [[hi! link tutorExpect Special]]
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect then
vim.b.tutor_extmarks = {}
for expct, _ in pairs(vim.b.tutor_metadata.expect) do
---@diagnostic disable-next-line: assign-type-mismatch
local lnum = tonumber(expct) ---@type integer
vim.api.nvim_buf_set_extmark(0, tutor_hl_ns, lnum - 1, 0, {
line_hl_group = 'tutorExpect',
invalidate = true,
})
local mark_id = vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, lnum - 1, 0, {})
-- Cannot edit field of a Vimscript dictionary from Lua directly, see `:h lua-vim-variables`
---@type nvim.TutorExtmarks
local tutor_extmarks = vim.b.tutor_extmarks
tutor_extmarks[tostring(mark_id)] = expct
vim.b.tutor_extmarks = tutor_extmarks
check_line(lnum)
end
end
end
function M.apply_marks_on_changed()
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
local lnum = vim.fn.line('.')
check_line(lnum)
end
end
return M

View File

@@ -191,7 +191,7 @@ do
--- client is attached. If no client is attached, or if a server does not support a capability, an --- client is attached. If no client is attached, or if a server does not support a capability, an
--- error message is displayed rather than exhibiting different behavior. --- error message is displayed rather than exhibiting different behavior.
--- ---
--- See |grr|, |grn|, |gra|, |gri|, |gO|, |i_CTRL-S|. --- See |grr|, |grn|, |gra|, |gri|, |grt| |gO|, |i_CTRL-S|.
do do
vim.keymap.set('n', 'grn', function() vim.keymap.set('n', 'grn', function()
vim.lsp.buf.rename() vim.lsp.buf.rename()
@@ -209,6 +209,10 @@ do
vim.lsp.buf.implementation() vim.lsp.buf.implementation()
end, { desc = 'vim.lsp.buf.implementation()' }) end, { desc = 'vim.lsp.buf.implementation()' })
vim.keymap.set('n', 'grt', function()
vim.lsp.buf.type_definition()
end, { desc = 'vim.lsp.buf.type_definition()' })
vim.keymap.set('n', 'gO', function() vim.keymap.set('n', 'gO', function()
vim.lsp.buf.document_symbol() vim.lsp.buf.document_symbol()
end, { desc = 'vim.lsp.buf.document_symbol()' }) end, { desc = 'vim.lsp.buf.document_symbol()' })
@@ -622,10 +626,10 @@ do
end end
vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC' vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC'
vim.keymap.set('n', '[[', function() vim.keymap.set({ 'n', 'x', 'o' }, '[[', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, -vim.v.count1) jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, -vim.v.count1)
end, { buffer = args.buf, desc = 'Jump [count] shell prompts backward' }) end, { buffer = args.buf, desc = 'Jump [count] shell prompts backward' })
vim.keymap.set('n', ']]', function() vim.keymap.set({ 'n', 'x', 'o' }, ']]', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, vim.v.count1) jump_to_prompt(nvim_terminal_prompt_ns, 0, args.buf, vim.v.count1)
end, { buffer = args.buf, desc = 'Jump [count] shell prompts forward' }) end, { buffer = args.buf, desc = 'Jump [count] shell prompts forward' })
end, end,

View File

@@ -106,7 +106,7 @@ local utfs = {
--- - env: table<string,string> Set environment variables for the new process. Inherits the --- - env: table<string,string> Set environment variables for the new process. Inherits the
--- current environment with `NVIM` set to |v:servername|. --- current environment with `NVIM` set to |v:servername|.
--- - clear_env: (boolean) `env` defines the job environment exactly, instead of merging current --- - clear_env: (boolean) `env` defines the job environment exactly, instead of merging current
--- environment. --- environment. Note: if `env` is `nil`, the current environment is used but without `NVIM` set.
--- - stdin: (string|string[]|boolean) If `true`, then a pipe to stdin is opened and can be written --- - stdin: (string|string[]|boolean) If `true`, then a pipe to stdin is opened and can be written
--- to via the `write()` method to SystemObj. If string or string[] then will be written to stdin --- to via the `write()` method to SystemObj. If string or string[] then will be written to stdin
--- and closed. Defaults to `false`. --- and closed. Defaults to `false`.
@@ -399,22 +399,23 @@ local VIM_CMD_ARG_MAX = 20
--- Executes Vimscript (|Ex-commands|). --- Executes Vimscript (|Ex-commands|).
--- ---
--- Note that `vim.cmd` can be indexed with a command name to return a callable function to the --- Can be indexed with a command name to get a function, thus you can write `vim.cmd.echo(…)`
--- command. --- instead of `vim.cmd{cmd='echo',…}`.
--- ---
--- Example: --- Examples:
--- ---
--- ```lua --- ```lua
--- -- Single command:
--- vim.cmd('echo 42') --- vim.cmd('echo 42')
--- -- Multiline script:
--- vim.cmd([[ --- vim.cmd([[
--- augroup My_group --- augroup my.group
--- autocmd! --- autocmd!
--- autocmd FileType c setlocal cindent --- autocmd FileType c setlocal cindent
--- augroup END --- augroup END
--- ]]) --- ]])
--- ---
--- -- Ex command :echo "foo" --- -- Ex command :echo "foo". Note: string literals must be double-quoted.
--- -- Note string literals need to be double quoted.
--- vim.cmd('echo "foo"') --- vim.cmd('echo "foo"')
--- vim.cmd { cmd = 'echo', args = { '"foo"' } } --- vim.cmd { cmd = 'echo', args = { '"foo"' } }
--- vim.cmd.echo({ args = { '"foo"' } }) --- vim.cmd.echo({ args = { '"foo"' } })
@@ -422,22 +423,19 @@ local VIM_CMD_ARG_MAX = 20
--- ---
--- -- Ex command :write! myfile.txt --- -- Ex command :write! myfile.txt
--- vim.cmd('write! myfile.txt') --- vim.cmd('write! myfile.txt')
--- vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true } --- vim.cmd { cmd = 'write', args = { 'myfile.txt' }, bang = true }
--- vim.cmd.write { args = { "myfile.txt" }, bang = true } --- vim.cmd.write { args = { 'myfile.txt' }, bang = true }
--- vim.cmd.write { "myfile.txt", bang = true } --- vim.cmd.write { 'myfile.txt', bang = true }
--- ---
--- -- Ex command :colorscheme blue --- -- Ex command :vertical resize +2
--- vim.cmd('colorscheme blue') --- vim.cmd.resize({ '+2', mods = { vertical = true } })
--- vim.cmd.colorscheme('blue')
--- ``` --- ```
--- ---
---@diagnostic disable-next-line: undefined-doc-param ---@diagnostic disable-next-line: undefined-doc-param
---@param command string|table Command(s) to execute. ---@param command string|table Command(s) to execute.
--- If a string, executes multiple lines of Vimscript at once. In this --- - The string form supports multiline Vimscript (alias to |nvim_exec2()|, behaves
--- case, it is an alias to |nvim_exec2()|, where `opts.output` is set --- like |:source|).
--- to false. Thus it works identical to |:source|. --- - The table form executes a single command (alias to |nvim_cmd()|).
--- If a table, executes a single command. In this case, it is an alias
--- to |nvim_cmd()| where `opts` is empty.
---@see |ex-cmd-index| ---@see |ex-cmd-index|
vim.cmd = setmetatable({}, { vim.cmd = setmetatable({}, {
__call = function(_, command) __call = function(_, command)

View File

@@ -3,13 +3,15 @@ local M = {}
--- @param module string --- @param module string
---@return string ---@return string
function M.includeexpr(module) function M.includeexpr(module)
local fname = module:gsub('%.', '/') module = module:gsub('%.', '/')
local root = vim.fs.root(vim.api.nvim_buf_get_name(0), 'lua') or vim.fn.getcwd() local root = vim.fs.root(vim.api.nvim_buf_get_name(0), 'lua') or vim.fn.getcwd()
for _, suf in ipairs { '.lua', '/init.lua' } do for _, fname in ipairs { module, vim.fs.joinpath(root, 'lua', module) } do
local path = vim.fs.joinpath(root, 'lua', fname .. suf) for _, suf in ipairs { '.lua', '/init.lua' } do
if vim.uv.fs_stat(path) then local path = fname .. suf
return path if vim.uv.fs_stat(path) then
return path
end
end end
end end

View File

@@ -200,7 +200,9 @@ function vim.show_pos(bufnr, row, col, filter)
capture, capture,
string.format( string.format(
'priority: %d language: %s', 'priority: %d language: %s',
capture.metadata.priority or vim.hl.priorities.treesitter, capture.metadata.priority
or (capture.metadata[capture.id] and capture.metadata[capture.id].priority)
or vim.hl.priorities.treesitter,
capture.lang capture.lang
) )
) )

View File

@@ -369,8 +369,11 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {}) --- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
--- ``` --- ```
--- ---
--- If `end` is less than `start`, traversal works backwards. (Useful --- If `end` is less than `start`, marks are returned in reverse order.
--- with `limit`, to get the first marks prior to a given position.) --- (Useful with `limit`, to get the first marks prior to a given position.)
---
--- Note: For a reverse range, `limit` does not actually affect the traversed
--- range, just how many marks are returned
--- ---
--- Note: when using extmark ranges (marks with a end_row/end_col position) --- Note: when using extmark ranges (marks with a end_row/end_col position)
--- the `overlap` option might be useful. Otherwise only the start position --- the `overlap` option might be useful. Otherwise only the start position
@@ -939,8 +942,8 @@ function vim.api.nvim_create_augroup(name, opts) end
--- - desc (string) optional: description (for documentation and troubleshooting). --- - desc (string) optional: description (for documentation and troubleshooting).
--- - callback (function|string) optional: Lua function (or Vimscript function name, if --- - callback (function|string) optional: Lua function (or Vimscript function name, if
--- string) called when the event(s) is triggered. Lua callback can return a truthy --- string) called when the event(s) is triggered. Lua callback can return a truthy
--- value (not `false` or `nil`) to delete the autocommand. Receives one argument, --- value (not `false` or `nil`) to delete the autocommand, and receives one argument, a
--- a table with these keys: [event-args]() --- table with these keys: [event-args]()
--- - id: (number) autocommand id --- - id: (number) autocommand id
--- - event: (string) name of the triggered event `autocmd-events` --- - event: (string) name of the triggered event `autocmd-events`
--- - group: (number|nil) autocommand group id, if any --- - group: (number|nil) autocommand group id, if any
@@ -1659,10 +1662,7 @@ function vim.api.nvim_notify(msg, log_level, opts) end
--- ---
--- ```lua --- ```lua
--- vim.api.nvim_create_user_command('TermHl', function() --- vim.api.nvim_create_user_command('TermHl', function()
--- local b = vim.api.nvim_create_buf(false, true) --- vim.api.nvim_open_term(0, {})
--- local chan = vim.api.nvim_open_term(b, {})
--- vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
--- vim.api.nvim_win_set_buf(0, b)
--- end, { desc = 'Highlights ANSI termcodes in curbuf' }) --- end, { desc = 'Highlights ANSI termcodes in curbuf' })
--- ``` --- ```
--- ---
@@ -1840,7 +1840,8 @@ function vim.api.nvim_open_term(buffer, opts) end
--- the call. --- the call.
--- - fixed: If true when anchor is NW or SW, the float window --- - fixed: If true when anchor is NW or SW, the float window
--- would be kept fixed even if the window would be truncated. --- would be kept fixed even if the window would be truncated.
--- - hide: If true the floating window will be hidden. --- - hide: If true the floating window will be hidden and the cursor will be invisible when
--- focused on it.
--- - vertical: Split vertically `:vertical`. --- - vertical: Split vertically `:vertical`.
--- - split: Split direction: "left", "right", "above", "below". --- - split: Split direction: "left", "right", "above", "below".
--- @return integer # |window-ID|, or 0 on error --- @return integer # |window-ID|, or 0 on error

View File

@@ -184,14 +184,14 @@
--- @field signs vim.fn.sign[] --- @field signs vim.fn.sign[]
--- @class vim.fn.sign_place.dict --- @class vim.fn.sign_place.dict
--- @field lnum? integer --- @field lnum? integer|string
--- @field priority? integer --- @field priority? integer
--- @class vim.fn.sign_placelist.list.item --- @class vim.fn.sign_placelist.list.item
--- @field buffer integer|string --- @field buffer integer|string
--- @field group? string --- @field group? string
--- @field id? integer --- @field id? integer
--- @field lnum integer --- @field lnum? integer|string
--- @field name string --- @field name string
--- @field priority? integer --- @field priority? integer
@@ -297,3 +297,18 @@
--- A list of dictionaries with information about --- A list of dictionaries with information about
--- undo blocks. --- undo blocks.
--- @field entries vim.fn.undotree.entry[] --- @field entries vim.fn.undotree.entry[]
--- @class vim.fn.winlayout.leaf
--- @field [1] "leaf" Node type
--- @field [2] integer winid
--- @class vim.fn.winlayout.branch
--- @field [1] "row" | "col" Node type
--- @field [2] (vim.fn.winlayout.leaf|vim.fn.winlayout.branch)[] children
--- @class vim.fn.winlayout.empty
--- @alias vim.fn.winlayout.ret
--- | vim.fn.winlayout.leaf
--- | vim.fn.winlayout.branch
--- | vim.fn.winlayout.empty

View File

@@ -655,8 +655,8 @@ vim.bo.bl = vim.bo.buflisted
--- "acwrite" implies that the buffer name is not related to a file, like --- "acwrite" implies that the buffer name is not related to a file, like
--- "nofile", but it will be written. Thus, in contrast to "nofile" and --- "nofile", but it will be written. Thus, in contrast to "nofile" and
--- "nowrite", ":w" does work and a modified buffer can't be abandoned --- "nowrite", ":w" does work and a modified buffer can't be abandoned
--- without saving. For writing there must be matching `BufWriteCmd|, --- without saving. For writing there must be matching `BufWriteCmd`,
--- |FileWriteCmd` or `FileAppendCmd` autocommands. --- `FileWriteCmd` or `FileAppendCmd` autocommands.
--- ---
--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt' --- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt'
vim.o.buftype = "" vim.o.buftype = ""
@@ -2015,10 +2015,21 @@ vim.bo.et = vim.bo.expandtab
--- in the `trust` list. Use `:trust` to manage trusted files. See also --- in the `trust` list. Use `:trust` to manage trusted files. See also
--- `vim.secure.read()`. --- `vim.secure.read()`.
--- ---
--- To get its own location, Lua exrc files can use `debug.getinfo()`.
---
--- Compare 'exrc' to `editorconfig`: --- Compare 'exrc' to `editorconfig`:
--- - 'exrc' can execute any code; editorconfig only specifies settings. --- - 'exrc' can execute any code; editorconfig only specifies settings.
--- - 'exrc' is Nvim-specific; editorconfig works in other editors. --- - 'exrc' is Nvim-specific; editorconfig works in other editors.
--- ---
--- To achieve project-local LSP configuration:
--- 1. Enable 'exrc'.
--- 2. Place LSP configs at ".nvim/lsp/*.lua" in your project root.
--- 3. Create ".nvim.lua" in your project root directory with this line:
---
--- ```lua
--- vim.cmd[[set runtimepath+=.nvim]]
--- ```
---
--- This option cannot be set from a `modeline` or in the `sandbox`, for --- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons. --- security reasons.
--- ---
@@ -3151,8 +3162,8 @@ vim.o.ims = vim.o.imsearch
vim.bo.imsearch = vim.o.imsearch vim.bo.imsearch = vim.o.imsearch
vim.bo.ims = vim.bo.imsearch vim.bo.ims = vim.bo.imsearch
--- When nonempty, shows the effects of `:substitute`, `:smagic|, --- When nonempty, shows the effects of `:substitute`, `:smagic`,
--- |:snomagic` and user commands with the `:command-preview` flag as you --- `:snomagic` and user commands with the `:command-preview` flag as you
--- type. --- type.
--- ---
--- Possible values: --- Possible values:
@@ -3491,8 +3502,8 @@ vim.go.js = vim.go.joinspaces
--- when navigating backwards in the jumplist and then --- when navigating backwards in the jumplist and then
--- jumping to a location. `jumplist-stack` --- jumping to a location. `jumplist-stack`
--- ---
--- view When moving through the jumplist, `changelist|, --- view When moving through the jumplist, `changelist`,
--- |alternate-file` or using `mark-motions` try to --- `alternate-file` or using `mark-motions` try to
--- restore the `mark-view` in which the action occurred. --- restore the `mark-view` in which the action occurred.
--- ---
--- clean Remove unloaded buffers from the jumplist. --- clean Remove unloaded buffers from the jumplist.
@@ -5239,9 +5250,14 @@ vim.go.sect = vim.go.sections
--- the end of line the line break still isn't included. --- the end of line the line break still isn't included.
--- When "exclusive" is used, cursor position in visual mode will be --- When "exclusive" is used, cursor position in visual mode will be
--- adjusted for inclusive motions `inclusive-motion-selection-exclusive`. --- adjusted for inclusive motions `inclusive-motion-selection-exclusive`.
--- Note that when "exclusive" is used and selecting from the end ---
--- backwards, you cannot include the last character of a line, when --- Note:
--- starting in Normal mode and 'virtualedit' empty. --- - When "exclusive" is used and selecting from the end backwards, you
--- cannot include the last character of a line, when starting in Normal
--- mode and 'virtualedit' empty.
--- - when "exclusive" is used with a single character visual selection,
--- Vim will behave as if the 'selection' is inclusive (in other words,
--- you cannot visually select an empty region).
--- ---
--- @type 'inclusive'|'exclusive'|'old' --- @type 'inclusive'|'exclusive'|'old'
vim.o.selection = "inclusive" vim.o.selection = "inclusive"
@@ -5627,8 +5643,8 @@ vim.go.ssl = vim.go.shellslash
--- and the 'shell' command does not need to support redirection. --- and the 'shell' command does not need to support redirection.
--- The advantage of using a temp file is that the file type and encoding --- The advantage of using a temp file is that the file type and encoding
--- can be detected. --- can be detected.
--- The `FilterReadPre`, `FilterReadPost` and `FilterWritePre|, --- The `FilterReadPre`, `FilterReadPost` and `FilterWritePre`,
--- |FilterWritePost` autocommands event are not triggered when --- `FilterWritePost` autocommands event are not triggered when
--- 'shelltemp' is off. --- 'shelltemp' is off.
--- `system()` does not respect this option, it always uses pipes. --- `system()` does not respect this option, it always uses pipes.
--- ---
@@ -6198,8 +6214,8 @@ vim.bo.spo = vim.bo.spelloptions
--- ---
--- expr:{expr} Evaluate expression {expr}. Use a function to avoid --- expr:{expr} Evaluate expression {expr}. Use a function to avoid
--- trouble with spaces. Best is to call a function --- trouble with spaces. Best is to call a function
--- without arguments, see `expr-option-function|. --- without arguments, see `expr-option-function`.
--- |v:val` holds the badly spelled word. The expression --- `v:val` holds the badly spelled word. The expression
--- must evaluate to a List of Lists, each with a --- must evaluate to a List of Lists, each with a
--- suggestion and a score. --- suggestion and a score.
--- Example: --- Example:
@@ -6343,8 +6359,7 @@ vim.o.stc = vim.o.statuscolumn
vim.wo.statuscolumn = vim.o.statuscolumn vim.wo.statuscolumn = vim.o.statuscolumn
vim.wo.stc = vim.wo.statuscolumn vim.wo.stc = vim.wo.statuscolumn
--- When non-empty, this option determines the content of the status line. --- Sets the `status-line`.
--- Also see `status-line`.
--- ---
--- The option consists of printf style '%' items interspersed with --- The option consists of printf style '%' items interspersed with
--- normal text. Each status line item is of the form: --- normal text. Each status line item is of the form:
@@ -7834,14 +7849,15 @@ vim.wo.winbl = vim.wo.winblend
--- Defines the default border style of floating windows. The default value --- Defines the default border style of floating windows. The default value
--- is empty, which is equivalent to "none". Valid values include: --- is empty, which is equivalent to "none". Valid values include:
--- - "bold": Bold line box.
--- - "double": Double-line box.
--- - "none": No border. --- - "none": No border.
--- - "single": A single line box.
--- - "double": A double line box.
--- - "rounded": Like "single", but with rounded corners ("╭" etc.). --- - "rounded": Like "single", but with rounded corners ("╭" etc.).
--- - "shadow": Drop shadow effect, by blending with the background.
--- - "single": Single-line box.
--- - "solid": Adds padding by a single whitespace cell. --- - "solid": Adds padding by a single whitespace cell.
--- - "shadow": A drop shadow effect by blending with the background.
--- ---
--- @type ''|'double'|'single'|'shadow'|'rounded'|'solid'|'none' --- @type ''|'double'|'single'|'shadow'|'rounded'|'solid'|'bold'|'none'
vim.o.winborder = "" vim.o.winborder = ""
vim.go.winborder = vim.o.winborder vim.go.winborder = vim.o.winborder

View File

@@ -85,7 +85,7 @@ function vim.fn.api_info() end
--- let failed = append(0, ["Chapter 1", "the beginning"]) --- let failed = append(0, ["Chapter 1", "the beginning"])
--- < --- <
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param text string|string[] --- @param text string|string[]
--- @return 0|1 --- @return 0|1
function vim.fn.append(lnum, text) end function vim.fn.append(lnum, text) end
@@ -907,7 +907,7 @@ function vim.fn.chdir(dir) end
--- ---
--- To get or set indent of lines in a string, see |vim.text.indent()|. --- To get or set indent of lines in a string, see |vim.text.indent()|.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.cindent(lnum) end function vim.fn.cindent(lnum) end
@@ -1244,7 +1244,7 @@ function vim.fn.ctxset(context, index) end
--- @return any --- @return any
function vim.fn.ctxsize() end function vim.fn.ctxsize() end
--- @param lnum integer --- @param lnum integer|string
--- @param col? integer --- @param col? integer
--- @param off? integer --- @param off? integer
--- @return any --- @return any
@@ -1441,7 +1441,7 @@ function vim.fn.did_filetype() end
--- line, "'m" mark m, etc. --- line, "'m" mark m, etc.
--- Returns 0 if the current window is not in diff mode. --- Returns 0 if the current window is not in diff mode.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.diff_filler(lnum) end function vim.fn.diff_filler(lnum) end
@@ -1455,7 +1455,7 @@ function vim.fn.diff_filler(lnum) end
--- The highlight ID can be used with |synIDattr()| to obtain --- The highlight ID can be used with |synIDattr()| to obtain
--- syntax information about the highlighting. --- syntax information about the highlighting.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param col integer --- @param col integer
--- @return any --- @return any
function vim.fn.diff_hlID(lnum, col) end function vim.fn.diff_hlID(lnum, col) end
@@ -1801,7 +1801,8 @@ function vim.fn.exp(expr) end
--- <SID> "<SNR>123_" where "123" is the --- <SID> "<SNR>123_" where "123" is the
--- current script ID |<SID>| --- current script ID |<SID>|
--- <script> sourced script file, or script file --- <script> sourced script file, or script file
--- where the current function was defined --- where the current function was defined.
--- Use |debug.getinfo()| in Lua scripts.
--- <stack> call stack --- <stack> call stack
--- <cword> word under the cursor --- <cword> word under the cursor
--- <cWORD> WORD under the cursor --- <cWORD> WORD under the cursor
@@ -2282,7 +2283,7 @@ function vim.fn.fnamemodify(fname, mods) end
--- {lnum} is used like with |getline()|. Thus "." is the current --- {lnum} is used like with |getline()|. Thus "." is the current
--- line, "'m" mark m, etc. --- line, "'m" mark m, etc.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.foldclosed(lnum) end function vim.fn.foldclosed(lnum) end
@@ -2292,7 +2293,7 @@ function vim.fn.foldclosed(lnum) end
--- {lnum} is used like with |getline()|. Thus "." is the current --- {lnum} is used like with |getline()|. Thus "." is the current
--- line, "'m" mark m, etc. --- line, "'m" mark m, etc.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.foldclosedend(lnum) end function vim.fn.foldclosedend(lnum) end
@@ -2307,7 +2308,7 @@ function vim.fn.foldclosedend(lnum) end
--- {lnum} is used like with |getline()|. Thus "." is the current --- {lnum} is used like with |getline()|. Thus "." is the current
--- line, "'m" mark m, etc. --- line, "'m" mark m, etc.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.foldlevel(lnum) end function vim.fn.foldlevel(lnum) end
@@ -2338,7 +2339,7 @@ function vim.fn.foldtext() end
--- line, "'m" mark m, etc. --- line, "'m" mark m, etc.
--- Useful when exporting folded text, e.g., to HTML. --- Useful when exporting folded text, e.g., to HTML.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return string --- @return string
function vim.fn.foldtextresult(lnum) end function vim.fn.foldtextresult(lnum) end
@@ -3284,7 +3285,7 @@ function vim.fn.getjumplist(winnr, tabnr) end
--- @return string --- @return string
function vim.fn.getline(lnum, end_) end function vim.fn.getline(lnum, end_) end
--- @param lnum integer --- @param lnum integer|string
--- @param end_ true|number|string|table --- @param end_ true|number|string|table
--- @return string|string[] --- @return string|string[]
function vim.fn.getline(lnum, end_) end function vim.fn.getline(lnum, end_) end
@@ -3692,6 +3693,10 @@ function vim.fn.getreginfo(regname) end
--- - It is evaluated in current window context, which makes a --- - It is evaluated in current window context, which makes a
--- difference if the buffer is displayed in a window with --- difference if the buffer is displayed in a window with
--- different 'virtualedit' or 'list' values. --- different 'virtualedit' or 'list' values.
--- - When specifying an exclusive selection and {pos1} and {pos2}
--- are equal, the returned list contains a single character as
--- if selection is inclusive, to match the behavior of an empty
--- exclusive selection in Visual mode.
--- ---
--- Examples: >vim --- Examples: >vim
--- xnoremap <CR> --- xnoremap <CR>
@@ -5170,7 +5175,7 @@ function vim.fn.line(expr, winid) end
--- |getline()|. When {lnum} is invalid -1 is returned. --- |getline()|. When {lnum} is invalid -1 is returned.
--- Also see |byte2line()|, |go| and |:goto|. --- Also see |byte2line()|, |go| and |:goto|.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.line2byte(lnum) end function vim.fn.line2byte(lnum) end
@@ -5180,7 +5185,7 @@ function vim.fn.line2byte(lnum) end
--- relevant. {lnum} is used just like in |getline()|. --- relevant. {lnum} is used just like in |getline()|.
--- When {lnum} is invalid, -1 is returned. --- When {lnum} is invalid, -1 is returned.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.lispindent(lnum) end function vim.fn.lispindent(lnum) end
@@ -5479,7 +5484,7 @@ function vim.fn.mapnew(expr1, expr2) end
--- @param mode string --- @param mode string
--- @param abbr? boolean --- @param abbr? boolean
--- @param dict? boolean --- @param dict? table<string,any>
--- @return any --- @return any
function vim.fn.mapset(mode, abbr, dict) end function vim.fn.mapset(mode, abbr, dict) end
@@ -5519,7 +5524,7 @@ function vim.fn.mapset(mode, abbr, dict) end
--- endfor --- endfor
--- < --- <
--- ---
--- @param dict boolean --- @param dict table<string,any>
--- @return any --- @return any
function vim.fn.mapset(dict) end function vim.fn.mapset(dict) end
@@ -6352,7 +6357,7 @@ function vim.fn.msgpackparse(data) end
--- {lnum} is used like with |getline()|. --- {lnum} is used like with |getline()|.
--- See also |prevnonblank()|. --- See also |prevnonblank()|.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.nextnonblank(lnum) end function vim.fn.nextnonblank(lnum) end
@@ -6451,7 +6456,7 @@ function vim.fn.pow(x, y) end
--- {lnum} is used like with |getline()|. --- {lnum} is used like with |getline()|.
--- Also see |nextnonblank()|. --- Also see |nextnonblank()|.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @return integer --- @return integer
function vim.fn.prevnonblank(lnum) end function vim.fn.prevnonblank(lnum) end
@@ -6717,7 +6722,8 @@ function vim.fn.prevnonblank(lnum) end
--- < 1.41 --- < 1.41
--- ---
--- You will get an overflow error |E1510|, when the field-width --- You will get an overflow error |E1510|, when the field-width
--- or precision will result in a string longer than 6400 chars. --- or precision will result in a string longer than 1 MB
--- (1024*1024 = 1048576) chars.
--- ---
--- *E1500* --- *E1500*
--- You cannot mix positional and non-positional arguments: >vim --- You cannot mix positional and non-positional arguments: >vim
@@ -8060,7 +8066,7 @@ function vim.fn.setcmdline(str, pos) end
--- @return any --- @return any
function vim.fn.setcmdpos(pos) end function vim.fn.setcmdpos(pos) end
--- @param lnum integer --- @param lnum integer|string
--- @param col? integer --- @param col? integer
--- @param off? integer --- @param off? integer
--- @return any --- @return any
@@ -8139,7 +8145,7 @@ function vim.fn.setfperm(fname, mode) end
--- ---
--- <Note: The '[ and '] marks are not set. --- <Note: The '[ and '] marks are not set.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param text any --- @param text any
--- @return any --- @return any
function vim.fn.setline(lnum, text) end function vim.fn.setline(lnum, text) end
@@ -9895,7 +9901,7 @@ function vim.fn.swapname(buf) end
--- echo synIDattr(synID(line("."), col("."), 1), "name") --- echo synIDattr(synID(line("."), col("."), 1), "name")
--- < --- <
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param col integer --- @param col integer
--- @param trans 0|1 --- @param trans 0|1
--- @return integer --- @return integer
@@ -9991,7 +9997,7 @@ function vim.fn.synIDtrans(synID) end
--- since syntax and matching highlighting are two different --- since syntax and matching highlighting are two different
--- mechanisms |syntax-vs-match|. --- mechanisms |syntax-vs-match|.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param col integer --- @param col integer
--- @return [integer, string, integer] --- @return [integer, string, integer]
function vim.fn.synconcealed(lnum, col) end function vim.fn.synconcealed(lnum, col) end
@@ -10014,7 +10020,7 @@ function vim.fn.synconcealed(lnum, col) end
--- character in a line and the first column in an empty line are --- character in a line and the first column in an empty line are
--- valid positions. --- valid positions.
--- ---
--- @param lnum integer --- @param lnum integer|string
--- @param col integer --- @param col integer
--- @return integer[] --- @return integer[]
function vim.fn.synstack(lnum, col) end function vim.fn.synstack(lnum, col) end
@@ -10878,14 +10884,13 @@ function vim.fn.wincol() end
--- @return string --- @return string
function vim.fn.windowsversion() end function vim.fn.windowsversion() end
--- The result is a Number, which is the height of window {nr}. --- Gets the height of |window-ID| {nr} (zero for "current
--- {nr} can be the window number or the |window-ID|. --- window"), excluding any 'winbar' and 'statusline'. Returns -1
--- When {nr} is zero, the height of the current window is --- if window {nr} doesn't exist. An existing window always has
--- returned. When window {nr} doesn't exist, -1 is returned. --- a height of zero or more.
--- An existing window always has a height of zero or more. ---
--- This excludes any window toolbar line.
--- Examples: >vim --- Examples: >vim
--- echo "The current window has " .. winheight(0) .. " lines." --- echo "Current window has " .. winheight(0) .. " lines."
--- < --- <
--- ---
--- @param nr integer --- @param nr integer
@@ -10929,7 +10934,7 @@ function vim.fn.winheight(nr) end
--- < --- <
--- ---
--- @param tabnr? integer --- @param tabnr? integer
--- @return any[] --- @return vim.fn.winlayout.ret
function vim.fn.winlayout(tabnr) end function vim.fn.winlayout(tabnr) end
--- The result is a Number, which is the screen line of the cursor --- The result is a Number, which is the screen line of the cursor
@@ -11037,18 +11042,21 @@ function vim.fn.winrestview(dict) end
--- @return vim.fn.winsaveview.ret --- @return vim.fn.winsaveview.ret
function vim.fn.winsaveview() end function vim.fn.winsaveview() end
--- The result is a Number, which is the width of window {nr}. --- Gets the width of |window-ID| {nr} (zero for "current
--- {nr} can be the window number or the |window-ID|. --- window"), including columns (|sign-column|, 'statuscolumn',
--- When {nr} is zero, the width of the current window is --- etc.). Returns -1 if window {nr} doesn't exist. An existing
--- returned. When window {nr} doesn't exist, -1 is returned. --- window always has a width of zero or more.
--- An existing window always has a width of zero or more. ---
--- Examples: >vim --- Example: >vim
--- echo "The current window has " .. winwidth(0) .. " columns." --- echo "Current window has " .. winwidth(0) .. " columns."
--- if winwidth(0) <= 50 --- if winwidth(0) <= 50
--- 50 wincmd | --- 50 wincmd |
--- endif --- endif
--- <For getting the terminal or screen size, see the 'columns' --- <
--- option. --- To get the buffer "viewport", use |getwininfo()|: >vim
--- :echo getwininfo(win_getid())[0].width - getwininfo(win_getid())[0].textoff
--- <
--- To get the Nvim screen size, see the 'columns' option.
--- ---
--- @param nr integer --- @param nr integer
--- @return integer --- @return integer

View File

@@ -210,12 +210,15 @@ end
--- @param clear_env? boolean --- @param clear_env? boolean
--- @return string[]? --- @return string[]?
local function setup_env(env, clear_env) local function setup_env(env, clear_env)
if clear_env then if not env and clear_env then
return env return
end end
--- @type table<string,string|number> env = env or {}
env = vim.tbl_extend('force', base_env(), env or {}) if not clear_env then
--- @type table<string,string|number>
env = vim.tbl_extend('force', base_env(), env)
end
local renv = {} --- @type string[] local renv = {} --- @type string[]
for k, v in pairs(env) do for k, v in pairs(env) do
@@ -245,7 +248,13 @@ local function spawn(cmd, opts, on_exit, on_error)
local handle, pid_or_err = uv.spawn(cmd, opts, on_exit) local handle, pid_or_err = uv.spawn(cmd, opts, on_exit)
if not handle then if not handle then
on_error() on_error()
error(pid_or_err) if opts.cwd and not uv.fs_stat(opts.cwd) then
error(("%s (cwd): '%s'"):format(pid_or_err, opts.cwd))
elseif vim.fn.executable(cmd) == 0 then
error(("%s (cmd): '%s'"):format(pid_or_err, cmd))
else
error(pid_or_err)
end
end end
return handle, pid_or_err --[[@as integer]] return handle, pid_or_err --[[@as integer]]
end end

View File

@@ -241,6 +241,10 @@ end
--- @class vim.diagnostic.Opts.VirtualLines --- @class vim.diagnostic.Opts.VirtualLines
--- ---
--- Only show virtual lines for diagnostics matching the given
--- severity |diagnostic-severity|
--- @field severity? vim.diagnostic.SeverityFilter
---
--- Only show diagnostics for the current line. --- Only show diagnostics for the current line.
--- (default: `false`) --- (default: `false`)
--- @field current_line? boolean --- @field current_line? boolean
@@ -252,7 +256,7 @@ end
--- @class vim.diagnostic.Opts.Signs --- @class vim.diagnostic.Opts.Signs
--- ---
--- Only show virtual text for diagnostics matching the given --- Only show signs for diagnostics matching the given
--- severity |diagnostic-severity| --- severity |diagnostic-severity|
--- @field severity? vim.diagnostic.SeverityFilter --- @field severity? vim.diagnostic.SeverityFilter
--- ---
@@ -1438,6 +1442,7 @@ M.handlers.signs = {
vim.validate('bufnr', bufnr, 'number') vim.validate('bufnr', bufnr, 'number')
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
vim.validate('opts.signs', (opts and opts or {}).signs, 'table', true)
bufnr = vim._resolve_bufnr(bufnr) bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {} opts = opts or {}
@@ -2615,7 +2620,9 @@ function M.toqflist(diagnostics)
end_lnum = v.end_lnum and (v.end_lnum + 1) or nil, end_lnum = v.end_lnum and (v.end_lnum + 1) or nil,
end_col = v.end_col and (v.end_col + 1) or nil, end_col = v.end_col and (v.end_col + 1) or nil,
text = v.message, text = v.message,
nr = tonumber(v.code),
type = errlist_type_map[v.severity] or 'E', type = errlist_type_map[v.severity] or 'E',
valid = 1,
} }
table.insert(list, item) table.insert(list, item)
end end
@@ -2647,7 +2654,8 @@ function M.fromqflist(list)
local col = math.max(0, item.col - 1) local col = math.max(0, item.col - 1)
local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum
local end_col = item.end_col > 0 and (item.end_col - 1) or col local end_col = item.end_col > 0 and (item.end_col - 1) or col
local severity = item.type ~= '' and M.severity[item.type] or M.severity.ERROR local code = item.nr > 0 and item.nr or nil
local severity = item.type ~= '' and M.severity[item.type:upper()] or M.severity.ERROR
diagnostics[#diagnostics + 1] = { diagnostics[#diagnostics + 1] = {
bufnr = item.bufnr, bufnr = item.bufnr,
lnum = lnum, lnum = lnum,
@@ -2656,6 +2664,7 @@ function M.fromqflist(list)
end_col = end_col, end_col = end_col,
severity = severity, severity = severity,
message = item.text, message = item.text,
code = code,
} }
end end
end end

View File

@@ -2427,8 +2427,26 @@ local pattern = {
['^%.?gtkrc'] = starsetf('gtkrc'), ['^%.?gtkrc'] = starsetf('gtkrc'),
['/doc/.*%.txt$'] = function(_, bufnr) ['/doc/.*%.txt$'] = function(_, bufnr)
local line = M._getline(bufnr, -1) local line = M._getline(bufnr, -1)
local ml = line:find('^vim:') or line:find('%svim:') if
if ml and M._matchregex(line:sub(ml), [[\<\(ft\|filetype\)=help\>]]) then M._findany(line, {
'^vim:ft=help[:%s]',
'^vim:ft=help$',
'^vim:filetype=help[:%s]',
'^vim:filetype=help$',
'^vim:.*[:%s]ft=help[:%s]',
'^vim:.*[:%s]ft=help$',
'^vim:.*[:%s]filetype=help[:%s]',
'^vim:.*[:%s]filetype=help$',
'%svim:ft=help[:%s]',
'%svim:ft=help$',
'%svim:filetype=help[:%s]',
'%svim:filetype=help$',
'%svim:.*[:%s]ft=help[:%s]',
'%svim:.*[:%s]ft=help$',
'%svim:.*[:%s]filetype=help[:%s]',
'%svim:.*[:%s]filetype=help$',
})
then
return 'help' return 'help'
end end
end, end,
@@ -2587,7 +2605,7 @@ end
--- Filetype mappings can be added either by extension or by filename (either --- Filetype mappings can be added either by extension or by filename (either
--- the "tail" or the full file path). The full file path is checked first, --- the "tail" or the full file path). The full file path is checked first,
--- followed by the file name. If a match is not found using the filename, then --- followed by the file name. If a match is not found using the filename, then
--- the filename is matched against the list of |lua-patterns| (sorted by priority) --- the filename is matched against the list of |lua-pattern|s (sorted by priority)
--- until a match is found. Lastly, if pattern matching does not find a --- until a match is found. Lastly, if pattern matching does not find a
--- filetype, then the file extension is used. --- filetype, then the file extension is used.
--- ---

View File

@@ -6,7 +6,7 @@
--- ---
--- >lua --- >lua
--- if vim.uv.fs_stat(file) then --- if vim.uv.fs_stat(file) then
--- vim.print("file exists") --- vim.print('file exists')
--- end --- end
--- < --- <
@@ -19,21 +19,21 @@ local sysname = uv.os_uname().sysname:lower()
local iswin = not not (sysname:find('windows') or sysname:find('mingw')) local iswin = not not (sysname:find('windows') or sysname:find('mingw'))
local os_sep = iswin and '\\' or '/' local os_sep = iswin and '\\' or '/'
--- Iterate over all the parents of the given path. --- Iterate over all the parents of the given path (not expanded/resolved, the caller must do that).
--- ---
--- Example: --- Example:
--- ---
--- ```lua --- ```lua
--- local root_dir --- local root_dir
--- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do --- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
--- if vim.fn.isdirectory(dir .. "/.git") == 1 then --- if vim.fn.isdirectory(dir .. '/.git') == 1 then
--- root_dir = dir --- root_dir = dir
--- break --- break
--- end --- end
--- end --- end
--- ---
--- if root_dir then --- if root_dir then
--- print("Found git repository at", root_dir) --- print('Found git repository at', root_dir)
--- end --- end
--- ``` --- ```
--- ---
@@ -55,7 +55,7 @@ function M.parents(start)
start start
end end
--- Return the parent directory of the given path --- Gets the parent directory of the given path (not expanded/resolved, the caller must do that).
--- ---
---@since 10 ---@since 10
---@generic T : string|nil ---@generic T : string|nil
@@ -124,6 +124,23 @@ function M.joinpath(...)
return (path:gsub('//+', '/')) return (path:gsub('//+', '/'))
end end
--- @class vim.fs.dir.Opts
--- @inlinedoc
---
--- How deep the traverse.
--- (default: `1`)
--- @field depth? integer
---
--- Predicate to control traversal.
--- Return false to stop searching the current directory.
--- Only useful when depth > 1
--- Return an iterator over the items located in {path}
--- @field skip? (fun(dir_name: string): boolean)
---
--- Follow symbolic links.
--- (default: `false`)
--- @field follow? boolean
---@alias Iterator fun(): string?, string? ---@alias Iterator fun(): string?, string?
--- Return an iterator over the items located in {path} --- Return an iterator over the items located in {path}
@@ -131,13 +148,7 @@ end
---@since 10 ---@since 10
---@param path (string) An absolute or relative path to the directory to iterate ---@param path (string) An absolute or relative path to the directory to iterate
--- over. The path is first normalized |vim.fs.normalize()|. --- over. The path is first normalized |vim.fs.normalize()|.
--- @param opts table|nil Optional keyword arguments: ---@param opts? vim.fs.dir.Opts Optional keyword arguments:
--- - depth: integer|nil How deep the traverse (default 1)
--- - skip: (fun(dir_name: string): boolean)|nil Predicate
--- to control traversal. Return false to stop searching the current directory.
--- Only useful when depth > 1
--- - follow: boolean|nil Follow symbolic links. (default: false)
---
---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type". ---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type".
--- "name" is the basename of the item relative to {path}. --- "name" is the basename of the item relative to {path}.
--- "type" is one of the following: --- "type" is one of the following:
@@ -234,16 +245,17 @@ end
--- Examples: --- Examples:
--- ---
--- ```lua --- ```lua
--- -- list all test directories under the runtime directory --- -- List all test directories under the runtime directory.
--- local test_dirs = vim.fs.find( --- local dirs = vim.fs.find(
--- {'test', 'tst', 'testdir'}, --- { 'test', 'tst', 'testdir' },
--- {limit = math.huge, type = 'directory', path = './runtime/'} --- { limit = math.huge, type = 'directory', path = './runtime/' }
--- ) --- )
--- ---
--- -- get all files ending with .cpp or .hpp inside lib/ --- -- Get all "lib/*.cpp" and "lib/*.hpp" files, using Lua patterns.
--- local cpp_hpp = vim.fs.find(function(name, path) --- -- Or use `vim.glob.to_lpeg(…):match(…)` for glob/wildcard matching.
--- local files = vim.fs.find(function(name, path)
--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') --- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
--- end, {limit = math.huge, type = 'file'}) --- end, { limit = math.huge, type = 'file' })
--- ``` --- ```
--- ---
---@since 10 ---@since 10
@@ -255,7 +267,7 @@ end
--- ---
--- The function should return `true` if the given item is considered a match. --- The function should return `true` if the given item is considered a match.
--- ---
---@param opts vim.fs.find.Opts Optional keyword arguments: ---@param opts? vim.fs.find.Opts Optional keyword arguments:
---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items ---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items
function M.find(names, opts) function M.find(names, opts)
opts = opts or {} opts = opts or {}
@@ -374,7 +386,7 @@ end
--- If the buffer is unnamed (has no backing file) or has a non-empty 'buftype' then the search --- If the buffer is unnamed (has no backing file) or has a non-empty 'buftype' then the search
--- begins from Nvim's |current-directory|. --- begins from Nvim's |current-directory|.
--- ---
--- Example: --- Examples:
--- ---
--- ```lua --- ```lua
--- -- Find the root of a Python project, starting from file 'main.py' --- -- Find the root of a Python project, starting from file 'main.py'
@@ -387,14 +399,21 @@ end
--- vim.fs.root(0, function(name, path) --- vim.fs.root(0, function(name, path)
--- return name:match('%.csproj$') ~= nil --- return name:match('%.csproj$') ~= nil
--- end) --- end)
---
--- -- Find the first ancestor directory containing EITHER "stylua.toml" or ".luarc.json"; if
--- -- not found, find the first ancestor containing ".git":
--- vim.fs.root(0, { { 'stylua.toml', '.luarc.json' }, '.git' })
--- ``` --- ```
--- ---
--- @since 12 --- @since 12
--- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or --- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or
--- relative to the |current-directory|) to begin the search from. --- relative to the |current-directory|) to begin the search from.
--- @param marker (string|string[]|fun(name: string, path: string): boolean) A marker, or list --- @param marker (string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean
--- of markers, to search for. If a function, the function is called for each --- Filename, function, or list thereof, that decides how to find the root. To
--- evaluated item and should return true if {name} and {path} are a match. --- indicate "equal priority", specify items in a nested list `{ { 'a.txt', 'b.lua' }, … }`.
--- A function item must return true if `name` and `path` are a match. Each item
--- (which may itself be a nested list) is evaluated in-order against all ancestors,
--- until a match is found.
--- @return string? # Directory path containing one of the given markers, or nil if no directory was --- @return string? # Directory path containing one of the given markers, or nil if no directory was
--- found. --- found.
function M.root(source, marker) function M.root(source, marker)
@@ -414,16 +433,19 @@ function M.root(source, marker)
error('invalid type for argument "source": expected string or buffer number') error('invalid type for argument "source": expected string or buffer number')
end end
local paths = M.find(marker, { local markers = type(marker) == 'table' and marker or { marker }
upward = true, for _, mark in ipairs(markers) do
path = vim.fn.fnamemodify(path, ':p:h'), local paths = M.find(mark, {
}) upward = true,
path = vim.fn.fnamemodify(path, ':p:h'),
})
if #paths == 0 then if #paths ~= 0 then
return nil return vim.fs.dirname(paths[1])
end
end end
return vim.fs.dirname(paths[1]) return nil
end end
--- Split a Windows path into a prefix and a body, such that the body can be processed like a POSIX --- Split a Windows path into a prefix and a body, such that the body can be processed like a POSIX

View File

@@ -1,16 +1,16 @@
--- @brief --- @brief
---<pre>help
--- vim.health is a minimal framework to help users troubleshoot configuration and
--- any other environment conditions that a plugin might care about. Nvim ships
--- with healthchecks for configuration, performance, python support, ruby
--- support, clipboard support, and more.
--- ---
--- To run all healthchecks, use: >vim --- vim.health is a minimal framework to help users troubleshoot configuration and any other
--- environment conditions that a plugin might care about. Nvim ships with healthchecks for
--- configuration, performance, python support, ruby support, clipboard support, and more.
--- ---
--- :checkhealth --- To run all healthchecks, use:
--- < --- ```vim
--- :checkhealth
--- ```
--- Plugin authors are encouraged to write new healthchecks. |health-dev| --- Plugin authors are encouraged to write new healthchecks. |health-dev|
--- ---
---<pre>help
--- COMMANDS *health-commands* --- COMMANDS *health-commands*
--- ---
--- *:che* *:checkhealth* --- *:che* *:checkhealth*
@@ -46,7 +46,6 @@
--- q Closes the window. --- q Closes the window.
--- ---
--- Global configuration: --- Global configuration:
---
--- *g:health* --- *g:health*
--- g:health Dictionary with the following optional keys: --- g:health Dictionary with the following optional keys:
--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in --- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
@@ -55,57 +54,69 @@
--- Example: >lua --- Example: >lua
--- vim.g.health = { style = 'float' } --- vim.g.health = { style = 'float' }
--- ---
---</pre>
---
--- Local configuration:
---
--- Checkhealth sets its buffer filetype to "checkhealth". You can customize the buffer by handling
--- the |FileType| event. For example if you don't want emojis in the health report:
--- ```vim
--- autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
--- ```
---
---<pre>help
--- -------------------------------------------------------------------------------- --- --------------------------------------------------------------------------------
--- Create a healthcheck *health-dev* --- Create a healthcheck *health-dev*
---</pre>
--- ---
--- Healthchecks are functions that check the user environment, configuration, or --- Healthchecks are functions that check the user environment, configuration, or any other
--- any other prerequisites that a plugin cares about. Nvim ships with --- prerequisites that a plugin cares about. Nvim ships with healthchecks in:
--- healthchecks in: --- - $VIMRUNTIME/autoload/health/
--- - $VIMRUNTIME/autoload/health/ --- - $VIMRUNTIME/lua/vim/lsp/health.lua
--- - $VIMRUNTIME/lua/vim/lsp/health.lua --- - $VIMRUNTIME/lua/vim/treesitter/health.lua
--- - $VIMRUNTIME/lua/vim/treesitter/health.lua --- - and more...
--- - and more...
--- ---
--- To add a new healthcheck for your own plugin, simply create a "health.lua" --- To add a new healthcheck for your own plugin, simply create a "health.lua" module on
--- module on 'runtimepath' that returns a table with a "check()" function. Then --- 'runtimepath' that returns a table with a "check()" function. Then |:checkhealth| will
--- |:checkhealth| will automatically find and invoke the function. --- automatically find and invoke the function.
--- ---
--- For example if your plugin is named "foo", define your healthcheck module at --- For example if your plugin is named "foo", define your healthcheck module at
--- one of these locations (on 'runtimepath'): --- one of these locations (on 'runtimepath'):
--- - lua/foo/health/init.lua --- - lua/foo/health/init.lua
--- - lua/foo/health.lua --- - lua/foo/health.lua
--- ---
--- If your plugin also provides a submodule named "bar" for which you want --- If your plugin also provides a submodule named "bar" for which you want a separate healthcheck,
--- a separate healthcheck, define the healthcheck at one of these locations: --- define the healthcheck at one of these locations:
--- - lua/foo/bar/health/init.lua --- - lua/foo/bar/health/init.lua
--- - lua/foo/bar/health.lua --- - lua/foo/bar/health.lua
--- ---
--- All such health modules must return a Lua table containing a `check()` --- All such health modules must return a Lua table containing a `check()` function.
--- function.
--- ---
--- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path --- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path with your plugin
--- with your plugin name: >lua --- name:
--- ---
--- local M = {} --- ```lua
--- local M = {}
--- ---
--- M.check = function() --- M.check = function()
--- vim.health.start("foo report") --- vim.health.start("foo report")
--- -- make sure setup function parameters are ok --- -- make sure setup function parameters are ok
--- if check_setup() then --- if check_setup() then
--- vim.health.ok("Setup is correct") --- vim.health.ok("Setup is correct")
--- else --- else
--- vim.health.error("Setup is incorrect") --- vim.health.error("Setup is incorrect")
--- end --- end
--- -- do some more checking --- -- do some more checking
--- -- ... --- -- ...
--- end --- end
--- ---
--- return M --- return M
---</pre> --- ```
local M = {} local M = {}
local s_output = {} ---@type string[] local s_output = {} ---@type string[]
local check_summary = { warn = 0, error = 0 }
-- From a path return a list [{name}, {func}, {type}] representing a healthcheck -- From a path return a list [{name}, {func}, {type}] representing a healthcheck
local function filepath_to_healthcheck(path) local function filepath_to_healthcheck(path)
@@ -275,7 +286,7 @@ end
--- ---
--- @param msg string --- @param msg string
function M.ok(msg) function M.ok(msg)
local input = format_report_message('OK', msg) local input = format_report_message('OK', msg)
collect_output(input) collect_output(input)
end end
@@ -284,8 +295,9 @@ end
--- @param msg string --- @param msg string
--- @param ... string|string[] Optional advice --- @param ... string|string[] Optional advice
function M.warn(msg, ...) function M.warn(msg, ...)
local input = format_report_message('WARNING', msg, ...) local input = format_report_message('⚠️ WARNING', msg, ...)
collect_output(input) collect_output(input)
check_summary['warn'] = check_summary['warn'] + 1
end end
--- Reports an error. --- Reports an error.
@@ -293,8 +305,9 @@ end
--- @param msg string --- @param msg string
--- @param ... string|string[] Optional advice --- @param ... string|string[] Optional advice
function M.error(msg, ...) function M.error(msg, ...)
local input = format_report_message('ERROR', msg, ...) local input = format_report_message('ERROR', msg, ...)
collect_output(input) collect_output(input)
check_summary['error'] = check_summary['error'] + 1
end end
local path2name = function(path) local path2name = function(path)
@@ -341,6 +354,23 @@ M._complete = function()
return rv return rv
end end
--- Gets the results heading for the current report section.
---
---@return string
local function get_summary()
local s = ''
local errors = check_summary['error']
local warns = check_summary['warn']
s = s .. (warns > 0 and (' %2d ⚠️'):format(warns) or '')
s = s .. (errors > 0 and (' %2d ❌'):format(errors) or '')
if errors == 0 and warns == 0 then
s = s .. ''
end
return s
end
--- Runs the specified healthchecks. --- Runs the specified healthchecks.
--- Runs all discovered healthchecks if plugin_names is empty. --- Runs all discovered healthchecks if plugin_names is empty.
--- ---
@@ -353,25 +383,29 @@ function M._check(mods, plugin_names)
local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$') local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$')
local bufnr = vim.api.nvim_create_buf(true, true) local bufnr ---@type integer
if if
vim.g.health vim.g.health
and type(vim.g.health) == 'table' and type(vim.g.health) == 'table'
and vim.tbl_get(vim.g.health, 'style') == 'float' and vim.tbl_get(vim.g.health, 'style') == 'float'
then then
local max_height = math.floor(vim.o.lines * 0.8) local available_lines = vim.o.lines - 12
local max_height = math.min(math.floor(vim.o.lines * 0.8), available_lines)
local max_width = 80 local max_width = 80
local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', { local float_winid
bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
height = max_height, height = max_height,
width = max_width, width = max_width,
offset_x = math.floor((vim.o.columns - max_width) / 2), offset_x = math.floor((vim.o.columns - max_width) / 2),
offset_y = math.floor((vim.o.lines - max_height) / 2) - 1, offset_y = math.floor((available_lines - max_height) / 2),
relative = 'editor', relative = 'editor',
close_events = {},
}) })
vim.api.nvim_set_current_win(float_winid) vim.api.nvim_set_current_win(float_winid)
vim.bo[float_bufnr].modifiable = true vim.bo[bufnr].modifiable = true
vim.wo[float_winid].list = false vim.wo[float_winid].list = false
else else
bufnr = vim.api.nvim_create_buf(true, true)
-- When no command modifiers are used: -- When no command modifiers are used:
-- - If the current buffer is empty, open healthcheck directly. -- - If the current buffer is empty, open healthcheck directly.
-- - If not specified otherwise open healthcheck in a tab. -- - If not specified otherwise open healthcheck in a tab.
@@ -383,7 +417,6 @@ function M._check(mods, plugin_names)
vim.cmd.bwipe('health://') vim.cmd.bwipe('health://')
end end
vim.cmd.file('health://') vim.cmd.file('health://')
vim.cmd.setfiletype('checkhealth')
-- This should only happen when doing `:checkhealth vim` -- This should only happen when doing `:checkhealth vim`
if next(healthchecks) == nil then if next(healthchecks) == nil then
@@ -397,9 +430,9 @@ function M._check(mods, plugin_names)
local func = value[1] local func = value[1]
local type = value[2] local type = value[2]
s_output = {} s_output = {}
check_summary = { warn = 0, error = 0 }
if func == '' then if func == '' then
s_output = {}
M.error('No healthcheck found for "' .. name .. '" plugin.') M.error('No healthcheck found for "' .. name .. '" plugin.')
end end
if type == 'v' then if type == 'v' then
@@ -420,10 +453,12 @@ function M._check(mods, plugin_names)
M.error('The healthcheck report for "' .. name .. '" plugin is empty.') M.error('The healthcheck report for "' .. name .. '" plugin is empty.')
end end
local report = get_summary()
local replen = vim.fn.strwidth(report)
local header = { local header = {
string.rep('=', 78), string.rep('=', 78),
-- Example: `foo.health: [ …] require("foo.health").check()` -- Example: `foo.health: [ …] 1 ⚠️ 5 ❌`
('%s: %s%s'):format(name, (' '):rep(76 - name:len() - func:len()), func), ('%s: %s%s'):format(name, (' '):rep(76 - name:len() - replen), report),
'', '',
} }
@@ -449,14 +484,19 @@ function M._check(mods, plugin_names)
vim.print('') vim.print('')
-- Quit with 'q' inside healthcheck buffers. -- Quit with 'q' inside healthcheck buffers.
vim.keymap.set('n', 'q', function() vim._with({ buf = bufnr }, function()
if not pcall(vim.cmd.close) then if vim.fn.maparg('q', 'n', false, false) == '' then
vim.cmd.bdelete() vim.keymap.set('n', 'q', function()
if not pcall(vim.cmd.close) then
vim.cmd.bdelete()
end
end, { buffer = bufnr, silent = true, noremap = true, nowait = true })
end end
end, { buffer = bufnr, silent = true, noremap = true, nowait = true }) end)
-- Once we're done writing checks, set nomodifiable. -- Once we're done writing checks, set nomodifiable.
vim.bo[bufnr].modifiable = false vim.bo[bufnr].modifiable = false
vim.cmd.setfiletype('checkhealth')
end end
return M return M

View File

@@ -87,9 +87,9 @@ local function check_config()
health.error( health.error(
'Locale does not support UTF-8. Unicode characters may not display correctly.' 'Locale does not support UTF-8. Unicode characters may not display correctly.'
.. ('\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s'):format( .. ('\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s'):format(
vim.env.LANG, vim.env.LANG or '',
vim.env.LC_ALL, vim.env.LC_ALL or '',
vim.env.LC_CTYPE vim.env.LC_CTYPE or ''
), ),
{ {
'If using tmux, try the -u option.', 'If using tmux, try the -u option.',
@@ -318,9 +318,11 @@ local function check_tmux()
'$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.', '$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.',
{ '$TERM may have been set by some rc (.bashrc, .zshrc, ...).' } { '$TERM may have been set by some rc (.bashrc, .zshrc, ...).' }
) )
elseif not vim.regex([[\v(tmux-256color|screen-256color)]]):match_str(vim.env.TERM) then elseif
not vim.regex([[\v(tmux-256color|tmux-direct|screen-256color)]]):match_str(vim.env.TERM)
then
health.error( health.error(
'$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.', '$TERM should be "screen-256color", "tmux-256color", or "tmux-direct" in tmux. Colors might look wrong.',
{ {
'Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal "screen-256color"', 'Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal "screen-256color"',
suggest_faq, suggest_faq,

View File

@@ -17,9 +17,6 @@ M.priorities = {
user = 200, user = 200,
} }
local range_timer --- @type uv.uv_timer_t?
local range_hl_clear --- @type fun()?
--- @class vim.hl.range.Opts --- @class vim.hl.range.Opts
--- @inlinedoc --- @inlinedoc
--- ---
@@ -47,6 +44,10 @@ local range_hl_clear --- @type fun()?
---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()| ---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()|
---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()| ---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()|
---@param opts? vim.hl.range.Opts ---@param opts? vim.hl.range.Opts
--- @return uv.uv_timer_t? range_timer A timer which manages how much time the
--- highlight has left
--- @return fun()? range_clear A function which allows clearing the highlight manually.
--- nil is returned if timeout is not specified
function M.range(bufnr, ns, higroup, start, finish, opts) function M.range(bufnr, ns, higroup, start, finish, opts)
opts = opts or {} opts = opts or {}
local regtype = opts.regtype or 'v' local regtype = opts.regtype or 'v'
@@ -108,38 +109,41 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
end end
end end
if range_timer and not range_timer:is_closing() then local extmarks = {} --- @type integer[]
range_timer:close()
assert(range_hl_clear)
range_hl_clear()
end
range_hl_clear = function()
range_timer = nil
range_hl_clear = nil
pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1)
pcall(vim.api.nvim__ns_set, { wins = {} })
end
for _, res in ipairs(region) do for _, res in ipairs(region) do
local start_row = res[1][2] - 1 local start_row = res[1][2] - 1
local start_col = res[1][3] - 1 local start_col = res[1][3] - 1
local end_row = res[2][2] - 1 local end_row = res[2][2] - 1
local end_col = res[2][3] local end_col = res[2][3]
api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, { table.insert(
hl_group = higroup, extmarks,
end_row = end_row, api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, {
end_col = end_col, hl_group = higroup,
priority = priority, end_row = end_row,
strict = false, end_col = end_col,
}) priority = priority,
strict = false,
})
)
end
local range_hl_clear = function()
if not api.nvim_buf_is_valid(bufnr) then
return
end
for _, mark in ipairs(extmarks) do
api.nvim_buf_del_extmark(bufnr, ns, mark)
end
end end
if timeout ~= -1 then if timeout ~= -1 then
range_timer = vim.defer_fn(range_hl_clear, timeout) local range_timer = vim.defer_fn(range_hl_clear, timeout)
return range_timer, range_hl_clear
end end
end end
local yank_timer --- @type uv.uv_timer_t?
local yank_hl_clear --- @type fun()?
local yank_ns = api.nvim_create_namespace('nvim.hlyank') local yank_ns = api.nvim_create_namespace('nvim.hlyank')
--- Highlight the yanked text during a |TextYankPost| event. --- Highlight the yanked text during a |TextYankPost| event.
@@ -179,8 +183,14 @@ function M.on_yank(opts)
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local winid = vim.api.nvim_get_current_win() local winid = vim.api.nvim_get_current_win()
if yank_timer and not yank_timer:is_closing() then
yank_timer:close()
assert(yank_hl_clear)
yank_hl_clear()
end
vim.api.nvim__ns_set(yank_ns, { wins = { winid } }) vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
M.range(bufnr, yank_ns, higroup, "'[", "']", { yank_timer, yank_hl_clear = M.range(bufnr, yank_ns, higroup, "'[", "']", {
regtype = event.regtype, regtype = event.regtype,
inclusive = true, inclusive = true,
priority = opts.priority or M.priorities.user, priority = opts.priority or M.priorities.user,

View File

@@ -957,6 +957,9 @@ end
---@private ---@private
function ArrayIter:last() function ArrayIter:last()
if self._head >= self._tail then
return nil
end
local inc = self._head < self._tail and 1 or -1 local inc = self._head < self._tail and 1 or -1
local v = self._table[self._tail - inc] local v = self._table[self._tail - inc]
self._head = self._tail self._head = self._tail

View File

@@ -147,7 +147,7 @@ end
--- @param config vim.lsp.ClientConfig --- @param config vim.lsp.ClientConfig
--- @return boolean --- @return boolean
local function reuse_client_default(client, config) local function reuse_client_default(client, config)
if client.name ~= config.name then if client.name ~= config.name or client:is_stopped() then
return false return false
end end
@@ -275,26 +275,47 @@ end
--- @class vim.lsp.Config : vim.lsp.ClientConfig --- @class vim.lsp.Config : vim.lsp.ClientConfig
--- ---
--- See `cmd` in [vim.lsp.ClientConfig]. --- See `cmd` in [vim.lsp.ClientConfig].
--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient --- See also `reuse_client` to dynamically decide (per-buffer) when `cmd` should be re-invoked.
--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient
--- ---
--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`. --- Filetypes the client will attach to, if activated by `vim.lsp.enable()`. If not provided, the
--- If not provided, then the client will attach to all filetypes. --- client will attach to all filetypes.
--- @field filetypes? string[] --- @field filetypes? string[]
--- ---
--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders, --- Predicate which decides if a client should be re-used. Used on all running clients. The default
--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided. --- implementation re-uses a client if name and root_dir matches.
--- @field root_markers? string[]
---
--- Directory where the LSP server will base its workspaceFolders, rootUri, and
--- rootPath on initialization. If a function, it is passed the buffer number
--- and a callback argument which must be called with the value of root_dir to
--- use. The LSP server will not be started until the callback is called.
--- @field root_dir? string|fun(bufnr: integer, cb:fun(root_dir?:string))
---
--- Predicate used to decide if a client should be re-used. Used on all
--- running clients. The default implementation re-uses a client if name and
--- root_dir matches.
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean --- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
---
--- [lsp-root_dir()]()
--- Decides the workspace root: the directory where the LSP server will base its workspaceFolders,
--- rootUri, and rootPath on initialization. The function form must call the `on_dir` callback to
--- provide the root dir, or LSP will not be activated for the buffer. Thus a `root_dir()` function
--- can dynamically decide per-buffer whether to activate (or skip) LSP.
--- See example at |vim.lsp.enable()|.
--- @field root_dir? string|fun(bufnr: integer, on_dir:fun(root_dir?:string))
---
--- [lsp-root_markers]()
--- Filename(s) (".git/", "package.json", …) used to decide the workspace root. Unused if `root_dir`
--- is defined. The list order decides priority. To indicate "equal priority", specify names in
--- a nested list `{ { 'a.txt', 'b.lua' }, ... }`.
---
--- For each item, Nvim will search upwards (from the buffer file) for that marker, or list of
--- markers; search stops at the first directory containing that marker, and the directory is used
--- as the root dir (workspace folder).
---
--- Example: Find the first ancestor directory containing file or directory "stylua.toml"; if not
--- found, find the first ancestor containing ".git":
--- ```lua
--- root_markers = { 'stylua.toml', '.git' }
--- ```
---
--- Example: Find the first ancestor directory containing EITHER "stylua.toml" or ".luarc.json"; if
--- not found, find the first ancestor containing ".git":
--- ```lua
--- root_markers = { { 'stylua.toml', '.luarc.json' }, '.git' }
--- ```
---
--- @field root_markers? (string|string[])[]
--- Update the configuration for an LSP client. --- Update the configuration for an LSP client.
--- ---
@@ -362,6 +383,19 @@ local function invalidate_enabled_config(name)
end end
end end
--- @param name any
local function validate_config_name(name)
validate('name', name, function(value)
if type(value) ~= 'string' then
return false
end
if value ~= '*' and value:match('%*') then
return false, 'LSP config name cannot contain wildcard ("*")'
end
return true
end, 'non-wildcard string')
end
--- @nodoc --- @nodoc
--- @class vim.lsp.config --- @class vim.lsp.config
--- @field [string] vim.lsp.Config --- @field [string] vim.lsp.Config
@@ -371,11 +405,16 @@ lsp.config = setmetatable({ _configs = {} }, {
--- @param name string --- @param name string
--- @return vim.lsp.Config --- @return vim.lsp.Config
__index = function(self, name) __index = function(self, name)
validate('name', name, 'string') validate_config_name(name)
local rconfig = lsp._enabled_configs[name] or {} local rconfig = lsp._enabled_configs[name] or {}
if not rconfig.resolved_config then if not rconfig.resolved_config then
if name == '*' then
rconfig.resolved_config = lsp.config._configs['*'] or {}
return rconfig.resolved_config
end
-- Resolve configs from lsp/*.lua -- Resolve configs from lsp/*.lua
-- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites. -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
local rtp_config --- @type vim.lsp.Config? local rtp_config --- @type vim.lsp.Config?
@@ -385,12 +424,12 @@ lsp.config = setmetatable({ _configs = {} }, {
--- @type vim.lsp.Config? --- @type vim.lsp.Config?
rtp_config = vim.tbl_deep_extend('force', rtp_config or {}, config) rtp_config = vim.tbl_deep_extend('force', rtp_config or {}, config)
else else
log.warn(string.format('%s does not return a table, ignoring', v)) log.warn(('%s does not return a table, ignoring'):format(v))
end end
end end
if not rtp_config and not self._configs[name] then if not rtp_config and not self._configs[name] then
log.warn(string.format('%s does not have a configuration', name)) log.warn(('%s does not have a configuration'):format(name))
return return
end end
@@ -410,8 +449,9 @@ lsp.config = setmetatable({ _configs = {} }, {
--- @param name string --- @param name string
--- @param cfg vim.lsp.Config --- @param cfg vim.lsp.Config
__newindex = function(self, name, cfg) __newindex = function(self, name, cfg)
validate('name', name, 'string') validate_config_name(name)
validate('cfg', cfg, 'table') local msg = ('table (hint: to resolve a config, use vim.lsp.config["%s"])'):format(name)
validate('cfg', cfg, 'table', msg)
invalidate_enabled_config(name) invalidate_enabled_config(name)
self._configs[name] = cfg self._configs[name] = cfg
end, end,
@@ -420,8 +460,9 @@ lsp.config = setmetatable({ _configs = {} }, {
--- @param name string --- @param name string
--- @param cfg vim.lsp.Config --- @param cfg vim.lsp.Config
__call = function(self, name, cfg) __call = function(self, name, cfg)
validate('name', name, 'string') validate_config_name(name)
validate('cfg', cfg, 'table') local msg = ('table (hint: to resolve a config, use vim.lsp.config["%s"])'):format(name)
validate('cfg', cfg, 'table', msg)
invalidate_enabled_config(name) invalidate_enabled_config(name)
self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg) self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
end, end,
@@ -480,6 +521,17 @@ local function lsp_enable_callback(bufnr)
return return
end end
-- Stop any clients that no longer apply to this buffer.
local clients = lsp.get_clients({ bufnr = bufnr, _uninitialized = true })
for _, client in ipairs(clients) do
if
lsp.is_enabled(client.name) and not can_start(bufnr, client.name, lsp.config[client.name])
then
lsp.buf_detach_client(bufnr, client.id)
end
end
-- Start any clients that apply to this buffer.
for name in vim.spairs(lsp._enabled_configs) do for name in vim.spairs(lsp._enabled_configs) do
local config = lsp.config[name] local config = lsp.config[name]
if config and can_start(bufnr, name, config) then if config and can_start(bufnr, name, config) then
@@ -502,20 +554,40 @@ local function lsp_enable_callback(bufnr)
end end
end end
--- Enable an LSP server to automatically start when opening a buffer. --- Auto-starts LSP when a buffer is opened, based on the |lsp-config| `filetypes`, `root_markers`,
--- --- and `root_dir` fields.
--- Uses configuration defined with `vim.lsp.config`.
--- ---
--- Examples: --- Examples:
--- ---
--- ```lua --- ```lua
--- vim.lsp.enable('clangd') --- vim.lsp.enable('clangd')
--- vim.lsp.enable({'luals', 'pyright'})
--- ```
--- ---
--- vim.lsp.enable({'luals', 'pyright'}) --- Example: [lsp-restart]() Passing `false` stops and detaches the client(s). Thus you can
--- "restart" LSP by disabling and re-enabling a given config:
---
--- ```lua
--- vim.lsp.enable('clangd', false)
--- vim.lsp.enable('clangd', true)
--- ```
---
--- Example: To _dynamically_ decide whether LSP is activated, define a |lsp-root_dir()| function
--- which calls `on_dir()` only when you want that config to activate:
---
--- ```lua
--- vim.lsp.config('lua_ls', {
--- root_dir = function(bufnr, on_dir)
--- if not vim.fn.bufname(bufnr):match('%.txt$') then
--- on_dir(vim.fn.getcwd())
--- end
--- end
--- })
--- ``` --- ```
--- ---
--- @param name string|string[] Name(s) of client(s) to enable. --- @param name string|string[] Name(s) of client(s) to enable.
--- @param enable? boolean `true|nil` to enable, `false` to disable. --- @param enable? boolean `true|nil` to enable, `false` to disable (actively stops and detaches
--- clients as needed)
function lsp.enable(name, enable) function lsp.enable(name, enable)
validate('name', name, { 'string', 'table' }) validate('name', name, { 'string', 'table' })
@@ -528,21 +600,44 @@ function lsp.enable(name, enable)
end end
if not next(lsp._enabled_configs) then if not next(lsp._enabled_configs) then
-- If there are no remaining LSPs enabled, remove the enable autocmd.
if lsp_enable_autocmd_id then if lsp_enable_autocmd_id then
api.nvim_del_autocmd(lsp_enable_autocmd_id) api.nvim_del_autocmd(lsp_enable_autocmd_id)
lsp_enable_autocmd_id = nil lsp_enable_autocmd_id = nil
end end
return else
-- Only ever create autocmd once to reuse computation of config merging.
lsp_enable_autocmd_id = lsp_enable_autocmd_id
or api.nvim_create_autocmd('FileType', {
group = api.nvim_create_augroup('nvim.lsp.enable', {}),
callback = function(args)
lsp_enable_callback(args.buf)
end,
})
end end
-- Only ever create autocmd once to reuse computation of config merging. -- Ensure any pre-existing buffers start/stop their LSP clients.
lsp_enable_autocmd_id = lsp_enable_autocmd_id if enable ~= false then
or api.nvim_create_autocmd('FileType', { if vim.v.vim_did_enter == 1 then
group = api.nvim_create_augroup('nvim.lsp.enable', {}), vim.cmd.doautoall('nvim.lsp.enable FileType')
callback = function(args) end
lsp_enable_callback(args.buf) else
end, for _, nm in ipairs(names) do
}) for _, client in ipairs(lsp.get_clients({ name = nm })) do
client:stop()
end
end
end
end
--- Checks if the given LSP config is enabled (globally, not per-buffer).
---
--- Unlike `vim.lsp.config['…']`, this does not have the side-effect of resolving the config.
---
--- @param name string Config name
--- @return boolean
function lsp.is_enabled(name)
return lsp._enabled_configs[name] ~= nil
end end
--- @class vim.lsp.start.Opts --- @class vim.lsp.start.Opts
@@ -564,7 +659,7 @@ end
--- Suppress error reporting if the LSP server fails to start (default false). --- Suppress error reporting if the LSP server fails to start (default false).
--- @field silent? boolean --- @field silent? boolean
--- ---
--- @field package _root_markers? string[] --- @field package _root_markers? (string|string[])[]
--- Create a new LSP client and start a language server or reuses an already --- Create a new LSP client and start a language server or reuses an already
--- running client if one is found matching `name` and `root_dir`. --- running client if one is found matching `name` and `root_dir`.
@@ -611,10 +706,23 @@ function lsp.start(config, opts)
local bufnr = vim._resolve_bufnr(opts.bufnr) local bufnr = vim._resolve_bufnr(opts.bufnr)
if not config.root_dir and opts._root_markers then if not config.root_dir and opts._root_markers then
validate('root_markers', opts._root_markers, 'table')
config = vim.deepcopy(config) config = vim.deepcopy(config)
config.root_dir = vim.fs.root(bufnr, opts._root_markers) config.root_dir = vim.fs.root(bufnr, opts._root_markers)
end end
if
not config.root_dir
and (not config.workspace_folders or #config.workspace_folders == 0)
and config.workspace_required
then
log.info(
('skipping config "%s": workspace_required=true, no workspace found'):format(config.name)
)
return
end
for _, client in pairs(all_clients) do for _, client in pairs(all_clients) do
if reuse_client(client, config) then if reuse_client(client, config) then
if opts.attach == false then if opts.attach == false then
@@ -1181,7 +1289,9 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
local function _cancel_all_requests() local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do for client_id, request_id in pairs(client_request_ids) do
local client = all_clients[client_id] local client = all_clients[client_id]
client:cancel_request(request_id) if client.requests[request_id] then
client:cancel_request(request_id)
end
end end
end end

View File

@@ -3,6 +3,13 @@ local log = require('vim.lsp.log')
local ms = require('vim.lsp.protocol').Methods local ms = require('vim.lsp.protocol').Methods
local api = vim.api local api = vim.api
---@type table<lsp.FoldingRangeKind, true>
local supported_fold_kinds = {
['comment'] = true,
['imports'] = true,
['region'] = true,
}
local M = {} local M = {}
---@class (private) vim.lsp.folding_range.BufState ---@class (private) vim.lsp.folding_range.BufState
@@ -39,7 +46,7 @@ local function renew(bufnr)
---@type table<integer, string?> ---@type table<integer, string?>
local row_text = {} local row_text = {}
for _, ranges in pairs(bufstate.client_ranges) do for client_id, ranges in pairs(bufstate.client_ranges) do
for _, range in ipairs(ranges) do for _, range in ipairs(ranges) do
local start_row = range.startLine local start_row = range.startLine
local end_row = range.endLine local end_row = range.endLine
@@ -49,9 +56,14 @@ local function renew(bufnr)
local kind = range.kind local kind = range.kind
if kind then if kind then
local kinds = row_kinds[start_row] or {} -- Ignore unsupported fold kinds.
kinds[kind] = true if supported_fold_kinds[kind] then
row_kinds[start_row] = kinds local kinds = row_kinds[start_row] or {}
kinds[kind] = true
row_kinds[start_row] = kinds
else
log.info(('Unknown fold kind "%s" from client %d'):format(kind, client_id))
end
end end
for row = start_row, end_row do for row = start_row, end_row do

View File

@@ -71,7 +71,7 @@ function M.hover(config)
if vim.tbl_isempty(results1) then if vim.tbl_isempty(results1) then
if config.silent ~= true then if config.silent ~= true then
vim.notify('No information available') vim.notify('No information available', vim.log.levels.INFO)
end end
return return
end end
@@ -215,7 +215,13 @@ local function get_locations(method, opts)
vim.fn.settagstack(vim.fn.win_getid(win), { items = tagstack }, 't') vim.fn.settagstack(vim.fn.win_getid(win), { items = tagstack }, 't')
vim.bo[b].buflisted = true vim.bo[b].buflisted = true
local w = opts.reuse_win and vim.fn.win_findbuf(b)[1] or win local w = win
if opts.reuse_win then
w = vim.fn.win_findbuf(b)[1] or w
if w ~= win then
api.nvim_set_current_win(w)
end
end
api.nvim_win_set_buf(w, b) api.nvim_win_set_buf(w, b)
api.nvim_win_set_cursor(w, { item.lnum, item.col - 1 }) api.nvim_win_set_cursor(w, { item.lnum, item.col - 1 })
vim._with({ win = w }, function() vim._with({ win = w }, function()
@@ -303,6 +309,7 @@ end
--- @param results table<integer,{err: lsp.ResponseError?, result: lsp.SignatureHelp?}> --- @param results table<integer,{err: lsp.ResponseError?, result: lsp.SignatureHelp?}>
local function process_signature_help_results(results) local function process_signature_help_results(results)
local signatures = {} --- @type [vim.lsp.Client,lsp.SignatureInformation][] local signatures = {} --- @type [vim.lsp.Client,lsp.SignatureInformation][]
local active_signature = 1
-- Pre-process results -- Pre-process results
for client_id, r in pairs(results) do for client_id, r in pairs(results) do
@@ -317,15 +324,19 @@ local function process_signature_help_results(results)
else else
local result = r.result --- @type lsp.SignatureHelp local result = r.result --- @type lsp.SignatureHelp
if result and result.signatures and result.signatures[1] then if result and result.signatures and result.signatures[1] then
for _, sig in ipairs(result.signatures) do for i, sig in ipairs(result.signatures) do
sig.activeParameter = sig.activeParameter or result.activeParameter sig.activeParameter = sig.activeParameter or result.activeParameter
signatures[#signatures + 1] = { client, sig } local idx = #signatures + 1
if (result.activeSignature or 0) + 1 == i then
active_signature = idx
end
signatures[idx] = { client, sig }
end end
end end
end end
end end
return signatures return signatures, active_signature
end end
local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help')
@@ -341,6 +352,7 @@ function M.signature_help(config)
config = config and vim.deepcopy(config) or {} config = config and vim.deepcopy(config) or {}
config.focus_id = method config.focus_id = method
local user_title = config.title
lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx) lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx)
if api.nvim_get_current_buf() ~= ctx.bufnr then if api.nvim_get_current_buf() ~= ctx.bufnr then
@@ -348,19 +360,19 @@ function M.signature_help(config)
return return
end end
local signatures = process_signature_help_results(results) local signatures, active_signature = process_signature_help_results(results)
if not next(signatures) then if not next(signatures) then
if config.silent ~= true then if config.silent ~= true then
print('No signature help available') vim.notify('No signature help available', vim.log.levels.INFO)
end end
return return
end end
local ft = vim.bo[ctx.bufnr].filetype local ft = vim.bo[ctx.bufnr].filetype
local total = #signatures local total = #signatures
local can_cycle = total > 1 and config.focusable local can_cycle = total > 1 and config.focusable ~= false
local idx = 0 local idx = active_signature - 1
--- @param update_win? integer --- @param update_win? integer
local function show_signature(update_win) local function show_signature(update_win)
@@ -375,15 +387,19 @@ function M.signature_help(config)
return return
end end
local sfx = can_cycle and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or '' -- Show title only if there are multiple clients or multiple signatures.
local title = string.format('Signature Help: %s%s', client.name, sfx) if total > 1 then
if config.border then local sfx = total > 1
config.title = title and string.format(' (%d/%d)%s', idx, total, can_cycle and ' (<C-s> to cycle)' or '')
else or ''
table.insert(lines, 1, '# ' .. title) config.title = user_title or string.format('Signature Help: %s%s', client.name, sfx)
if hl then -- If no border is set, render title inside the window.
hl[1] = hl[1] + 1 if not (config.border or vim.o.winborder ~= '') then
hl[3] = hl[3] + 1 table.insert(lines, 1, '# ' .. config.title)
if hl then
hl[1] = hl[1] + 1
hl[3] = hl[3] + 1
end
end end
end end
@@ -427,7 +443,7 @@ end
--- ---
---@see vim.lsp.protocol.CompletionTriggerKind ---@see vim.lsp.protocol.CompletionTriggerKind
function M.completion(context) function M.completion(context)
vim.depends('vim.lsp.buf.completion', 'vim.lsp.completion.trigger', '0.12') vim.deprecate('vim.lsp.buf.completion', 'vim.lsp.completion.trigger', '0.12')
return lsp.buf_request( return lsp.buf_request(
0, 0,
ms.textDocument_completion, ms.textDocument_completion,
@@ -957,7 +973,7 @@ function M.add_workspace_folder(workspace_folder)
return return
end end
if vim.fn.isdirectory(workspace_folder) == 0 then if vim.fn.isdirectory(workspace_folder) == 0 then
print(workspace_folder, ' is not a valid directory') vim.notify(workspace_folder .. ' is not a valid directory')
return return
end end
local bufnr = api.nvim_get_current_buf() local bufnr = api.nvim_get_current_buf()
@@ -981,7 +997,7 @@ function M.remove_workspace_folder(workspace_folder)
for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
client:_remove_workspace_folder(workspace_folder) client:_remove_workspace_folder(workspace_folder)
end end
print(workspace_folder, 'is not currently part of the workspace') vim.notify(workspace_folder .. 'is not currently part of the workspace')
end end
--- Lists all symbols in the current workspace in the quickfix window. --- Lists all symbols in the current workspace in the quickfix window.
@@ -1198,8 +1214,7 @@ local function on_code_action_results(results, opts)
vim.ui.select(actions, select_opts, on_user_choice) vim.ui.select(actions, select_opts, on_user_choice)
end end
--- Selects a code action available at the current --- Selects a code action (LSP: "textDocument/codeAction" request) available at cursor position.
--- cursor position.
--- ---
---@param opts? vim.lsp.buf.code_action.Opts ---@param opts? vim.lsp.buf.code_action.Opts
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction

View File

@@ -30,38 +30,11 @@ local validate = vim.validate
--- @field exit_timeout integer|false --- @field exit_timeout integer|false
--- @class vim.lsp.ClientConfig --- @class vim.lsp.ClientConfig
--- command string[] that launches the language
--- server (treated as in |jobstart()|, must be absolute or on `$PATH`, shell constructs like
--- "~" are not expanded), or function that creates an RPC client. Function receives
--- a `dispatchers` table and returns a table with member functions `request`, `notify`,
--- `is_closing` and `terminate`.
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
--- ---
--- Directory to launch the `cmd` process. Not related to `root_dir`. --- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters
--- (default: cwd) --- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
--- @field cmd_cwd? string --- You can use this to modify parameters before they are sent.
--- --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
--- Environment flags to pass to the LSP on spawn.
--- Must be specified using a table.
--- Non-string values are coerced to string.
--- Example:
--- ```lua
--- { PORT = 8080; HOST = "0.0.0.0"; }
--- ```
--- @field cmd_env? table
---
--- Daemonize the server process so that it runs in a separate process group from Nvim.
--- Nvim will shutdown the process on exit, but if Nvim fails to exit cleanly this could leave
--- behind orphaned server processes.
--- (default: true)
--- @field detached? boolean
---
--- List of workspace folders passed to the language server.
--- For backwards compatibility rootUri and rootPath will be derived from the first workspace
--- folder in this list. See `workspaceFolders` in the LSP spec.
--- @field workspace_folders? lsp.WorkspaceFolder[]
--- ---
--- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, --- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|,
--- passed to the language server on initialization. Hint: use make_client_capabilities() and modify --- passed to the language server on initialization. Hint: use make_client_capabilities() and modify
@@ -70,93 +43,143 @@ local validate = vim.validate
--- array. --- array.
--- @field capabilities? lsp.ClientCapabilities --- @field capabilities? lsp.ClientCapabilities
--- ---
--- Map of language server method names to |lsp-handler| --- Command `string[]` that launches the language server (treated as in |jobstart()|, must be
--- @field handlers? table<string,function> --- absolute or on `$PATH`, shell constructs like "~" are not expanded), or function that creates an
--- RPC client. Function receives a `dispatchers` table and the resolved `config`, and must return
--- a table with member functions `request`, `notify`, `is_closing` and `terminate`.
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient
--- ---
--- Map with language server specific settings. --- Directory to launch the `cmd` process. Not related to `root_dir`.
--- See the {settings} in |vim.lsp.Client|. --- (default: cwd)
--- @field settings? lsp.LSPObject --- @field cmd_cwd? string
--- ---
--- Table that maps string of clientside commands to user-defined functions. --- Environment variables passed to the LSP process on spawn. Non-string values are coerced to
--- Commands passed to `start()` take precedence over the global command registry. Each key --- string.
--- must be a unique command name, and the value is a function which is called if any LSP action --- Example:
--- (code action, code lenses, ...) triggers the command. --- ```lua
--- { PORT = 8080; HOST = '0.0.0.0'; }
--- ```
--- @field cmd_env? table
---
--- Client commands. Map of command names to user-defined functions. Commands passed to `start()`
--- take precedence over the global command registry. Each key must be a unique command name, and
--- the value is a function which is called if any LSP action (code action, code lenses, …) triggers
--- the command.
--- @field commands? table<string,fun(command: lsp.Command, ctx: table)> --- @field commands? table<string,fun(command: lsp.Command, ctx: table)>
--- ---
--- Daemonize the server process so that it runs in a separate process group from Nvim.
--- Nvim will shutdown the process on exit, but if Nvim fails to exit cleanly this could leave
--- behind orphaned server processes.
--- (default: `true`)
--- @field detached? boolean
---
--- A table with flags for the client. The current (experimental) flags are:
--- @field flags? vim.lsp.Client.Flags
---
--- Language ID as string. Defaults to the buffer filetype.
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
---
--- Map of LSP method names to |lsp-handler|s.
--- @field handlers? table<string,function>
---
--- Values to pass in the initialization request as `initializationOptions`. See `initialize` in --- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
--- the LSP spec. --- the LSP spec.
--- @field init_options? lsp.LSPObject --- @field init_options? lsp.LSPObject
--- ---
--- Name in log messages. --- Name in logs and user messages.
--- (default: client-id) --- (default: client-id)
--- @field name? string --- @field name? string
--- ---
--- Language ID as string. Defaults to the buffer filetype. --- Called "position encoding" in LSP spec. The encoding that the LSP server expects, used for
--- @field get_language_id? fun(bufnr: integer, filetype: string): string --- communication. Not validated. Can be modified in `on_init` before text is sent to the server.
---
--- Called "position encoding" in LSP spec, the encoding that the LSP server expects.
--- Client does not verify this is correct.
--- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32' --- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32'
--- ---
--- Callback invoked when client attaches to a buffer.
--- @field on_attach? elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>
---
--- Callback invoked when the client operation throws an error. `code` is a number describing the error. --- Callback invoked when the client operation throws an error. `code` is a number describing the error.
--- Other arguments may be passed depending on the error kind. See `vim.lsp.rpc.client_errors` --- Other arguments may be passed depending on the error kind. See `vim.lsp.rpc.client_errors`
--- for possible errors. Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name. --- for possible errors. Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
--- @field on_error? fun(code: integer, err: string) --- @field on_error? fun(code: integer, err: string)
--- ---
--- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters
--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
--- You can use this to modify parameters before they are sent.
--- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
---
--- Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and
--- anything else the server may send. For example, clangd sends `init_result.offsetEncoding` if
--- `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding`
--- here before any notifications are sent.
--- @field on_init? elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>
---
--- Callback invoked on client exit. --- Callback invoked on client exit.
--- - code: exit code of the process --- - code: exit code of the process
--- - signal: number describing the signal used to terminate (if any) --- - signal: number describing the signal used to terminate (if any)
--- - client_id: client handle --- - client_id: client handle
--- @field on_exit? elem_or_list<fun(code: integer, signal: integer, client_id: integer)> --- @field on_exit? elem_or_list<fun(code: integer, signal: integer, client_id: integer)>
--- ---
--- Callback invoked when client attaches to a buffer. --- Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and
--- @field on_attach? elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)> --- anything else the server may send. For example, clangd sends `init_result.offsetEncoding` if
--- `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding`
--- here before any notifications are sent.
--- @field on_init? elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>
---
--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on initialization.
--- @field root_dir? string
---
--- Map of language server-specific settings, decided by the client. Sent to the LS if requested via
--- `workspace/configuration`. Keys are case-sensitive.
--- @field settings? lsp.LSPObject
--- ---
--- Passed directly to the language server in the initialize request. Invalid/empty values will --- Passed directly to the language server in the initialize request. Invalid/empty values will
--- (default: "off") --- (default: "off")
--- @field trace? 'off'|'messages'|'verbose' --- @field trace? 'off'|'messages'|'verbose'
--- ---
--- A table with flags for the client. The current (experimental) flags are: --- List of workspace folders passed to the language server. For backwards compatibility rootUri and
--- @field flags? vim.lsp.Client.Flags --- rootPath are derived from the first workspace folder in this list. Can be `null` if the client
--- supports workspace folders but none are configured. See `workspaceFolders` in LSP spec.
--- @field workspace_folders? lsp.WorkspaceFolder[]
--- ---
--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on initialization. --- Server requires a workspace (no "single file" support). Note: Without
--- @field root_dir? string --- a workspace, cross-file features (navigation, hover) may or may not work depending on the
--- language server, even if the server doesn't require a workspace.
--- (default: `false`)
--- @field workspace_required? boolean
--- @class vim.lsp.Client.Progress: vim.Ringbuf<{token: integer|string, value: any}> --- @class vim.lsp.Client.Progress: vim.Ringbuf<{token: integer|string, value: any}>
--- @field pending table<lsp.ProgressToken,lsp.LSPAny> --- @field pending table<lsp.ProgressToken,lsp.LSPAny>
--- @class vim.lsp.Client --- @class vim.lsp.Client
--- ---
--- @field attached_buffers table<integer,true>
---
--- Capabilities provided by the client (editor or tool), at startup.
--- @field capabilities lsp.ClientCapabilities
---
--- Client commands. See [vim.lsp.ClientConfig].
--- @field commands table<string,fun(command: lsp.Command, ctx: table)>
---
--- Copy of the config passed to |vim.lsp.start()|.
--- @field config vim.lsp.ClientConfig
---
--- Capabilities provided at runtime (after startup).
--- @field dynamic_capabilities lsp.DynamicCapabilities
---
--- A table with flags for the client. The current (experimental) flags are:
--- @field flags vim.lsp.Client.Flags
---
--- See [vim.lsp.ClientConfig].
--- @field get_language_id fun(bufnr: integer, filetype: string): string
---
--- See [vim.lsp.ClientConfig].
--- @field handlers table<string,lsp.Handler>
---
--- The id allocated to the client. --- The id allocated to the client.
--- @field id integer --- @field id integer
--- ---
--- If a name is specified on creation, that will be used. Otherwise it is just --- @field initialized true?
--- the client id. This is used for logs and messages. ---
--- See [vim.lsp.ClientConfig].
--- @field name string --- @field name string
--- ---
--- RPC client object, for low level interaction with the client. --- See [vim.lsp.ClientConfig].
--- See |vim.lsp.rpc.start()|.
--- @field rpc vim.lsp.rpc.PublicClient
---
--- Called "position encoding" in LSP spec,
--- the encoding used for communicating with the server.
--- You can modify this in the `config`'s `on_init` method
--- before text is sent to the server.
--- @field offset_encoding string --- @field offset_encoding string
--- ---
--- The handlers used by the client as described in |lsp-handler|. --- A ring buffer (|vim.ringbuf()|) containing progress messages
--- @field handlers table<string,lsp.Handler> --- sent by the server.
--- @field progress vim.lsp.Client.Progress
--- ---
--- The current pending requests in flight to the server. Entries are key-value --- The current pending requests in flight to the server. Entries are key-value
--- pairs with the key being the request id while the value is a table with --- pairs with the key being the request id while the value is a table with
@@ -166,34 +189,25 @@ local validate = vim.validate
--- are received from the server. --- are received from the server.
--- @field requests table<integer,{ type: string, bufnr: integer, method: string}?> --- @field requests table<integer,{ type: string, bufnr: integer, method: string}?>
--- ---
--- copy of the table that was passed by the user --- See [vim.lsp.ClientConfig].
--- to |vim.lsp.start()|.
--- @field config vim.lsp.ClientConfig
---
--- Response from the server sent on `initialize` describing the server's
--- capabilities.
--- @field server_capabilities lsp.ServerCapabilities?
---
--- Response from the server sent on `initialize` describing information about
--- the server.
--- @field server_info lsp.ServerInfo?
---
--- A ring buffer (|vim.ringbuf()|) containing progress messages
--- sent by the server.
--- @field progress vim.lsp.Client.Progress
---
--- @field initialized true?
---
--- The workspace folders configured in the client when the server starts.
--- This property is only available if the client supports workspace folders.
--- It can be `null` if the client supports workspace folders but none are
--- configured.
--- @field workspace_folders lsp.WorkspaceFolder[]?
--- @field root_dir string? --- @field root_dir string?
--- ---
--- @field attached_buffers table<integer,true> --- RPC client object, for low level interaction with the client.
--- See |vim.lsp.rpc.start()|.
--- @field rpc vim.lsp.rpc.PublicClient
---
--- Response from the server sent on `initialize` describing the server's capabilities.
--- @field server_capabilities lsp.ServerCapabilities?
---
--- Response from the server sent on `initialize` describing server information (e.g. version).
--- @field server_info lsp.ServerInfo?
---
--- See [vim.lsp.ClientConfig].
--- @field settings lsp.LSPObject
---
--- See [vim.lsp.ClientConfig].
--- @field workspace_folders lsp.WorkspaceFolder[]?
--- ---
--- @field private _log_prefix string
--- ---
--- Track this so that we can escalate automatically if we've already tried a --- Track this so that we can escalate automatically if we've already tried a
--- graceful shutdown --- graceful shutdown
@@ -203,26 +217,8 @@ local validate = vim.validate
--- trace = "off" | "messages" | "verbose"; --- trace = "off" | "messages" | "verbose";
--- @field private _trace 'off'|'messages'|'verbose' --- @field private _trace 'off'|'messages'|'verbose'
--- ---
--- Table of command name to function which is called if any LSP action
--- (code action, code lenses, ...) triggers the command.
--- Client commands take precedence over the global command registry.
--- @field commands table<string,fun(command: lsp.Command, ctx: table)>
---
--- Map with language server specific settings. These are returned to the
--- language server if requested via `workspace/configuration`. Keys are
--- case-sensitive.
--- @field settings lsp.LSPObject
---
--- A table with flags for the client. The current (experimental) flags are:
--- @field flags vim.lsp.Client.Flags
---
--- @field get_language_id fun(bufnr: integer, filetype: string): string
---
--- The capabilities provided by the client (editor or tool)
--- @field capabilities lsp.ClientCapabilities
--- @field private registrations table<string,lsp.Registration[]> --- @field private registrations table<string,lsp.Registration[]>
--- @field dynamic_capabilities lsp.DynamicCapabilities --- @field private _log_prefix string
---
--- @field private _before_init_cb? vim.lsp.client.before_init_cb --- @field private _before_init_cb? vim.lsp.client.before_init_cb
--- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[] --- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[]
--- @field private _on_init_cbs vim.lsp.client.on_init_cb[] --- @field private _on_init_cbs vim.lsp.client.on_init_cb[]
@@ -392,6 +388,7 @@ function Client.create(config)
capabilities = config.capabilities, capabilities = config.capabilities,
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir), workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
root_dir = config.root_dir, root_dir = config.root_dir,
_is_stopping = false,
_before_init_cb = config.before_init, _before_init_cb = config.before_init,
_on_init_cbs = vim._ensure_list(config.on_init), _on_init_cbs = vim._ensure_list(config.on_init),
_on_exit_cbs = vim._ensure_list(config.on_exit), _on_exit_cbs = vim._ensure_list(config.on_exit),
@@ -458,7 +455,7 @@ function Client.create(config)
-- Start the RPC client. -- Start the RPC client.
local config_cmd = config.cmd local config_cmd = config.cmd
if type(config_cmd) == 'function' then if type(config_cmd) == 'function' then
self.rpc = config_cmd(dispatchers) self.rpc = config_cmd(dispatchers, config)
else else
self.rpc = lsp.rpc.start(config_cmd, dispatchers, { self.rpc = lsp.rpc.start(config_cmd, dispatchers, {
cwd = config.cmd_cwd, cwd = config.cmd_cwd,
@@ -675,6 +672,12 @@ function Client:request(method, params, handler, bufnr)
bufnr = vim._resolve_bufnr(bufnr) bufnr = vim._resolve_bufnr(bufnr)
local version = lsp.util.buf_versions[bufnr] local version = lsp.util.buf_versions[bufnr]
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr) log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
-- Detect if request resolved synchronously (only possible with in-process servers).
local already_responded = false
local request_registered = false
-- NOTE: rpc.request might call an in-process (Lua) server, thus may be synchronous.
local success, request_id = self.rpc.request(method, params, function(err, result) local success, request_id = self.rpc.request(method, params, function(err, result)
handler(err, result, { handler(err, result, {
method = method, method = method,
@@ -685,11 +688,15 @@ function Client:request(method, params, handler, bufnr)
}) })
end, function(request_id) end, function(request_id)
-- Called when the server sends a response to the request (including cancelled acknowledgment). -- Called when the server sends a response to the request (including cancelled acknowledgment).
self:_process_request(request_id, 'complete') if request_registered then
self:_process_request(request_id, 'complete')
end
already_responded = true
end) end)
if success and request_id then if success and request_id and not already_responded then
self:_process_request(request_id, 'pending', bufnr, method) self:_process_request(request_id, 'pending', bufnr, method)
request_registered = true
end end
return success, request_id return success, request_id
@@ -799,12 +806,13 @@ end
--- ---
--- @param force? boolean --- @param force? boolean
function Client:stop(force) function Client:stop(force)
local rpc = self.rpc if self:is_stopped() then
if rpc.is_closing() then
return return
end end
self._is_stopping = true
local rpc = self.rpc
vim.lsp._watchfiles.cancel(self.id) vim.lsp._watchfiles.cancel(self.id)
if force or not self.initialized or self._graceful_shutdown_failed then if force or not self.initialized or self._graceful_shutdown_failed then
@@ -930,7 +938,7 @@ end
--- @return boolean # true if client is stopped or in the process of being --- @return boolean # true if client is stopped or in the process of being
--- stopped; false otherwise --- stopped; false otherwise
function Client:is_stopped() function Client:is_stopped()
return self.rpc.is_closing() return self.rpc.is_closing() or self._is_stopping
end end
--- Execute a lsp command, either via client command function (if available) --- Execute a lsp command, either via client command function (if available)

View File

@@ -370,7 +370,7 @@ end
local function adjust_start_col(lnum, line, items, encoding) local function adjust_start_col(lnum, line, items, encoding)
local min_start_char = nil local min_start_char = nil
for _, item in pairs(items) do for _, item in pairs(items) do
if item.textEdit and item.textEdit.range.start.line == lnum then if item.textEdit and item.textEdit.range and item.textEdit.range.start.line == lnum then
if min_start_char and min_start_char ~= item.textEdit.range.start.character then if min_start_char and min_start_char ~= item.textEdit.range.start.character then
return nil return nil
end end
@@ -506,14 +506,19 @@ local function trigger(bufnr, clients, ctx)
local matches = {} local matches = {}
local server_start_boundary --- @type integer? local server_start_boundary --- @type integer?
for client_id, response in pairs(responses) do for client_id, response in pairs(responses) do
local client = lsp.get_client_by_id(client_id)
if response.err then if response.err then
vim.notify_once(response.err.message, vim.log.levels.WARN) local msg = ('%s: %s %s'):format(
client and client.name or 'UNKNOWN',
response.err.code or 'NO_CODE',
response.err.message
)
vim.notify_once(msg, vim.log.levels.WARN)
end end
local result = response.result local result = response.result
if result then if result then
Context.isIncomplete = Context.isIncomplete or result.isIncomplete Context.isIncomplete = Context.isIncomplete or result.isIncomplete
local client = lsp.get_client_by_id(client_id)
local encoding = client and client.offset_encoding or 'utf-16' local encoding = client and client.offset_encoding or 'utf-16'
local client_matches local client_matches
client_matches, server_start_boundary = M._convert_results( client_matches, server_start_boundary = M._convert_results(

View File

@@ -93,12 +93,16 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
message = diagnostic.message.value message = diagnostic.message.value
end end
local line = buf_lines and buf_lines[start.line + 1] or '' local line = buf_lines and buf_lines[start.line + 1] or ''
local end_line = line
if _end.line > start.line then
end_line = buf_lines and buf_lines[_end.line + 1] or ''
end
--- @type vim.Diagnostic --- @type vim.Diagnostic
return { return {
lnum = start.line, lnum = start.line,
col = vim.str_byteindex(line, position_encoding, start.character, false), col = vim.str_byteindex(line, position_encoding, start.character, false),
end_lnum = _end.line, end_lnum = _end.line,
end_col = vim.str_byteindex(line, position_encoding, _end.character, false), end_col = vim.str_byteindex(end_line, position_encoding, _end.character, false),
severity = severity_lsp_to_vim(diagnostic.severity), severity = severity_lsp_to_vim(diagnostic.severity),
message = message, message = message,
source = diagnostic.source, source = diagnostic.source,

View File

@@ -111,8 +111,8 @@ local function check_watcher()
watchfunc_name = 'libuv-watch' watchfunc_name = 'libuv-watch'
elseif watchfunc == vim._watch.watchdirs then elseif watchfunc == vim._watch.watchdirs then
watchfunc_name = 'libuv-watchdirs' watchfunc_name = 'libuv-watchdirs'
elseif watchfunc == vim._watch.inotifywait then elseif watchfunc == vim._watch.inotify then
watchfunc_name = 'inotifywait' watchfunc_name = 'inotify'
else else
local nm = debug.getinfo(watchfunc, 'S').source local nm = debug.getinfo(watchfunc, 'S').source
watchfunc_name = string.format('Custom (%s)', nm) watchfunc_name = string.format('Custom (%s)', nm)
@@ -187,26 +187,32 @@ local function check_enabled_configs()
local config = vim.lsp.config[name] local config = vim.lsp.config[name]
local text = {} --- @type string[] local text = {} --- @type string[]
text[#text + 1] = ('%s:'):format(name) text[#text + 1] = ('%s:'):format(name)
for k, v in if not config then
vim.spairs(config --[[@as table<string,any>]]) report_warn(
do ("'%s' config not found. Ensure that vim.lsp.config('%s') was called."):format(name, name)
local v_str --- @type string? )
if k == 'name' then else
v_str = nil for k, v in
elseif k == 'filetypes' or k == 'root_markers' then vim.spairs(config --[[@as table<string,any>]])
v_str = table.concat(v, ', ') do
elseif type(v) == 'function' then local v_str --- @type string?
v_str = func_tostring(v) if k == 'name' then
else v_str = nil
v_str = vim.inspect(v, { newline = '\n ' }) elseif k == 'filetypes' then
end v_str = table.concat(v, ', ')
elseif type(v) == 'function' then
v_str = func_tostring(v)
else
v_str = vim.inspect(v, { newline = '\n ' })
end
if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then
report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1])) report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1]))
end end
if v_str then if v_str then
text[#text + 1] = ('- %s: %s'):format(k, v_str) text[#text + 1] = ('- %s: %s'):format(k, v_str)
end
end end
end end
text[#text + 1] = '' text[#text + 1] = ''

View File

@@ -94,10 +94,10 @@ function M.on_refresh(err, _, ctx)
for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do
for _, winid in ipairs(api.nvim_list_wins()) do for _, winid in ipairs(api.nvim_list_wins()) do
if api.nvim_win_get_buf(winid) == bufnr then if api.nvim_win_get_buf(winid) == bufnr then
if bufstates[bufnr] then if bufstates[bufnr] and bufstates[bufnr].enabled then
bufstates[bufnr].applied = {} bufstates[bufnr].applied = {}
util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr })
end end
util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr })
end end
end end
end end

View File

@@ -1,4 +1,25 @@
-- Logger for language client plugin. --- @brief
--- The `vim.lsp.log` module provides logging for the Nvim LSP client.
---
--- When debugging language servers, it is helpful to enable extra-verbose logging of the LSP client
--- RPC events. Example:
--- ```lua
--- vim.lsp.set_log_level 'trace'
--- require('vim.lsp.log').set_format_func(vim.inspect)
--- ```
---
--- Then try to run the language server, and open the log with:
--- ```vim
--- :lua vim.cmd('tabnew ' .. vim.lsp.get_log_path())
--- ```
---
--- (Or use `:LspLog` if you have nvim-lspconfig installed.)
---
--- Note:
--- - Remember to DISABLE verbose logging ("debug" or "trace" level), else you may encounter
--- performance issues.
--- - "ERROR" messages containing "stderr" only indicate that the log was sent to stderr. Many
--- servers send harmless messages via stderr.
local log = {} local log = {}

View File

@@ -162,14 +162,13 @@ local constants = {
MethodNotFound = -32601, MethodNotFound = -32601,
InvalidParams = -32602, InvalidParams = -32602,
InternalError = -32603, InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002, ServerNotInitialized = -32002,
UnknownErrorCode = -32001, UnknownErrorCode = -32001,
-- Defined by the protocol. -- Defined by the protocol.
RequestCancelled = -32800, RequestCancelled = -32800,
ContentModified = -32801, ContentModified = -32801,
ServerCancelled = -32802, ServerCancelled = -32802,
RequestFailed = -32803,
}, },
-- Describes the content type that a client supports in various -- Describes the content type that a client supports in various
@@ -329,6 +328,7 @@ end
--- capabilities. --- capabilities.
--- @return lsp.ClientCapabilities --- @return lsp.ClientCapabilities
function protocol.make_client_capabilities() function protocol.make_client_capabilities()
---@type lsp.ClientCapabilities
return { return {
general = { general = {
positionEncodings = { positionEncodings = {
@@ -340,6 +340,9 @@ function protocol.make_client_capabilities()
textDocument = { textDocument = {
diagnostic = { diagnostic = {
dynamicRegistration = false, dynamicRegistration = false,
tagSupport = {
valueSet = get_value_set(constants.DiagnosticTag),
},
}, },
inlayHint = { inlayHint = {
dynamicRegistration = true, dynamicRegistration = true,
@@ -436,6 +439,9 @@ function protocol.make_client_capabilities()
foldingRange = { foldingRange = {
dynamicRegistration = false, dynamicRegistration = false,
lineFoldingOnly = true, lineFoldingOnly = true,
foldingRangeKind = {
valueSet = { 'comment', 'imports', 'region' },
},
foldingRange = { foldingRange = {
collapsedText = true, collapsedText = true,
}, },

View File

@@ -24,6 +24,7 @@ local border_size = {
rounded = { 2, 2 }, rounded = { 2, 2 },
solid = { 2, 2 }, solid = { 2, 2 },
shadow = { 1, 1 }, shadow = { 1, 1 },
bold = { 2, 2 },
} }
--- Check the border given by opts or the default border for the additional --- Check the border given by opts or the default border for the additional
@@ -744,6 +745,10 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
if type(doc) == 'string' then if type(doc) == 'string' then
signature.documentation = { kind = 'plaintext', value = doc } signature.documentation = { kind = 'plaintext', value = doc }
end end
-- Add delimiter if there is documentation to display
if signature.documentation.value ~= '' then
contents[#contents + 1] = '---'
end
M.convert_input_to_markdown_lines(signature.documentation, contents) M.convert_input_to_markdown_lines(signature.documentation, contents)
end end
if signature.parameters and #signature.parameters > 0 then if signature.parameters and #signature.parameters > 0 then
@@ -860,7 +865,7 @@ function M.make_floating_popup_options(width, height, opts)
col = 1 col = 1
end end
local title = (opts.border and opts.title) and opts.title or nil local title = ((opts.border or vim.o.winborder ~= '') and opts.title) and opts.title or nil
local title_pos --- @type 'left'|'center'|'right'? local title_pos --- @type 'left'|'center'|'right'?
if title then if title then
@@ -1138,10 +1143,8 @@ function M.stylize_markdown(bufnr, contents, opts)
-- Clean up -- Clean up
contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true })
local stripped = {} local stripped = {} --- @type string[]
local highlights = {} --- @type {ft:string,start:integer,finish:integer}[] local highlights = {} --- @type {ft:string,start:integer,finish:integer}[]
-- keep track of lnums that contain markdown
local markdown_lines = {} --- @type table<integer,boolean>
local i = 1 local i = 1
while i <= #contents do while i <= #contents do
@@ -1156,7 +1159,7 @@ function M.stylize_markdown(bufnr, contents, opts)
i = i + 1 i = i + 1
break break
end end
table.insert(stripped, line) stripped[#stripped + 1] = line
i = i + 1 i = i + 1
end end
table.insert(highlights, { table.insert(highlights, {
@@ -1166,30 +1169,23 @@ function M.stylize_markdown(bufnr, contents, opts)
}) })
-- add a separator, but not on the last line -- add a separator, but not on the last line
if opts.separator and i < #contents then if opts.separator and i < #contents then
table.insert(stripped, '---') stripped[#stripped + 1] = '---'
markdown_lines[#stripped] = true
end end
else else
-- strip any empty lines or separators prior to this separator in actual markdown -- strip any empty lines or separators prior to this separator in actual markdown
if line:match('^---+$') then if line:match('^---+$') then
while while
markdown_lines[#stripped] stripped[#stripped]
and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$'))
do do
markdown_lines[#stripped] = false stripped[#stripped] = nil
table.remove(stripped, #stripped)
end end
end end
-- add the line if its not an empty line following a separator -- add the line if its not an empty line following a separator
if if
not ( not (line:match('^%s*$') and stripped[#stripped] and stripped[#stripped]:match('^---+$'))
line:match('^%s*$')
and markdown_lines[#stripped]
and stripped[#stripped]:match('^---+$')
)
then then
table.insert(stripped, line) stripped[#stripped + 1] = line
markdown_lines[#stripped] = true
end end
i = i + 1 i = i + 1
end end
@@ -1220,7 +1216,7 @@ function M.stylize_markdown(bufnr, contents, opts)
local sep_line = string.rep('', math.min(width, opts.wrap_at or width)) local sep_line = string.rep('', math.min(width, opts.wrap_at or width))
for l in pairs(markdown_lines) do for l in ipairs(stripped) do
if stripped[l]:match('^---+$') then if stripped[l]:match('^---+$') then
stripped[l] = sep_line stripped[l] = sep_line
end end
@@ -1348,26 +1344,34 @@ end
--- ---
---@param events table list of events ---@param events table list of events
---@param winnr integer window id of preview window ---@param winnr integer window id of preview window
---@param bufnrs table list of buffers where the preview window will remain visible ---@param floating_bufnr integer floating preview buffer
---@param bufnr integer buffer that opened the floating preview buffer
---@see autocmd-events ---@see autocmd-events
local function close_preview_autocmd(events, winnr, bufnrs) local function close_preview_autocmd(events, winnr, floating_bufnr, bufnr)
local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, { local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, {
clear = true, clear = true,
}) })
-- close the preview window when entered a buffer that is not -- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it -- the floating window buffer or the buffer that spawned it
api.nvim_create_autocmd('BufEnter', { api.nvim_create_autocmd('BufLeave', {
group = augroup, group = augroup,
buffer = bufnr,
callback = function() callback = function()
close_preview_window(winnr, bufnrs) vim.schedule(function()
-- When jumping to the quickfix window from the preview window,
-- do not close the preview window.
if api.nvim_get_option_value('filetype', { buf = 0 }) ~= 'qf' then
close_preview_window(winnr, { floating_bufnr, bufnr })
end
end)
end, end,
}) })
if #events > 0 then if #events > 0 then
api.nvim_create_autocmd(events, { api.nvim_create_autocmd(events, {
group = augroup, group = augroup,
buffer = bufnrs[2], buffer = bufnr,
callback = function() callback = function()
close_preview_window(winnr) close_preview_window(winnr)
end, end,
@@ -1614,7 +1618,7 @@ function M.open_floating_preview(contents, syntax, opts)
'<cmd>bdelete<cr>', '<cmd>bdelete<cr>',
{ silent = true, noremap = true, nowait = true } { silent = true, noremap = true, nowait = true }
) )
close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) close_preview_autocmd(opts.close_events, floating_winnr, floating_bufnr, bufnr)
-- save focus_id -- save focus_id
if opts.focus_id then if opts.focus_id then
@@ -1801,7 +1805,7 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
'symbols_to_items must be called with valid position encoding', 'symbols_to_items must be called with valid position encoding',
vim.log.levels.WARN vim.log.levels.WARN
) )
position_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding position_encoding = vim.lsp.get_clients({ bufnr = bufnr })[1].offset_encoding
end end
local items = {} --- @type vim.quickfix.entry[] local items = {} --- @type vim.quickfix.entry[]

View File

@@ -107,9 +107,22 @@ local function provider_disabled(provider)
return false return false
end end
--- Checks the hygiene of a `g:loaded_xx_provider` variable.
local function check_loaded_var(var)
if vim.g[var] == 1 then
health.error(('`g:%s=1` may have been set by mistake.'):format(var), {
('Remove `vim.g.%s=1` from your config.'):format(var),
'To disable the provider, set this to 0, not 1.',
'If you want to enable the provider but skip automatic detection, set the respective `g:…_host_prog` var. See :help provider',
})
end
end
local function clipboard() local function clipboard()
health.start('Clipboard (optional)') health.start('Clipboard (optional)')
check_loaded_var('loaded_clipboard_provider')
if if
os.getenv('TMUX') os.getenv('TMUX')
and vim.fn.executable('tmux') == 1 and vim.fn.executable('tmux') == 1
@@ -144,6 +157,8 @@ end
local function node() local function node()
health.start('Node.js provider (optional)') health.start('Node.js provider (optional)')
check_loaded_var('loaded_node_provider')
if provider_disabled('node') then if provider_disabled('node') then
return return
end end
@@ -247,6 +262,8 @@ end
local function perl() local function perl()
health.start('Perl provider (optional)') health.start('Perl provider (optional)')
check_loaded_var('loaded_perl_provider')
if provider_disabled('perl') then if provider_disabled('perl') then
return return
end end
@@ -256,7 +273,7 @@ local function perl()
if not perl_exec then if not perl_exec then
health.warn(assert(perl_warnings), { health.warn(assert(perl_warnings), {
'See :help provider-perl for more information.', 'See :help provider-perl for more information.',
'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim', 'You can disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim',
}) })
health.warn('No usable perl executable found') health.warn('No usable perl executable found')
return return
@@ -547,7 +564,7 @@ local function version_info(python)
local nvim_path_base = vim.fn.fnamemodify(nvim_path, [[:~:h]]) local nvim_path_base = vim.fn.fnamemodify(nvim_path, [[:~:h]])
local version_status = 'unknown; ' .. nvim_path_base local version_status = 'unknown; ' .. nvim_path_base
if is_bad_response(nvim_version) and is_bad_response(pypi_version) then if not is_bad_response(nvim_version) and not is_bad_response(pypi_version) then
if vim.version.lt(nvim_version, pypi_version) then if vim.version.lt(nvim_version, pypi_version) then
version_status = 'outdated; from ' .. nvim_path_base version_status = 'outdated; from ' .. nvim_path_base
else else
@@ -561,6 +578,8 @@ end
local function python() local function python()
health.start('Python 3 provider (optional)') health.start('Python 3 provider (optional)')
check_loaded_var('loaded_python3_provider')
local python_exe = '' local python_exe = ''
local virtual_env = os.getenv('VIRTUAL_ENV') local virtual_env = os.getenv('VIRTUAL_ENV')
local venv = virtual_env and vim.fn.resolve(virtual_env) or '' local venv = virtual_env and vim.fn.resolve(virtual_env) or ''
@@ -595,7 +614,7 @@ local function python()
if pythonx_warnings then if pythonx_warnings then
health.warn(pythonx_warnings, { health.warn(pythonx_warnings, {
'See :help provider-python for more information.', 'See :help provider-python for more information.',
'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim', 'You can disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim',
}) })
elseif pyname and pyname ~= '' and python_exe == '' then elseif pyname and pyname ~= '' and python_exe == '' then
if not vim.g[host_prog_var] then if not vim.g[host_prog_var] then
@@ -705,10 +724,9 @@ local function python()
local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": ' local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": '
.. pynvim_exe .. pynvim_exe
local advice = { 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 uninstall pynvim neovim',
pynvim_exe .. ' -m pip install pynvim', 'Then see :help provider-python for "pynvim" installation steps.',
pynvim_exe .. ' -m pip install neovim # only if needed by third-party software',
} }
health.error(message, advice) health.error(message, advice)
end end
@@ -734,7 +752,7 @@ local function python()
if is_bad_response(current) then if is_bad_response(current) then
health.error( health.error(
'pynvim is not installed.\nError: ' .. current, 'pynvim is not installed.\nError: ' .. current,
'Run in shell: ' .. python_exe .. ' -m pip install pynvim' 'See :help provider-python for "pynvim" installation steps.'
) )
end end
@@ -840,6 +858,8 @@ end
local function ruby() local function ruby()
health.start('Ruby provider (optional)') health.start('Ruby provider (optional)')
check_loaded_var('loaded_ruby_provider')
if provider_disabled('ruby') then if provider_disabled('ruby') then
return return
end end
@@ -860,7 +880,7 @@ local function ruby()
'Run `gem environment` to ensure the gem bin directory is in $PATH.', 'Run `gem environment` to ensure the gem bin directory is in $PATH.',
'If you are using rvm/rbenv/chruby, try "rehashing".', 'If you are using rvm/rbenv/chruby, try "rehashing".',
'See :help g:ruby_host_prog for non-standard gem installations.', 'See :help g:ruby_host_prog for non-standard gem installations.',
'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim', 'You can disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim',
}) })
return return
end end

View File

@@ -1,5 +1,5 @@
local M = {} local M = {}
local min_version = '3.7' local min_version = '3.9'
local s_err ---@type string? local s_err ---@type string?
local s_host ---@type string? local s_host ---@type string?
@@ -83,6 +83,10 @@ function M.detect_by_module(module)
return vim.fn.exepath(vim.fn.expand(python_exe, true)), nil return vim.fn.exepath(vim.fn.expand(python_exe, true)), nil
end end
if vim.fn.executable('pynvim-python') == 1 then
return 'pynvim-python'
end
local errors = {} local errors = {}
for _, exe in ipairs(python_candidates) do for _, exe in ipairs(python_candidates) do
local error = check_for_module(exe, module) local error = check_for_module(exe, module)

View File

@@ -21,6 +21,50 @@ local function read_trust()
return trust return trust
end end
--- If {fullpath} is a file, read the contents of {fullpath} (or the contents of {bufnr}
--- if given) and returns the contents and a hash of the contents.
---
--- If {fullpath} is a directory, then nothing is read from the filesystem, and
--- `contents = true` and `hash = "directory"` is returned instead.
---
---@param fullpath (string) Path to a file or directory to read.
---@param bufnr (number?) The number of the buffer.
---@return string|boolean? contents the contents of the file, or true if it's a directory
---@return string? hash the hash of the contents, or "directory" if it's a directory
local function compute_hash(fullpath, bufnr)
local contents ---@type string|boolean?
local hash ---@type string
if vim.fn.isdirectory(fullpath) == 1 then
return true, 'directory'
end
if bufnr then
local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n'
contents =
table.concat(vim.api.nvim_buf_get_lines(bufnr --[[@as integer]], 0, -1, false), newline)
if vim.bo[bufnr].endofline then
contents = contents .. newline
end
else
do
local f = io.open(fullpath, 'r')
if not f then
return nil, nil
end
contents = f:read('*a')
f:close()
end
if not contents then
return nil, nil
end
end
hash = vim.fn.sha256(contents)
return contents, hash
end
--- Writes provided {trust} table to trust database at --- Writes provided {trust} table to trust database at
--- $XDG_STATE_HOME/nvim/trust. --- $XDG_STATE_HOME/nvim/trust.
--- ---
@@ -37,17 +81,22 @@ local function write_trust(trust)
f:close() f:close()
end end
--- Attempt to read the file at {path} prompting the user if the file should be --- If {path} is a file: attempt to read the file, prompting the user if the file should be
--- trusted. The user's choice is persisted in a trust database at --- trusted.
---
--- If {path} is a directory: return true if the directory is trusted (non-recursive), prompting
--- the user as necessary.
---
--- The user's choice is persisted in a trust database at
--- $XDG_STATE_HOME/nvim/trust. --- $XDG_STATE_HOME/nvim/trust.
--- ---
---@since 11 ---@since 11
---@see |:trust| ---@see |:trust|
--- ---
---@param path (string) Path to a file to read. ---@param path (string) Path to a file or directory to read.
--- ---
---@return (string|nil) The contents of the given file if it exists and is ---@return (boolean|string|nil) If {path} is not trusted or does not exist, returns `nil`. Otherwise,
--- trusted, or nil otherwise. --- returns the contents of {path} if it is a file, or true if {path} is a directory.
function M.read(path) function M.read(path)
vim.validate('path', path, 'string') vim.validate('path', path, 'string')
local fullpath = vim.uv.fs_realpath(vim.fs.normalize(path)) local fullpath = vim.uv.fs_realpath(vim.fs.normalize(path))
@@ -62,26 +111,25 @@ function M.read(path)
return nil return nil
end end
local contents ---@type string? local contents, hash = compute_hash(fullpath, nil)
do if not contents then
local f = io.open(fullpath, 'r') return nil
if not f then
return nil
end
contents = f:read('*a')
f:close()
end end
local hash = vim.fn.sha256(contents)
if trust[fullpath] == hash then if trust[fullpath] == hash then
-- File already exists in trust database -- File already exists in trust database
return contents return contents
end end
local dir_msg = ''
if hash == 'directory' then
dir_msg = ' DIRECTORY trust is decided only by its name, not its contents.'
end
-- File either does not exist in trust database or the hash does not match -- File either does not exist in trust database or the hash does not match
local ok, result = pcall( local ok, result = pcall(
vim.fn.confirm, vim.fn.confirm,
string.format('%s is not trusted.', fullpath), string.format('%s is not trusted.%s', fullpath, dir_msg),
'&ignore\n&view\n&deny\n&allow', '&ignore\n&view\n&deny\n&allow',
1 1
) )
@@ -169,13 +217,10 @@ function M.trust(opts)
local trust = read_trust() local trust = read_trust()
if action == 'allow' then if action == 'allow' then
local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' local contents, hash = compute_hash(fullpath, bufnr)
local contents = if not contents then
table.concat(vim.api.nvim_buf_get_lines(bufnr --[[@as integer]], 0, -1, false), newline) return false, string.format('could not read path: %s', fullpath)
if vim.bo[bufnr].endofline then
contents = contents .. newline
end end
local hash = vim.fn.sha256(contents)
trust[fullpath] = hash trust[fullpath] = hash
elseif action == 'deny' then elseif action == 'deny' then

View File

@@ -95,7 +95,7 @@ end
--- ---
--- @see |string.gmatch()| --- @see |string.gmatch()|
--- @see |vim.split()| --- @see |vim.split()|
--- @see |lua-patterns| --- @see |lua-pattern|s
--- @see https://www.lua.org/pil/20.2.html --- @see https://www.lua.org/pil/20.2.html
--- @see http://lua-users.org/wiki/StringLibraryTutorial --- @see http://lua-users.org/wiki/StringLibraryTutorial
--- ---
@@ -784,7 +784,7 @@ end
--- Trim whitespace (Lua pattern "%s") from both sides of a string. --- Trim whitespace (Lua pattern "%s") from both sides of a string.
--- ---
---@see |lua-patterns| ---@see |lua-pattern|s
---@see https://www.lua.org/pil/20.2.html ---@see https://www.lua.org/pil/20.2.html
---@param s string String to trim ---@param s string String to trim
---@return string String with whitespace removed from its beginning and end ---@return string String with whitespace removed from its beginning and end
@@ -793,7 +793,7 @@ function vim.trim(s)
return s:match('^%s*(.*%S)') or '' return s:match('^%s*(.*%S)') or ''
end end
--- Escapes magic chars in |lua-patterns|. --- Escapes magic chars in |lua-pattern|s.
--- ---
---@see https://github.com/rxi/lume ---@see https://github.com/rxi/lume
---@param s string String to escape ---@param s string String to escape
@@ -854,7 +854,7 @@ do
--- @param param_name string --- @param param_name string
--- @param val any --- @param val any
--- @param validator vim.validate.Validator --- @param validator vim.validate.Validator
--- @param message? string --- @param message? string "Expected" message
--- @param allow_alias? boolean Allow short type names: 'n', 's', 't', 'b', 'f', 'c' --- @param allow_alias? boolean Allow short type names: 'n', 's', 't', 'b', 'f', 'c'
--- @return string? --- @return string?
local function is_valid(param_name, val, validator, message, allow_alias) local function is_valid(param_name, val, validator, message, allow_alias)
@@ -866,18 +866,18 @@ do
end end
if not is_type(val, expected) then if not is_type(val, expected) then
return string.format('%s: expected %s, got %s', param_name, expected, type(val)) return ('%s: expected %s, got %s'):format(param_name, message or expected, type(val))
end end
elseif vim.is_callable(validator) then elseif vim.is_callable(validator) then
-- Check user-provided validation function -- Check user-provided validation function
local valid, opt_msg = validator(val) local valid, opt_msg = validator(val)
if not valid then if not valid then
local err_msg = local err_msg = ('%s: expected %s, got %s'):format(
string.format('%s: expected %s, got %s', param_name, message or '?', tostring(val)) param_name,
message or '?',
if opt_msg then tostring(val)
err_msg = string.format('%s. Info: %s', err_msg, opt_msg) )
end err_msg = opt_msg and ('%s. Info: %s'):format(err_msg, opt_msg) or err_msg
return err_msg return err_msg
end end

View File

@@ -119,7 +119,7 @@ local Tabstop = {}
function Tabstop.new(index, bufnr, range, choices) function Tabstop.new(index, bufnr, range, choices)
local extmark_id = vim.api.nvim_buf_set_extmark(bufnr, snippet_ns, range[1], range[2], { local extmark_id = vim.api.nvim_buf_set_extmark(bufnr, snippet_ns, range[1], range[2], {
right_gravity = true, right_gravity = true,
end_right_gravity = true, end_right_gravity = false,
end_line = range[3], end_line = range[3],
end_col = range[4], end_col = range[4],
hl_group = hl_group, hl_group = hl_group,
@@ -170,7 +170,7 @@ function Tabstop:set_right_gravity(right_gravity)
local range = self:get_range() local range = self:get_range()
self.extmark_id = vim.api.nvim_buf_set_extmark(self.bufnr, snippet_ns, range[1], range[2], { self.extmark_id = vim.api.nvim_buf_set_extmark(self.bufnr, snippet_ns, range[1], range[2], {
right_gravity = right_gravity, right_gravity = right_gravity,
end_right_gravity = true, end_right_gravity = not right_gravity,
end_line = range[3], end_line = range[3],
end_col = range[4], end_col = range[4],
hl_group = hl_group, hl_group = hl_group,
@@ -257,10 +257,21 @@ local M = { session = nil }
local function display_choices(tabstop) local function display_choices(tabstop)
assert(tabstop.choices, 'Tabstop has no choices') assert(tabstop.choices, 'Tabstop has no choices')
local text = tabstop:get_text()
local found_text = false
local start_col = tabstop:get_range()[2] + 1 local start_col = tabstop:get_range()[2] + 1
local matches = {} --- @type table[] local matches = {} --- @type table[]
for _, choice in ipairs(tabstop.choices) do for _, choice in ipairs(tabstop.choices) do
matches[#matches + 1] = { word = choice } if choice ~= text then
matches[#matches + 1] = { word = choice }
else
found_text = true
end
end
if found_text then
table.insert(matches, 1, text)
end end
vim.defer_fn(function() vim.defer_fn(function()
@@ -278,17 +289,6 @@ local function select_tabstop(tabstop)
vim.api.nvim_feedkeys(keys, 'n', true) vim.api.nvim_feedkeys(keys, 'n', true)
end end
--- NOTE: We don't use `vim.api.nvim_win_set_cursor` here because it causes the cursor to end
--- at the end of the selection instead of the start.
---
--- @param row integer
--- @param col integer
local function move_cursor_to(row, col)
local line = vim.fn.getline(row) --[[ @as string ]]
col = math.max(vim.fn.strchars(line:sub(1, col)) - 1, 0)
feedkeys(string.format('%sG0%s', row, string.rep('<Right>', col)))
end
local range = tabstop:get_range() local range = tabstop:get_range()
local mode = vim.fn.mode() local mode = vim.fn.mode()
@@ -309,17 +309,21 @@ local function select_tabstop(tabstop)
vim.cmd.startinsert({ bang = range[4] >= #vim.api.nvim_get_current_line() }) vim.cmd.startinsert({ bang = range[4] >= #vim.api.nvim_get_current_line() })
end end
if tabstop.choices then if tabstop.choices then
vim.fn.cursor(range[3] + 1, range[4] + 1)
display_choices(tabstop) display_choices(tabstop)
end end
else else
-- Else, select the tabstop's text. -- Else, select the tabstop's text.
if mode ~= 'n' then -- Need this exact order so cannot mix regular API calls with feedkeys, which
feedkeys('<Esc>') -- are not executed immediately. Use <Cmd> to set the cursor position.
end local keys = {
move_cursor_to(range[1] + 1, range[2] + 1) mode ~= 'n' and '<Esc>' or '',
feedkeys('v') ('<Cmd>call cursor(%s,%s)<CR>'):format(range[1] + 1, range[2] + 1),
move_cursor_to(range[3] + 1, range[4]) 'v',
feedkeys('o<c-g><c-r>_') ('<Cmd>call cursor(%s,%s)<CR>'):format(range[3] + 1, range[4]),
'o<c-g><c-r>_',
}
feedkeys(table.concat(keys))
end end
end end

View File

@@ -40,10 +40,6 @@ local heading_queries = {
]], ]],
} }
local function hash_tick(bufnr)
return tostring(vim.b[bufnr].changedtick)
end
---@class TS.Heading ---@class TS.Heading
---@field bufnr integer ---@field bufnr integer
---@field lnum integer ---@field lnum integer
@@ -53,7 +49,7 @@ end
--- Extract headings from buffer --- Extract headings from buffer
--- @param bufnr integer buffer to extract headings from --- @param bufnr integer buffer to extract headings from
--- @return TS.Heading[] --- @return TS.Heading[]
local get_headings = vim.func._memoize(hash_tick, function(bufnr) local get_headings = function(bufnr)
local lang = ts.language.get_lang(vim.bo[bufnr].filetype) local lang = ts.language.get_lang(vim.bo[bufnr].filetype)
if not lang then if not lang then
return {} return {}
@@ -85,11 +81,13 @@ local get_headings = vim.func._memoize(hash_tick, function(bufnr)
end end
end end
return headings return headings
end) end
--- @param qf_height? integer height of loclist window
--- Shows an Outline (table of contents) of the current buffer, in the loclist. --- Shows an Outline (table of contents) of the current buffer, in the loclist.
function M.show_toc() function M.show_toc(qf_height)
local bufnr = api.nvim_get_current_buf() local bufnr = api.nvim_get_current_buf()
local bufname = api.nvim_buf_get_name(bufnr)
local headings = get_headings(bufnr) local headings = get_headings(bufnr)
if #headings == 0 then if #headings == 0 then
return return
@@ -105,7 +103,10 @@ function M.show_toc()
end end
vim.fn.setloclist(0, headings, ' ') vim.fn.setloclist(0, headings, ' ')
vim.fn.setloclist(0, {}, 'a', { title = 'Table of contents' }) vim.fn.setloclist(0, {}, 'a', { title = 'Table of contents' })
vim.cmd.lopen() vim.cmd.lopen({ count = qf_height })
vim.w.qf_toc = bufname
-- reload syntax file after setting qf_toc variable
vim.bo.filetype = 'qf'
end end
--- Jump to section --- Jump to section

View File

@@ -18,11 +18,14 @@ error('Cannot require a meta file')
---@field captures string[] ---@field captures string[]
---@field patterns table<integer, (integer|string)[][]> ---@field patterns table<integer, (integer|string)[][]>
--- ---
---@class TSLangMetadata
---@field major_version integer
---@field minor_version integer
---@field patch_version integer
---
---@class TSLangInfo ---@class TSLangInfo
---@field abi_version integer ---@field abi_version integer
---@field major_version? integer ---@field metadata? TSLangMetadata -- ABI 15 only
---@field minor_version? integer
---@field patch_version? integer
---@field state_count integer ---@field state_count integer
---@field fields string[] ---@field fields string[]
---@field symbols table<string,boolean> ---@field symbols table<string,boolean>

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