Compare commits

...

174 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
252 changed files with 5667 additions and 1612 deletions

View File

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

View File

@@ -2,21 +2,26 @@
${NVIM_VERSION}
```
## Release notes
- [Changelog](https://github.com/neovim/neovim/commit/${NVIM_COMMIT}) (fixes + features)
- [News](./runtime/doc/news.txt) (`:help news` in Nvim)
## Install
### Windows
#### Zip
1. Download **nvim-win64.zip**
1. Download **nvim-win64.zip** (or **nvim-win-arm64.zip** for ARM)
2. Extract the zip
3. Run `nvim.exe` on your CLI of choice
3. Run `nvim.exe` in your terminal
#### MSI
1. Download **nvim-win64.msi**
1. Download **nvim-win64.msi** (or **nvim-win-arm64.msi** for ARM)
2. Run the MSI
3. Run `nvim.exe` on your CLI of choice
3. Run `nvim.exe` in your terminal
Note: On Windows "Server" you may need to [install vcruntime140.dll](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170).
@@ -75,5 +80,3 @@ If your system does not have the [required glibc version](https://neovim.io/doc/
### Other
- 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:
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:
- uses: actions/checkout@v4
with:
# Perform a full checkout #13471
fetch-depth: 0
- run: .github/scripts/env.ps1
env:
BUILD_ARCH: ${{ matrix.arch }}
- name: Install Wix
run: |
Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" -OutFile "wix314-binaries.zip"
Expand-Archive -Path "wix314-binaries.zip" -DestinationPath "C:/wix"
echo "C:\wix" >> $env:GITHUB_PATH
- name: Build deps
run: |
cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build .deps
- name: build package
- name: Build package
run: |
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
cmake --build build --target package
- uses: actions/upload-artifact@v4
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: nvim-win64
name: nvim-win-${{ matrix.arch }}
path: |
build/nvim-win64.msi
build/nvim-win64.zip
build/${{ matrix.archive_name }}.zip
build/${{ matrix.archive_name }}.msi
retention-days: 1
publish:
@@ -193,25 +210,14 @@ jobs:
echo 'PRERELEASE=') >> $GITHUB_ENV
gh release delete stable --yes || 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
env:
NVIM_VERSION: ${{ needs.linux.outputs.version }}
NVIM_COMMIT: ${{ github.sha }}
DEBUG: api
run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
echo '```' >> "$RUNNER_TEMP/notes.md"
cat shasum.txt >> "$RUNNER_TEMP/notes.md"
echo '```' >> "$RUNNER_TEMP/notes.md"
if [ "$TAG_NAME" != "nightly" ]; then
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/* 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
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.
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:
```sh

View File

@@ -141,8 +141,8 @@ endif()
# version string, else they are combined with the result of `git describe`.
set(NVIM_VERSION_MAJOR 0)
set(NVIM_VERSION_MINOR 11)
set(NVIM_VERSION_PATCH 2)
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
set(NVIM_VERSION_PATCH 5)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level
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/lsp/ => runtime/doc/lsp.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
```

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
[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));
- 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:
```vim
let g:python3_host_prog='C:/Users/foo/Envs/neovim3/Scripts/python.exe'
- For Python plugins you need the `pynvim` module. Installation via uv
(https://docs.astral.sh/uv/) is recommended; the `--upgrade` switch ensures
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.

View File

@@ -73,7 +73,7 @@ if(HAS_OG_FLAG)
set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS})
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),
# 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)
if(LUAJIT_FOUND)
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)
set(LUA_ENGINE Lua)
string(APPEND DEPS_INCLUDE_FLAGS " -I${LUA_INCLUDE_DIR}")
string(APPEND DEPS_INCLUDE_FLAGS " -I\"${LUA_INCLUDE_DIR}\"")
else()
message(FATAL_ERROR "Could not find system lua or luajit")
endif()

View File

@@ -7,5 +7,6 @@ ExternalProject_Add(wasmtime
-D WASMTIME_FASTEST_RUNTIME=ON # build with full LTO
-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_GC_DRC=ON # ...and this one (needed by ts to create engines)
USES_TERMINAL_BUILD TRUE
${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_SHA256 6f4f1b639daa6dca9f80bc5db1233e9cbaa31a67790887106160b33ef743f136
TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.23.4.tar.gz
TREESITTER_C_SHA256 b66c5043e26d84e5f17a059af71b157bcf202221069ed220aa1696d7d1d28a7a
TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.3.0.tar.gz
TREESITTER_LUA_SHA256 a34cc70abfd8d2d4b0fabf01403ea05f848e1a4bc37d8a4bfea7164657b35d31
TREESITTER_VIM_URL https://github.com/tree-sitter-grammars/tree-sitter-vim/archive/v0.5.0.tar.gz
TREESITTER_VIM_SHA256 90019d12d2da0751c027124f27f5335babf069a050457adaed53693b5e9cf10a
TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v3.0.1.tar.gz
TREESITTER_VIMDOC_SHA256 76b65e5bee9ff78eb21256619b1995aac4d80f252c19e1c710a4839481ded09e
TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.5.1.tar.gz
TREESITTER_QUERY_SHA256 fe8c712880a529d454347cd4c58336ac2db22243bae5055bdb5844fb3ea56192
TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.4.1.tar.gz
TREESITTER_MARKDOWN_SHA256 e0fdb2dca1eb3063940122e1475c9c2b069062a638c95939e374c5427eddee9f
TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.3.tar.gz
TREESITTER_SHA256 862fac52653bc7bc9d2cd0630483e6bdf3d02bcd23da956ca32663c4798a93e3
TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.24.1.tar.gz
TREESITTER_C_SHA256 25dd4bb3dec770769a407e0fc803f424ce02c494a56ce95fedc525316dcf9b48
TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.4.0.tar.gz
TREESITTER_LUA_SHA256 b0977aced4a63bb75f26725787e047b8f5f4a092712c840ea7070765d4049559
TREESITTER_VIM_URL https://github.com/tree-sitter-grammars/tree-sitter-vim/archive/v0.7.0.tar.gz
TREESITTER_VIM_SHA256 44eabc31127c4feacda19f2a05a5788272128ff561ce01093a8b7a53aadcc7b2
TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v4.0.0.tar.gz
TREESITTER_VIMDOC_SHA256 8096794c0f090b2d74b7bff94548ac1be3285b929ec74f839bd9b3ff4f4c6a0b
TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.6.2.tar.gz
TREESITTER_QUERY_SHA256 90682e128d048fbf2a2a17edca947db71e326fa0b3dba4136e041e096538b4eb
TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.5.0.tar.gz
TREESITTER_MARKDOWN_SHA256 14c2c948ccf0e9b606eec39b09286c59dddf28307849f71b7ce2b1d1ef06937e
TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.6.tar.gz
TREESITTER_SHA256 ac6ed919c6d849e8553e246d5cd3fa22661f6c7b6497299264af433f3629957c
WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v29.0.1.tar.gz
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)
endif()
@@ -27,9 +27,13 @@ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
if(WIN32)
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(CPACK_PACKAGE_FILE_NAME "nvim-win-arm64")
else()
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
endif()
set(CPACK_GENERATOR ZIP WIX)
# WIX
# CPACK_WIX_UPGRADE_GUID should be set, but should never change.
# CPACK_WIX_PRODUCT_GUID should not be set (leave as default to auto-generate).

View File

@@ -268,13 +268,11 @@ function! provider#clipboard#Executable() abort
endfunction
function! s:clipboard.get(reg) abort
if type(s:paste[a:reg]) == v:t_func
return s:paste[a:reg]()
elseif s:selections[a:reg].owner > 0
if s:selections[a:reg].owner > 0
return s:selections[a:reg].data
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
\ && type(clipboard_data) == v:t_list
\ && get(s:selections[a:reg].data, 0, []) ==# clipboard_data
@@ -294,13 +292,12 @@ function! s:clipboard.set(lines, regtype, reg) abort
return 0
end
if type(s:copy[a:reg]) == v:t_func
call s:copy[a:reg](a:lines, a:regtype)
return 0
end
if s:cache_enabled == 0
call s:try_cmd(s:copy[a:reg], a:lines)
if s:cache_enabled == 0 || type(s:copy[a:reg]) == v:t_func
if type(s:copy[a:reg]) == v:t_func
call s:copy[a:reg](a:lines, a:regtype)
else
call s:try_cmd(s:copy[a:reg], a:lines)
endif
"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].data = [a:lines, a:regtype]

View File

@@ -77,46 +77,6 @@ function! tutor#TutorFolds()
endif
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
function! s:Locale()
@@ -167,15 +127,21 @@ function! s:Sort(a, b)
return retval
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:
" 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
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'
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
call extend(l:tutors, l:locale_tutors)
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')
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
echom "No tutorial with that name found"
@@ -225,7 +191,7 @@ function! tutor#TutorCmd(tutor_name)
endfunction
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'))
return join(l:names, "\n")
endfunction
@@ -237,9 +203,9 @@ function! tutor#EnableInteractive(enable)
setlocal buftype=nofile
setlocal concealcursor+=inv
setlocal conceallevel=2
call tutor#ApplyMarks()
lua require('nvim.tutor').apply_marks()
augroup tutor_interactive
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
autocmd! TextChanged,TextChangedI <buffer> lua require('nvim.tutor').apply_marks_on_changed()
augroup END
else
setlocal buftype<

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
like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua
vim.api.nvim_create_user_command('TermHl', function()
local b = vim.api.nvim_create_buf(false, true)
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)
vim.api.nvim_open_term(0, {})
end, { desc = 'Highlights ANSI termcodes in curbuf' })
<
@@ -3539,7 +3536,8 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
the duration of the call.
• fixed: If true when anchor is NW or SW, the float window
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|.
• split: Split direction: "left", "right", "above", "below".

View File

@@ -441,6 +441,7 @@ Use existing common {verb} names (actions) if possible:
- get: Gets things. Two variants (overloads):
1. `get<T>(id: int): T` returns one item.
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
- is_enabled: Checks if functionality is enabled.
- 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
Do NOT use these deprecated verbs:
- contains: Prefer "has".
- disable: Prefer `enable(enable: boolean)`.
- exit: Prefer "cancel" (or "stop" if appropriate).
- is_disabled: Prefer `is_enabled()`.

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
==============================================================================
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*

View File

@@ -187,20 +187,19 @@ Run |:checkhealth| in Nvim for automatic diagnosis.
Other hints:
- The python `neovim` module was renamed to `pynvim` (long ago).
- 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|.
- Read |provider-python| to learn how to install `pynvim`.
- Be sure you have the latest version of the `pynvim` Python module: >bash
python -m pip install setuptools
python -m pip install --upgrade pynvim
python3 -m pip install --upgrade pynvim
uv tool 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
problem. If you get `E117: Unknown function`, that means there's a runtime
issue: |faq-runtime|.
- The python `neovim` module was renamed to `pynvim` (long ago).
:CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME ~

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
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. >
function Example_Func()
echo "Example"
endfunction
function Example_Func()
echo "Example"
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:
- a special key name expressed either in <> notation as in <PageDown>, or
as a Ctrl character as in CTRL-X

View File

@@ -28,39 +28,42 @@ Follow these steps to get LSP features:
upstream installation instructions. You can find language servers here:
https://microsoft.github.io/language-server-protocol/implementors/servers/
2. Use |vim.lsp.config()| to define a configuration for an LSP client.
Example: >lua
vim.lsp.config['luals'] = {
-- Command and arguments to start the server.
cmd = { 'lua-language-server' },
2. Use |vim.lsp.config()| to define a configuration for an LSP client
(see https://github.com/neovim/nvim-lspconfig for examples).
Example: >lua
vim.lsp.config['luals'] = {
-- Command and arguments to start the server.
cmd = { 'lua-language-server' },
-- Filetypes to automatically attach to.
filetypes = { 'lua' },
-- Filetypes to automatically attach to.
filetypes = { 'lua' },
-- Sets the "root directory" to the parent directory of the file in the
-- current buffer that contains either a ".luarc.json" or a
-- ".luarc.jsonc" file. Files that share a root directory will reuse
-- the connection to the same LSP server.
-- Nested lists indicate equal priority, see |vim.lsp.Config|.
root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
-- Sets the "root directory" to the parent directory of the file in the
-- current buffer that contains either a ".luarc.json" or a
-- ".luarc.jsonc" file. Files that share a root directory will reuse
-- the connection to the same LSP server.
-- 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.
Example: >lua
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
:checkhealth vim.lsp
@@ -77,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
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
completion.
- 'tagfunc' is set to |vim.lsp.tagfunc()|. This enables features like
@@ -88,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
a custom keymap for `K` exists.
*grr* *gra* *grn* *gri* *i_CTRL-S*
Some 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()|
- "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()|
DISABLING DEFAULTS *lsp-defaults-disable*
You can remove GLOBAL keymaps at any time using |vim.keymap.del()| or
|:unmap|. See also |gr-default|.
If not wanted, these keymaps can be removed at any time using
|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
To remove or override BUFFER-LOCAL defaults, define a |LspAttach| handler: >lua
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
@@ -130,7 +135,7 @@ following (in increasing priority):
1. Configuration defined for the `'*'` name.
2. Configuration from the result of merging all tables returned by
`lsp/<name>.lua` files in 'runtimepath' for a server of name `name`.
`lsp/<config>.lua` files in 'runtimepath' for the config named `<config>`.
3. Configurations defined anywhere else.
Note: The merge semantics of configurations follow the behaviour of
@@ -251,8 +256,14 @@ FAQ *lsp-faq*
" (async = false is the default for format)
autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false })
<
- Q: How to avoid my own lsp/ folder being overridden?
- A: Place your configs under "after/lsp/". Files in "after/lsp/" are loaded
after those in "nvim/lsp/", so your settings will take precedence over
the defaults provided by nvim-lspconfig. See also: |after-directory|
*lsp-vs-treesitter*
- Q: How do LSP and Treesitter compare?
- Q: How do LSP, Treesitter and Ctags compare?
- A: LSP requires a client and language server. The language server uses
semantic analysis to understand code at a project level. This provides
language servers with the ability to rename across files, find
@@ -264,6 +275,11 @@ FAQ *lsp-faq*
like syntax highlighting, simple goto-definitions, scope analysis and
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*
@@ -705,8 +721,10 @@ Lua module: vim.lsp *lsp-core*
Fields: ~
• {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
See `cmd` in |vim.lsp.ClientConfig|.
• {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient`)
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
activated by `vim.lsp.enable()`. If not provided, the
client will attach to all filetypes.
@@ -716,46 +734,40 @@ Lua module: vim.lsp *lsp-core*
implementation re-uses a client if name and root_dir
matches.
• {root_dir}? (`string|fun(bufnr: integer, on_dir:fun(root_dir?:string))`)
*lsp-root_dir()* Directory where the LSP server will
base its workspaceFolders, rootUri, and rootPath on
initialization. The function form receives a buffer
number and `on_dir` callback which it must call to
provide 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[])[]`) Directory markers (.e.g.
'.git/') where the LSP server will base its
*lsp-root_dir()* Decides the workspace root: the
directory where the LSP server will base its
workspaceFolders, rootUri, and rootPath on
initialization. Unused if `root_dir` is provided.
The list order decides the priority. To indicate
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', 'b' }, ... }`) Each entry in this list is
a set of one or more markers. For each set, Nvim will
search upwards for each marker contained in the set.
If a marker is found, the directory which contains
that marker is used as the root directory. If no
markers from the set are found, the process is
repeated with the next set in the list.
`{ { 'a.txt', 'b.lua' }, ... }`.
Example: >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' }
<
Find the first parent directory containing the file
`stylua.toml`. If not found, find the first parent
directory containing the file or directory `.git`.
Example: >lua
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' }
<
Find the first parent directory containing EITHER
`stylua.toml` or `.luarc.json`. If not found, find
the first parent directory containing the file or
directory `.git`.
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
Implements the `textDocument/did…` notifications required to track a
@@ -918,6 +930,12 @@ enable({name}, {enable}) *vim.lsp.enable()*
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
@@ -932,7 +950,8 @@ enable({name}, {enable}) *vim.lsp.enable()*
Parameters: ~
• {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()*
Close all {kind} of folds in the the window with {winid}.
@@ -1045,6 +1064,9 @@ get_log_path() *vim.lsp.get_log_path()*
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
@@ -1294,16 +1316,16 @@ Lua module: vim.lsp.client *lsp-client*
• Note: To send an empty dictionary use
|vim.empty_dict()|, else it will be encoded
as an array.
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
command string[] that launches the language
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.PublicClient`)
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()|,
`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()|
@@ -1322,11 +1344,12 @@ Lua module: vim.lsp.client *lsp-client*
command name, and the value is a function which
is called if any LSP action (code action, code
lenses, …) triggers the command.
• {detached}? (`boolean`, default: true) 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.
• {detached}? (`boolean`, default: `true`) 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.
• {flags}? (`table`) A table with flags for the client.
The current (experimental) flags are:
• {allow_incremental_sync}? (`boolean`,
@@ -1351,8 +1374,8 @@ Lua module: vim.lsp.client *lsp-client*
initialization request as
`initializationOptions`. See `initialize` in
the LSP spec.
• {name}? (`string`) (default: client-id) Name in logs
and user messages.
• {name}? (`string`, default: client-id) Name in logs and
user messages.
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position
encoding" in LSP spec. The encoding that the
LSP server expects, used for communication. Not
@@ -1402,7 +1425,7 @@ Lua module: vim.lsp.client *lsp-client*
this list. Can be `null` if the client supports
workspace folders but none are configured. See
`workspaceFolders` in LSP spec.
• {workspace_required}? (`boolean`) (default false) Server requires a
• {workspace_required}? (`boolean`, default: `false`) Server requires a
workspace (no "single file" support). Note:
Without a workspace, cross-file features
(navigation, hover) may or may not work

View File

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

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

@@ -1466,20 +1466,21 @@ Lua module: vim *lua-vim*
vim.cmd({command}) *vim.cmd()*
Executes Vimscript (|Ex-commands|).
Note that `vim.cmd` can be indexed with a command name to return a
callable function to the command.
Can be indexed with a command name to get a function, thus you can write
`vim.cmd.echo(…)` instead of `vim.cmd{cmd='echo',…}`.
Example: >lua
Examples: >lua
-- Single command:
vim.cmd('echo 42')
-- Multiline script:
vim.cmd([[
augroup My_group
augroup my.group
autocmd!
autocmd FileType c setlocal cindent
augroup END
]])
-- Ex command :echo "foo"
-- Note string literals need to be double quoted.
-- Ex command :echo "foo". Note: string literals must be double-quoted.
vim.cmd('echo "foo"')
vim.cmd { cmd = 'echo', args = { '"foo"' } }
vim.cmd.echo({ args = { '"foo"' } })
@@ -1487,22 +1488,20 @@ vim.cmd({command}) *vim.cmd()*
-- Ex command :write! myfile.txt
vim.cmd('write! myfile.txt')
vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true }
vim.cmd.write { args = { "myfile.txt" }, bang = true }
vim.cmd.write { "myfile.txt", bang = true }
vim.cmd { cmd = 'write', args = { 'myfile.txt' }, bang = true }
vim.cmd.write { args = { 'myfile.txt' }, bang = true }
vim.cmd.write { 'myfile.txt', bang = true }
-- Ex command :colorscheme blue
vim.cmd('colorscheme blue')
vim.cmd.colorscheme('blue')
-- Ex command :vertical resize +2
vim.cmd.resize({ '+2', mods = { vertical = true } })
<
Parameters: ~
• {command} (`string|table`) Command(s) to execute. If a string,
executes multiple lines of Vimscript at once. In this case,
it is an alias to |nvim_exec2()|, where `opts.output` is
set to false. Thus it works identical to |:source|. If a
table, executes a single command. In this case, it is an
alias to |nvim_cmd()| where `opts` is empty.
• {command} (`string|table`) Command(s) to execute.
• The string form supports multiline Vimscript (alias to
|nvim_exec2()|, behaves like |:source|).
• The table form executes a single command (alias to
|nvim_cmd()|).
See also: ~
• |ex-cmd-index|
@@ -1779,7 +1778,9 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
the new process. Inherits the current environment with
`NVIM` set to |v:servername|.
• 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
to stdin is opened and can be written to via the
`write()` method to SystemObj. If string or string[] then
@@ -2940,11 +2941,13 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
iterate over. The path is first normalized
|vim.fs.normalize()|.
• {opts} (`table?`) Optional keyword arguments:
• depth: integer|nil How deep the traverse (default 1)
• skip: (fun(dir_name: string): boolean)|nil Predicate to
{depth}? (`integer`, default: `1`) How deep the traverse.
{skip}? (`fun(dir_name: string): boolean`) 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)
current directory. Only useful when depth > 1 Return an
iterator over the items located in {path}
• {follow}? (`boolean`, default: `false`) Follow symbolic
links.
Return: ~
(`Iterator`) over items in {path}. Each iteration yields two values:
@@ -3006,7 +3009,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
The function should return `true` if the given item is
considered a match.
• {opts} (`table`) Optional keyword arguments:
• {opts} (`table?`) Optional keyword arguments:
• {path}? (`string`) Path to begin searching from. If
omitted, the |current-directory| is used.
• {upward}? (`boolean`, default: `false`) Search upward
@@ -3154,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
'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'
vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' })
@@ -3165,6 +3168,10 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
vim.fs.root(0, function(name, path)
return name:match('%.csproj$') ~= nil
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: ~
@@ -3174,10 +3181,13 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
• {source} (`integer|string`) Buffer number (0 for current buffer) or
file path (absolute or relative to the |current-directory|)
to begin the search from.
• {marker} (`string|string[]|fun(name: string, path: string): boolean`)
A marker, or list of markers, to search for. If a function,
the function is called for each evaluated item and should
return true if {name} and {path} are a match.
• {marker} (`(string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean`)
Filename, function, or list thereof, that decides how to
find the root. To 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

View File

@@ -188,6 +188,7 @@ DEFAULTS
• |gri| in Normal mode maps to |vim.lsp.buf.implementation()|
• |gO| in Normal mode maps to |vim.lsp.buf.document_symbol()|
• |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()|
• Mouse |popup-menu| includes an "Open in web browser" item when you right-click
on a URL.
@@ -283,6 +284,8 @@ LSP
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
@@ -305,6 +308,7 @@ LUA
• |vim.hl.range()| now has a optional `timeout` field which allows for multiple
timed highlights.
• |vim.text.indent()| indents/dedents text.
• |vim.fs.root()| can define "equal priority" via nested lists.
OPTIONS
@@ -352,6 +356,11 @@ PLUGINS
• 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
• |-es| ("script mode") disables shada by default.

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
the complete reference.
If you already use Vim, see |nvim-from-vim| for a quickstart. If you just
installed Nvim and have never used it before, watch this 10-minute
video: https://youtu.be/TQn2hJeHQbM .
- If you already use Vim, see |nvim-from-vim| for a quickstart.
- If you have never used Vim or Nvim before, see below.
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
: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*
@@ -72,31 +100,5 @@ the same Nvim configuration on all of your machines, by creating
~/AppData/Local/nvim/init.vim containing just this line: >vim
source ~/.config/nvim/init.vim
==============================================================================
What next? *nvim-quickstart*
If you want to use Lua to configure Nvim, you can copy an example
configuration to your |init.lua|
>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 Language Server Protocol. See |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):
- *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:

View File

@@ -2357,6 +2357,8 @@ 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
|vim.secure.read()|.
To get its own location, Lua exrc files can use |debug.getinfo()|.
Compare 'exrc' to |editorconfig|:
- 'exrc' can execute any code; editorconfig only specifies settings.
- 'exrc' is Nvim-specific; editorconfig works in other editors.
@@ -5010,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.
When "exclusive" is used, cursor position in visual mode will be
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
starting in Normal mode and 'virtualedit' empty.
Note:
- 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' string (default "")

View File

@@ -36,21 +36,18 @@ itself).
For Python 3 plugins:
1. Make sure Python 3.9+ is available in your $PATH.
2. Install the module (try "python" if "python3" is missing): >bash
python3 -m pip install --user --upgrade pynvim
2. Install either uv (https://docs.astral.sh/uv/) or pipx
(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.
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 ~
*g:python3_host_prog*
Command to start Python 3 (executable, not directory). Setting this makes
@@ -65,20 +62,18 @@ To disable Python 3 support: >vim
PYTHON VIRTUALENVS ~
*python-virtualenv*
If you plan to use per-project virtualenvs often, you should assign one
virtualenv for Nvim and hard-code the interpreter path via
|g:python3_host_prog| so that the "pynvim" package is not required
for each virtualenv.
Example using pyenv: >bash
pyenv install 3.4.4
pyenv virtualenv 3.4.4 py3nvim
pyenv activate py3nvim
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'
Using pynvim 0.6.0+ installed via uv or pipx, Nvim will automatically detect
pynvim even if other Python virtual environments are activated (technical
note: via the "pynvim-python" global python tool). For older pynvim (or older
Neovim), where detection involved finding the first Python interpreter and
checking if it could import pynvim, automatic detection would fail when
another virtual environment is active. Upgrading to the latest pynvim is the
recommended solution to this; but if that's not an option, then you can set
the variable |g:python3_host_prog| in `init.vim` to point to the full path to
the Python interpreter where `pynvim` is installed, e.g.: >vim
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
==============================================================================

View File

@@ -573,7 +573,10 @@ parent tree. The language injection query allows you to specify these
“injections” using the following captures:
• `@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
contain the name of a language that should be used to re-parse the
`@injection.content`.

View File

@@ -25,9 +25,9 @@ Table of contents: |usr_toc.txt|
==============================================================================
*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
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
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
@@ -580,7 +566,7 @@ Summary: *help-summary* >
:help quote:
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
single letter. E.g. >
:help expr-!
@@ -660,10 +646,13 @@ Summary: *help-summary* >
command switch of Vim use: >
:help -f
24) Optional features always start with "+". To find out about the
conceal feature use: >
:help +conceal
24) Lua language and Nvim's Lua standard library are available at >vim
:help lua.txt
< 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
available in the form ft-<filetype>-<functionality>. So >
:help ft-c-syntax

View File

@@ -134,7 +134,8 @@ To remove only the "How-to disable mouse" menu item (and its separator): >vim
DEFAULT MAPPINGS
*default-mappings*
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|
- <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|
- |gra|
- |gri|
- |grt|
- |gO|
- <C-S> |i_CTRL-S|
- ]d |]d-default|
@@ -855,9 +857,8 @@ Startup:
Test functions:
- test_alloc_fail()
- test_autochdir()
- test_disable_char_avail()
- test_feedinput()
- test_garbagecollect_soon
- test_garbagecollect_soon()
- test_getvalue()
- test_ignore_error()
- test_null_blob()
@@ -875,6 +876,8 @@ Test functions:
- test_setmouse()
- test_settime()
- test_srand_seed()
- test_unknown()
- test_void()
TUI:
*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
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|.

View File

@@ -1,10 +1,10 @@
*builtin.txt* Nvim
*vimfn.txt* Nvim
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|.
@@ -2040,7 +2040,8 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
<SID> "<SNR>123_" where "123" is the
current script ID |<SID>|
<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
<cword> word under the cursor
<cWORD> WORD under the cursor
@@ -4093,6 +4094,10 @@ getregion({pos1}, {pos2} [, {opts}]) *getregion()*
- It is evaluated in current window context, which makes a
difference if the buffer is displayed in a window with
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
xnoremap <CR>

View File

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

View File

@@ -1,5 +1,5 @@
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' })
vim.keymap.set('n', ']]', function()

View File

@@ -1,7 +1,8 @@
" Vim filetype plugin file
" Language: gpg(1) configuration file
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -17,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=:GpgKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer GpgKeywordPrg'

View File

@@ -5,7 +5,9 @@
" Contributor: Dorai Sitaram <ds26@gte.com>
" C.D. MacEachern <craig.daniel.maceachern@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")
finish
@@ -28,6 +30,7 @@ set cpo&vim
setlocal comments=:---,:--
setlocal commentstring=--\ %s
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\)\='
@@ -35,7 +38,7 @@ let &l:include = '\<\%(\%(do\|load\)file\|require\)\s*('
setlocal includeexpr=s:LuaInclude(v:fname)
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")
let b:match_ignorecase = 0

View File

@@ -2,7 +2,7 @@
" Language: modules.conf(5) configuration file
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -19,7 +19,7 @@ setlocal formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=:ModconfKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ModconfKeywordPrg'

View File

@@ -1,7 +1,8 @@
" Vim filetype plugin file
" Language: mutt RC File
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -20,7 +21,7 @@ let &l:include = '^\s*source\>'
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=: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 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
" 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
if exists("b:did_ftplugin") | finish | endif
@@ -51,7 +52,7 @@ endif
if exists('s:pwsh_cmd')
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
command! -buffer -nargs=1 GetHelp echo system(s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full <args>')
endif

View File

@@ -3,6 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Last Change: 2024 Sep 19 (simplify keywordprg #15696)
" 2024 Jul 22 by Vim project (use :hor term #17822)
if exists("b:did_ftplugin")
finish
@@ -36,7 +37,7 @@ endif
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=: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)
" 2024 Dec 29 by Vim Project (improve setting shellcheck compiler)
" 2025 Mar 09 by Vim Project (set b:match_skip)
" 2025 Jul 22 by phanium (use :hor term #17822)
if exists("b:did_ftplugin")
finish
@@ -53,7 +54,7 @@ let s:is_kornshell = get(b:, "is_kornshell", get(g:, "is_kornshell", 0))
if s:is_bash
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
command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || MANPAGER= man "<args>"')
endif

View File

@@ -2,7 +2,7 @@
" Language: OpenSSH client configuration file
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -17,7 +17,7 @@ let b:undo_ftplugin = 'setlocal com< cms< fo<'
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=:SshconfigKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SshconfigKeywordPrg'

View File

@@ -2,7 +2,7 @@
" Language: sudoers(5) configuration files
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=:SudoersKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SudoersKeywordPrg'

View File

@@ -2,7 +2,7 @@
" Language: udev(8) rules file
" Maintainer: This runtime file is looking for a new maintainer.
" 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")
finish
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
if has('unix') && executable('less') && exists(':terminal') == 2
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 keywordprg=:UdevrulesKeywordPrg
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer UdevrulesKeywordPrg'

View File

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

View File

@@ -3,6 +3,7 @@
" Maintainer: See https://github.com/HerringtonDarkholme/yats.vim
" Last Change: 2019 Oct 18
" 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
" 0. Initialization {{{1
@@ -18,10 +19,9 @@ setlocal nosmartindent
" Now, set up our indentation expression and keys that trigger it.
setlocal indentexpr=GetTypescriptIndent()
setlocal formatexpr=Fixedgq(v:lnum,v:count)
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.
if exists("*GetTypescriptIndent")
@@ -443,64 +443,3 @@ endfunction
let &cpo = 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
--- @type string?
local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$')
local glob = line:match('^%s*%[(.*)%]%s*$')
if glob then
return glob
end

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
--- 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
vim.keymap.set('n', 'grn', function()
vim.lsp.buf.rename()
@@ -209,6 +209,10 @@ do
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.lsp.buf.document_symbol()
end, { desc = 'vim.lsp.buf.document_symbol()' })

View File

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

View File

@@ -200,7 +200,9 @@ function vim.show_pos(bufnr, row, col, filter)
capture,
string.format(
'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
)
)

View File

@@ -1662,10 +1662,7 @@ function vim.api.nvim_notify(msg, log_level, opts) end
---
--- ```lua
--- vim.api.nvim_create_user_command('TermHl', function()
--- local b = vim.api.nvim_create_buf(false, true)
--- 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)
--- vim.api.nvim_open_term(0, {})
--- end, { desc = 'Highlights ANSI termcodes in curbuf' })
--- ```
---
@@ -1843,7 +1840,8 @@ function vim.api.nvim_open_term(buffer, opts) end
--- the call.
--- - fixed: If true when anchor is NW or SW, the float window
--- 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`.
--- - split: Split direction: "left", "right", "above", "below".
--- @return integer # |window-ID|, or 0 on error

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
--- "nofile", but it will be written. Thus, in contrast to "nofile" and
--- "nowrite", ":w" does work and a modified buffer can't be abandoned
--- without saving. For writing there must be matching `BufWriteCmd|,
--- |FileWriteCmd` or `FileAppendCmd` autocommands.
--- without saving. For writing there must be matching `BufWriteCmd`,
--- `FileWriteCmd` or `FileAppendCmd` autocommands.
---
--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt'
vim.o.buftype = ""
@@ -2015,6 +2015,8 @@ vim.bo.et = vim.bo.expandtab
--- in the `trust` list. Use `:trust` to manage trusted files. See also
--- `vim.secure.read()`.
---
--- To get its own location, Lua exrc files can use `debug.getinfo()`.
---
--- Compare 'exrc' to `editorconfig`:
--- - 'exrc' can execute any code; editorconfig only specifies settings.
--- - 'exrc' is Nvim-specific; editorconfig works in other editors.
@@ -3160,8 +3162,8 @@ vim.o.ims = vim.o.imsearch
vim.bo.imsearch = vim.o.imsearch
vim.bo.ims = vim.bo.imsearch
--- When nonempty, shows the effects of `:substitute`, `:smagic|,
--- |:snomagic` and user commands with the `:command-preview` flag as you
--- When nonempty, shows the effects of `:substitute`, `:smagic`,
--- `:snomagic` and user commands with the `:command-preview` flag as you
--- type.
---
--- Possible values:
@@ -3500,8 +3502,8 @@ vim.go.js = vim.go.joinspaces
--- when navigating backwards in the jumplist and then
--- jumping to a location. `jumplist-stack`
---
--- view When moving through the jumplist, `changelist|,
--- |alternate-file` or using `mark-motions` try to
--- view When moving through the jumplist, `changelist`,
--- `alternate-file` or using `mark-motions` try to
--- restore the `mark-view` in which the action occurred.
---
--- clean Remove unloaded buffers from the jumplist.
@@ -5248,9 +5250,14 @@ vim.go.sect = vim.go.sections
--- the end of line the line break still isn't included.
--- When "exclusive" is used, cursor position in visual mode will be
--- 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
--- starting in Normal mode and 'virtualedit' empty.
---
--- Note:
--- - 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'
vim.o.selection = "inclusive"
@@ -5636,8 +5643,8 @@ vim.go.ssl = vim.go.shellslash
--- and the 'shell' command does not need to support redirection.
--- The advantage of using a temp file is that the file type and encoding
--- can be detected.
--- The `FilterReadPre`, `FilterReadPost` and `FilterWritePre|,
--- |FilterWritePost` autocommands event are not triggered when
--- The `FilterReadPre`, `FilterReadPost` and `FilterWritePre`,
--- `FilterWritePost` autocommands event are not triggered when
--- 'shelltemp' is off.
--- `system()` does not respect this option, it always uses pipes.
---
@@ -6207,8 +6214,8 @@ vim.bo.spo = vim.bo.spelloptions
---
--- expr:{expr} Evaluate expression {expr}. Use a function to avoid
--- trouble with spaces. Best is to call a function
--- without arguments, see `expr-option-function|.
--- |v:val` holds the badly spelled word. The expression
--- without arguments, see `expr-option-function`.
--- `v:val` holds the badly spelled word. The expression
--- must evaluate to a List of Lists, each with a
--- suggestion and a score.
--- Example:

View File

@@ -1801,7 +1801,8 @@ function vim.fn.exp(expr) end
--- <SID> "<SNR>123_" where "123" is the
--- current script ID |<SID>|
--- <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
--- <cword> word under the cursor
--- <cWORD> WORD under the cursor
@@ -3692,6 +3693,10 @@ function vim.fn.getreginfo(regname) end
--- - It is evaluated in current window context, which makes a
--- difference if the buffer is displayed in a window with
--- 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
--- xnoremap <CR>

View File

@@ -210,12 +210,15 @@ end
--- @param clear_env? boolean
--- @return string[]?
local function setup_env(env, clear_env)
if clear_env then
return env
if not env and clear_env then
return
end
--- @type table<string,string|number>
env = vim.tbl_extend('force', base_env(), env or {})
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[]
for k, v in pairs(env) do

View File

@@ -1442,6 +1442,7 @@ M.handlers.signs = {
vim.validate('bufnr', bufnr, 'number')
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true)
vim.validate('opts.signs', (opts and opts or {}).signs, 'table', true)
bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
@@ -2619,7 +2620,9 @@ function M.toqflist(diagnostics)
end_lnum = v.end_lnum and (v.end_lnum + 1) or nil,
end_col = v.end_col and (v.end_col + 1) or nil,
text = v.message,
nr = tonumber(v.code),
type = errlist_type_map[v.severity] or 'E',
valid = 1,
}
table.insert(list, item)
end
@@ -2651,7 +2654,8 @@ function M.fromqflist(list)
local col = math.max(0, item.col - 1)
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 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] = {
bufnr = item.bufnr,
lnum = lnum,
@@ -2660,6 +2664,7 @@ function M.fromqflist(list)
end_col = end_col,
severity = severity,
message = item.text,
code = code,
}
end
end

View File

@@ -124,6 +124,23 @@ function M.joinpath(...)
return (path:gsub('//+', '/'))
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?
--- Return an iterator over the items located in {path}
@@ -131,13 +148,7 @@ end
---@since 10
---@param path (string) An absolute or relative path to the directory to iterate
--- over. The path is first normalized |vim.fs.normalize()|.
--- @param opts table|nil 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)
---
---@param opts? vim.fs.dir.Opts Optional keyword arguments:
---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type".
--- "name" is the basename of the item relative to {path}.
--- "type" is one of the following:
@@ -256,7 +267,7 @@ end
---
--- 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
function M.find(names, opts)
opts = opts or {}
@@ -375,7 +386,7 @@ end
--- If the buffer is unnamed (has no backing file) or has a non-empty 'buftype' then the search
--- begins from Nvim's |current-directory|.
---
--- Example:
--- Examples:
---
--- ```lua
--- -- Find the root of a Python project, starting from file 'main.py'
@@ -388,14 +399,21 @@ end
--- vim.fs.root(0, function(name, path)
--- return name:match('%.csproj$') ~= nil
--- 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
--- @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.
--- @param marker (string|string[]|fun(name: string, path: string): boolean) A marker, or list
--- of markers, to search for. If a function, the function is called for each
--- evaluated item and should return true if {name} and {path} are a match.
--- @param marker (string|string[]|fun(name: string, path: string): boolean)[]|string|fun(name: string, path: string): boolean
--- Filename, function, or list thereof, that decides how to find the root. To
--- 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
--- found.
function M.root(source, marker)
@@ -415,16 +433,19 @@ function M.root(source, marker)
error('invalid type for argument "source": expected string or buffer number')
end
local paths = M.find(marker, {
upward = true,
path = vim.fn.fnamemodify(path, ':p:h'),
})
local markers = type(marker) == 'table' and marker or { marker }
for _, mark in ipairs(markers) do
local paths = M.find(mark, {
upward = true,
path = vim.fn.fnamemodify(path, ':p:h'),
})
if #paths == 0 then
return nil
if #paths ~= 0 then
return vim.fs.dirname(paths[1])
end
end
return vim.fs.dirname(paths[1])
return nil
end
--- Split a Windows path into a prefix and a body, such that the body can be processed like a POSIX

View File

@@ -389,15 +389,17 @@ function M._check(mods, plugin_names)
and type(vim.g.health) == 'table'
and vim.tbl_get(vim.g.health, 'style') == 'float'
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 float_winid
bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
height = max_height,
width = max_width,
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',
close_events = {},
})
vim.api.nvim_set_current_win(float_winid)
vim.bo[bufnr].modifiable = true

View File

@@ -87,9 +87,9 @@ local function check_config()
health.error(
'Locale does not support UTF-8. Unicode characters may not display correctly.'
.. ('\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s'):format(
vim.env.LANG,
vim.env.LC_ALL,
vim.env.LC_CTYPE
vim.env.LANG or '',
vim.env.LC_ALL or '',
vim.env.LC_CTYPE or ''
),
{
'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 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(
'$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"',
suggest_faq,

View File

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

View File

@@ -275,7 +275,8 @@ end
--- @class vim.lsp.Config : 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()`. If not provided, the
--- client will attach to all filetypes.
@@ -285,43 +286,35 @@ end
--- implementation re-uses a client if name and root_dir matches.
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
---
--- [lsp-root_dir()]() Directory where the LSP server will base its workspaceFolders, rootUri, and
--- rootPath on initialization. The function form receives a buffer number and `on_dir` callback
--- which it must call to provide 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()|.
--- [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))
---
--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders,
--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
--- [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' }, ... }`.
---
--- The list order decides the priority. To indicate "equal priority", specify names in a nested list (`{ { 'a', 'b' }, ... }`)
--- Each entry in this list is a set of one or more markers. For each set, Nvim
--- will search upwards for each marker contained in the set. If a marker is
--- found, the directory which contains that marker is used as the root
--- directory. If no markers from the set are found, the process is repeated
--- with the next set in the list.
---
--- Example:
--- 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' }
--- ```
---
--- Find the first parent directory containing the file `stylua.toml`. If not
--- found, find the first parent directory containing the file or directory
--- `.git`.
---
--- Example:
---
--- 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' }
--- ```
---
--- Find the first parent directory containing EITHER `stylua.toml` or
--- `.luarc.json`. If not found, find the first parent directory containing the
--- file or directory `.git`.
---
--- @field root_markers? (string|string[])[]
--- Update the configuration for an LSP client.
@@ -531,7 +524,9 @@ local function lsp_enable_callback(bufnr)
-- 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.config[client.name] and not can_start(bufnr, client.name, lsp.config[client.name]) then
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
@@ -569,6 +564,14 @@ end
--- 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:
---
@@ -583,7 +586,8 @@ end
--- ```
---
--- @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)
validate('name', name, { 'string', 'table' })
@@ -628,6 +632,8 @@ 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)
@@ -703,13 +709,7 @@ function lsp.start(config, opts)
validate('root_markers', opts._root_markers, 'table')
config = vim.deepcopy(config)
for _, marker in ipairs(opts._root_markers) do
local root = vim.fs.root(bufnr, marker)
if root ~= nil then
config.root_dir = root
break
end
end
config.root_dir = vim.fs.root(bufnr, opts._root_markers)
end
if
@@ -1289,7 +1289,9 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do
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

View File

@@ -3,6 +3,13 @@ local log = require('vim.lsp.log')
local ms = require('vim.lsp.protocol').Methods
local api = vim.api
---@type table<lsp.FoldingRangeKind, true>
local supported_fold_kinds = {
['comment'] = true,
['imports'] = true,
['region'] = true,
}
local M = {}
---@class (private) vim.lsp.folding_range.BufState
@@ -39,7 +46,7 @@ local function renew(bufnr)
---@type table<integer, string?>
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
local start_row = range.startLine
local end_row = range.endLine
@@ -49,9 +56,14 @@ local function renew(bufnr)
local kind = range.kind
if kind then
local kinds = row_kinds[start_row] or {}
kinds[kind] = true
row_kinds[start_row] = kinds
-- Ignore unsupported fold kinds.
if supported_fold_kinds[kind] then
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
for row = start_row, end_row do

View File

@@ -71,7 +71,7 @@ function M.hover(config)
if vim.tbl_isempty(results1) then
if config.silent ~= true then
vim.notify('No information available')
vim.notify('No information available', vim.log.levels.INFO)
end
return
end
@@ -352,6 +352,7 @@ function M.signature_help(config)
config = config and vim.deepcopy(config) or {}
config.focus_id = method
local user_title = config.title
lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx)
if api.nvim_get_current_buf() ~= ctx.bufnr then
@@ -363,7 +364,7 @@ function M.signature_help(config)
if not next(signatures) then
if config.silent ~= true then
print('No signature help available')
vim.notify('No signature help available', vim.log.levels.INFO)
end
return
end
@@ -386,17 +387,19 @@ function M.signature_help(config)
return
end
local sfx = total > 1
and string.format(' (%d/%d)%s', idx, total, can_cycle and ' (<C-s> to cycle)' or '')
or ''
local title = string.format('Signature Help: %s%s', client.name, sfx)
if config.border then
config.title = title
else
table.insert(lines, 1, '# ' .. title)
if hl then
hl[1] = hl[1] + 1
hl[3] = hl[3] + 1
-- Show title only if there are multiple clients or multiple signatures.
if total > 1 then
local sfx = total > 1
and string.format(' (%d/%d)%s', idx, total, can_cycle and ' (<C-s> to cycle)' or '')
or ''
config.title = user_title or string.format('Signature Help: %s%s', client.name, sfx)
-- If no border is set, render title inside the window.
if not (config.border or vim.o.winborder ~= '') then
table.insert(lines, 1, '# ' .. config.title)
if hl then
hl[1] = hl[1] + 1
hl[3] = hl[3] + 1
end
end
end
@@ -440,7 +443,7 @@ end
---
---@see vim.lsp.protocol.CompletionTriggerKind
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(
0,
ms.textDocument_completion,
@@ -970,7 +973,7 @@ function M.add_workspace_folder(workspace_folder)
return
end
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
end
local bufnr = api.nvim_get_current_buf()
@@ -994,7 +997,7 @@ function M.remove_workspace_folder(workspace_folder)
for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
client:_remove_workspace_folder(workspace_folder)
end
print(workspace_folder, 'is not currently part of the workspace')
vim.notify(workspace_folder .. 'is not currently part of the workspace')
end
--- Lists all symbols in the current workspace in the quickfix window.

View File

@@ -43,14 +43,13 @@ local validate = vim.validate
--- array.
--- @field capabilities? lsp.ClientCapabilities
---
--- 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`.
--- 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()|
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
--- 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
---
--- Directory to launch the `cmd` process. Not related to `root_dir`.
--- (default: cwd)
@@ -73,7 +72,7 @@ local validate = vim.validate
--- 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)
--- (default: `true`)
--- @field detached? boolean
---
--- A table with flags for the client. The current (experimental) flags are:
@@ -89,7 +88,8 @@ local validate = vim.validate
--- the LSP spec.
--- @field init_options? lsp.LSPObject
---
--- (default: client-id) Name in logs and user messages.
--- Name in logs and user messages.
--- (default: client-id)
--- @field name? string
---
--- Called "position encoding" in LSP spec. The encoding that the LSP server expects, used for
@@ -132,9 +132,10 @@ local validate = vim.validate
--- supports workspace folders but none are configured. See `workspaceFolders` in LSP spec.
--- @field workspace_folders? lsp.WorkspaceFolder[]
---
--- (default false) Server requires a workspace (no "single file" support). Note: Without
--- Server requires a workspace (no "single file" support). Note: Without
--- 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}>
@@ -454,7 +455,7 @@ function Client.create(config)
-- Start the RPC client.
local config_cmd = config.cmd
if type(config_cmd) == 'function' then
self.rpc = config_cmd(dispatchers)
self.rpc = config_cmd(dispatchers, config)
else
self.rpc = lsp.rpc.start(config_cmd, dispatchers, {
cwd = config.cmd_cwd,

View File

@@ -198,7 +198,7 @@ local function check_enabled_configs()
local v_str --- @type string?
if k == 'name' then
v_str = nil
elseif k == 'filetypes' or k == 'root_markers' then
elseif k == 'filetypes' then
v_str = table.concat(v, ', ')
elseif type(v) == 'function' then
v_str = func_tostring(v)

View File

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

View File

@@ -745,6 +745,10 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
if type(doc) == 'string' then
signature.documentation = { kind = 'plaintext', value = doc }
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)
end
if signature.parameters and #signature.parameters > 0 then
@@ -861,7 +865,7 @@ function M.make_floating_popup_options(width, height, opts)
col = 1
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'?
if title then
@@ -1340,26 +1344,34 @@ end
---
---@param events table list of events
---@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
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, {
clear = true,
})
-- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it
api.nvim_create_autocmd('BufEnter', {
api.nvim_create_autocmd('BufLeave', {
group = augroup,
buffer = bufnr,
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,
})
if #events > 0 then
api.nvim_create_autocmd(events, {
group = augroup,
buffer = bufnrs[2],
buffer = bufnr,
callback = function()
close_preview_window(winnr)
end,
@@ -1606,7 +1618,7 @@ function M.open_floating_preview(contents, syntax, opts)
'<cmd>bdelete<cr>',
{ 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
if opts.focus_id then

View File

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

View File

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

View File

@@ -119,7 +119,7 @@ local Tabstop = {}
function Tabstop.new(index, bufnr, range, choices)
local extmark_id = vim.api.nvim_buf_set_extmark(bufnr, snippet_ns, range[1], range[2], {
right_gravity = true,
end_right_gravity = true,
end_right_gravity = false,
end_line = range[3],
end_col = range[4],
hl_group = hl_group,
@@ -170,7 +170,7 @@ function Tabstop:set_right_gravity(right_gravity)
local range = self:get_range()
self.extmark_id = vim.api.nvim_buf_set_extmark(self.bufnr, snippet_ns, range[1], range[2], {
right_gravity = right_gravity,
end_right_gravity = true,
end_right_gravity = not right_gravity,
end_line = range[3],
end_col = range[4],
hl_group = hl_group,
@@ -257,10 +257,21 @@ local M = { session = nil }
local function display_choices(tabstop)
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 matches = {} --- @type table[]
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
vim.defer_fn(function()
@@ -298,6 +309,7 @@ local function select_tabstop(tabstop)
vim.cmd.startinsert({ bang = range[4] >= #vim.api.nvim_get_current_line() })
end
if tabstop.choices then
vim.fn.cursor(range[3] + 1, range[4] + 1)
display_choices(tabstop)
end
else

View File

@@ -83,8 +83,9 @@ local get_headings = function(bufnr)
return headings
end
--- @param qf_height? integer height of loclist window
--- 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 bufname = api.nvim_buf_get_name(bufnr)
local headings = get_headings(bufnr)
@@ -102,7 +103,7 @@ function M.show_toc()
end
vim.fn.setloclist(0, headings, ' ')
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'

View File

@@ -26,6 +26,7 @@ function TSTree:root() end
---@param end_col_old integer
---@param end_row_new integer
---@param end_col_new integer
---@return TSTree
---@nodoc
function TSTree:edit(start_byte, end_byte_old, end_byte_new, start_row, start_col, end_row_old, end_col_old, end_row_new, end_col_new) end

View File

@@ -23,23 +23,24 @@ function M.check()
---@class ParserEntry
---@field name string
---@field path string
---@field index integer runtime path index (unique)
local sorted_parsers = {} ---@type ParserEntry[]
for _, parser in ipairs(parsers) do
for i, parser in ipairs(parsers) do
local parsername = vim.fn.fnamemodify(parser, ':t:r')
table.insert(sorted_parsers, { name = parsername, path = parser })
table.insert(sorted_parsers, { name = parsername, path = parser, index = i })
end
table.sort(sorted_parsers, function(a, b)
if a.name == b.name then
return a.path < b.path
return a.index < b.index -- if names are the same sort by rtpath index (unique)
else
return a.name < b.name
end
end)
for _, parser in ipairs(sorted_parsers) do
for i, parser in ipairs(sorted_parsers) do
local is_loadable, err_or_nil = pcall(ts.language.add, parser.name)
if not is_loadable then
@@ -51,10 +52,15 @@ function M.check()
err_or_nil or '?'
)
)
elseif i > 1 and sorted_parsers[i - 1].name == parser.name then
-- Sorted by runtime path order (load order), thus, if the previous parser has the same name,
-- the current parser will not be loaded and `ts.language.inspect(parser.name)` with have
-- incorrect information.
health.ok(string.format('Parser: %-20s (not loaded), path: %s', parser.name, parser.path))
else
local lang = ts.language.inspect(parser.name)
health.ok(
string.format('Parser: %-20s ABI: %d, path: %s', parser.name, lang.abi_version, parser.path)
string.format('Parser: %-25s ABI: %d, path: %s', parser.name, lang.abi_version, parser.path)
)
end
end

View File

@@ -52,26 +52,30 @@ function TSHighlighterQuery:query()
return self._query
end
---@alias MarkInfo { start_line: integer, start_col: integer, opts: vim.api.keyset.set_extmark }
---@class (private) vim.treesitter.highlighter.State
---@field tstree TSTree
---@field next_row integer
---@field iter vim.treesitter.highlighter.Iter?
---@field highlighter_query vim.treesitter.highlighter.Query
---@field prev_marks MarkInfo[]
---@nodoc
---@class vim.treesitter.highlighter
---@field active table<integer,vim.treesitter.highlighter>
---@field bufnr integer
---@field private orig_spelloptions string
--- A map of highlight states.
--- A map from window ID to highlight states.
--- This state is kept during rendering across each line update.
---@field private _highlight_states vim.treesitter.highlighter.State[]
---@field private _highlight_states table<integer, vim.treesitter.highlighter.State[]>
---@field private _queries table<string,vim.treesitter.highlighter.Query>
---@field _conceal_line boolean?
---@field _conceal_checked table<integer, boolean>
---@field tree vim.treesitter.LanguageTree
---@field private redraw_count integer
---@field parsing boolean true if we are parsing asynchronously
--- A map from window ID to whether we are currently parsing that window asynchronously
---@field parsing table<integer, boolean>
local TSHighlighter = {
active = {},
}
@@ -132,6 +136,7 @@ function TSHighlighter.new(tree, opts)
self._conceal_checked = {}
self._queries = {}
self._highlight_states = {}
self.parsing = {}
-- Queries for a specific language can be overridden by a custom
-- string query... if one is not provided it will be looked up by file.
@@ -177,19 +182,23 @@ function TSHighlighter:destroy()
vim.b[self.bufnr].ts_highlight = nil
api.nvim_buf_clear_namespace(self.bufnr, ns, 0, -1)
if vim.g.syntax_on == 1 then
api.nvim_exec_autocmds(
'FileType',
{ group = 'syntaxset', buffer = self.bufnr, modeline = false }
)
-- FileType autocmds commonly assume curbuf is the target buffer, so nvim_buf_call.
api.nvim_buf_call(self.bufnr, function()
api.nvim_exec_autocmds(
'FileType',
{ group = 'syntaxset', buffer = self.bufnr, modeline = false }
)
end)
end
end
end
---@param win integer
---@param srow integer
---@param erow integer exclusive
---@private
function TSHighlighter:prepare_highlight_states(srow, erow)
self._highlight_states = {}
function TSHighlighter:prepare_highlight_states(win, srow, erow)
self._highlight_states[win] = {}
self.tree:for_each_tree(function(tstree, tree)
if not tstree then
@@ -212,19 +221,21 @@ function TSHighlighter:prepare_highlight_states(srow, erow)
-- _highlight_states should be a list so that the highlights are added in the same order as
-- for_each_tree traversal. This ensures that parents' highlight don't override children's.
table.insert(self._highlight_states, {
table.insert(self._highlight_states[win], {
tstree = tstree,
next_row = 0,
iter = nil,
highlighter_query = hl_query,
prev_marks = {},
})
end)
end
---@param win integer
---@param fn fun(state: vim.treesitter.highlighter.State)
---@package
function TSHighlighter:for_each_highlight_state(fn)
for _, state in ipairs(self._highlight_states) do
function TSHighlighter:for_each_highlight_state(win, fn)
for _, state in ipairs(self._highlight_states[win] or {}) do
fn(state)
end
end
@@ -307,14 +318,44 @@ local function get_spell(capture_name)
return nil, 0
end
---Adds the mark to the buffer, clipped by the line.
---Queues the remainder if the mark continues after the line.
---@param m MarkInfo
---@param buf integer
---@param line integer
---@param next_marks MarkInfo[]
local function add_mark(m, buf, line, next_marks)
local cur_start_l = m.start_line
local cur_start_c = m.start_col
if cur_start_l < line then
cur_start_l = line
cur_start_c = 0
end
local cur_opts = m.opts
if cur_opts.end_line >= line + 1 then
cur_opts = vim.deepcopy(cur_opts, true)
cur_opts.end_line = line + 1
cur_opts.end_col = 0
table.insert(next_marks, m)
end
local empty = cur_opts.end_line < cur_start_l
or (cur_opts.end_line == cur_start_l and cur_opts.end_col <= cur_start_c)
if cur_start_l <= line and not empty then
api.nvim_buf_set_extmark(buf, ns, cur_start_l, cur_start_c, cur_opts)
end
end
---@param self vim.treesitter.highlighter
---@param win integer
---@param buf integer
---@param line integer
---@param on_spell boolean
---@param on_conceal boolean
local function on_line_impl(self, buf, line, on_spell, on_conceal)
local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
self._conceal_checked[line] = true
self:for_each_highlight_state(function(state)
self:for_each_highlight_state(win, function(state)
local root_node = state.tstree:root()
local root_start_row, _, root_end_row, _ = root_node:range()
@@ -323,6 +364,12 @@ local function on_line_impl(self, buf, line, on_spell, on_conceal)
return
end
local next_marks = {}
for _, mark in ipairs(state.prev_marks) do
add_mark(mark, buf, line, next_marks)
end
if state.iter == nil or state.next_row < line then
-- Mainly used to skip over folds
@@ -362,7 +409,7 @@ local function on_line_impl(self, buf, line, on_spell, on_conceal)
local url = get_url(match, buf, capture, metadata)
if hl and end_row >= line and not on_conceal and (not on_spell or spell ~= nil) then
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
local opts = {
end_line = end_row,
end_col = end_col,
hl_group = hl,
@@ -371,7 +418,9 @@ local function on_line_impl(self, buf, line, on_spell, on_conceal)
conceal = conceal,
spell = spell,
url = url,
})
}
local mark = { start_line = start_row, start_col = start_col, opts = opts }
add_mark(mark, buf, line, next_marks)
end
if
@@ -390,27 +439,30 @@ local function on_line_impl(self, buf, line, on_spell, on_conceal)
state.next_row = start_row
end
end
state.prev_marks = next_marks
end)
end
---@private
---@param _win integer
---@param win integer
---@param buf integer
---@param line integer
function TSHighlighter._on_line(_, _win, buf, line, _)
function TSHighlighter._on_line(_, win, buf, line, _)
local self = TSHighlighter.active[buf]
if not self then
return
end
on_line_impl(self, buf, line, false, false)
on_line_impl(self, win, buf, line, false, false)
end
---@private
---@param win integer
---@param buf integer
---@param srow integer
---@param erow integer
function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
function TSHighlighter._on_spell_nav(_, win, buf, srow, _, erow, _)
local self = TSHighlighter.active[buf]
if not self then
return
@@ -418,30 +470,31 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
-- Do not affect potentially populated highlight state. Here we just want a temporary
-- empty state so the C code can detect whether the region should be spell checked.
local highlight_states = self._highlight_states
self:prepare_highlight_states(srow, erow)
local highlight_states = self._highlight_states[win]
self:prepare_highlight_states(win, srow, erow)
for row = srow, erow do
on_line_impl(self, buf, row, true, false)
on_line_impl(self, win, buf, row, true, false)
end
self._highlight_states = highlight_states
self._highlight_states[win] = highlight_states
end
---@private
---@param win integer
---@param buf integer
---@param row integer
function TSHighlighter._on_conceal_line(_, _, buf, row)
function TSHighlighter._on_conceal_line(_, win, buf, row)
local self = TSHighlighter.active[buf]
if not self or not self._conceal_line or self._conceal_checked[row] then
return
end
-- Do not affect potentially populated highlight state.
local highlight_states = self._highlight_states
local highlight_states = self._highlight_states[win]
self.tree:parse({ row, row })
self:prepare_highlight_states(row, row)
on_line_impl(self, buf, row, false, true)
self._highlight_states = highlight_states
self:prepare_highlight_states(win, row, row)
on_line_impl(self, win, buf, row, false, true)
self._highlight_states[win] = highlight_states
end
---@private
@@ -466,33 +519,33 @@ function TSHighlighter._on_win(_, win, buf, topline, botline)
if not self then
return false
end
self.parsing = self.parsing
self.parsing[win] = self.parsing[win]
or nil
== self.tree:parse({ topline, botline + 1 }, function(_, trees)
if trees and self.parsing then
self.parsing = false
api.nvim__redraw({ win = win, valid = false, flush = false })
if trees and self.parsing[win] then
self.parsing[win] = false
if api.nvim_win_is_valid(win) then
api.nvim__redraw({ win = win, valid = false, flush = false })
end
end
end)
if not self.parsing then
if not self.parsing[win] then
self.redraw_count = self.redraw_count + 1
self:prepare_highlight_states(topline, botline)
self:prepare_highlight_states(win, topline, botline)
else
self:for_each_highlight_state(function(state)
self:for_each_highlight_state(win, function(state)
-- TODO(ribru17): Inefficient. Eventually all marks should be applied in on_buf, and all
-- non-folded ranges of each open window should be merged, and iterators should only be
-- created over those regions. This would also fix #31777.
--
-- Currently this is not possible because the parser discards previously parsed injection
-- trees upon parsing a different region.
--
-- It would also be nice if rather than re-querying extmarks for old trees, we could tell the
-- decoration provider to not clear previous ephemeral marks for this redraw cycle.
state.iter = nil
state.next_row = 0
end)
end
return #self._highlight_states > 0
local hl_states = self._highlight_states[win] or {}
return #hl_states > 0
end
api.nvim_set_decoration_provider(ns, {

View File

@@ -956,7 +956,9 @@ function LanguageTree:_get_injection(match, metadata)
local ft = vim.filetype.match({ filename = text })
lang = ft and resolve_lang(ft)
elseif name == 'injection.content' then
ranges = get_node_ranges(node, self._source, metadata[id], include_children)
for _, range in ipairs(get_node_ranges(node, self._source, metadata[id], include_children)) do
ranges[#ranges + 1] = range
end
end
end
end
@@ -1080,8 +1082,8 @@ function LanguageTree:_edit(
end_row_new,
end_col_new
)
for _, tree in pairs(self._trees) do
tree:edit(
for i, tree in pairs(self._trees) do
self._trees[i] = tree:edit(
start_byte,
end_byte_old,
end_byte_new,

View File

@@ -979,8 +979,7 @@ function Query:iter_captures(node, source, start, stop, opts)
start, stop = value_or_node_range(start, stop, node)
-- Copy the tree to ensure it is valid during the entire lifetime of the iterator
local tree = node:tree():copy()
local tree = node:tree()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
-- For faster checks that a match is not in the cache.
@@ -1079,8 +1078,7 @@ function Query:iter_matches(node, source, start, stop, opts)
start, stop = value_or_node_range(start, stop, node)
-- Copy the tree to ensure it is valid during the entire lifetime of the iterator
local tree = node:tree():copy()
local tree = node:tree()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
local function iter()

View File

@@ -238,8 +238,8 @@ function VersionRange:has(version)
version = setmetatable(vim.deepcopy(version, true), Version)
end
if version then
if version.prerelease ~= self.from.prerelease then
return false
if self.from == self.to then
return version == self.from
end
return version >= self.from and (self.to == nil or version < self.to)
end
@@ -310,9 +310,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
local from = semver --- @type vim.Version?
local to = vim.deepcopy(semver, true) --- @type vim.Version?
---@diagnostic disable: need-check-nil
if mods == '' or mods == '=' then
to.patch = to.patch + 1
elseif mods == '<' then
if mods == '<' then
from = M._version({})
elseif mods == '<=' then
from = M._version({})

View File

@@ -26,6 +26,8 @@
</screenshots>
<releases>
<release date="2025-08-31" version="0.11.4"/>
<release date="2025-07-12" version="0.11.3"/>
<release date="2025-05-30" version="0.11.2"/>
<release date="2025-04-26" version="0.11.1"/>
<release date="2025-03-26" version="0.11.0"/>

View File

@@ -127,6 +127,7 @@
"eval"
"sign"
"abort"
"substitute"
] @keyword
(map_statement
@@ -252,6 +253,9 @@
(heredoc
(parameter) @keyword)
(script
(parameter) @keyword)
[
(marker_definition)
(endmarker)
@@ -313,6 +317,9 @@
(binary_operation
"." @operator)
(lua_statement
"=" @keyword)
; Punctuation
[
"("

View File

@@ -14,8 +14,8 @@ syn keyword DiagnosticError ERROR[:]
syn keyword DiagnosticWarn WARNING[:]
syn keyword DiagnosticOk OK[:]
" Note: hs=e starts higlighting on the title line (instead of the "===" line).
syn match helpSectionDelim /^======*\n.*$/hs=e
highlight helpSectionDelim gui=reverse cterm=reverse
syn match healthHeadingChar "=" conceal cchar= contained containedin=helpSectionDelim
syn match healthSectionDelim /^======*\n.*$/hs=e
highlight default healthSectionDelim gui=reverse cterm=reverse
syn match healthHeadingChar "=" conceal cchar= contained containedin=healthSectionDelim
let b:current_syntax = "checkhealth"

View File

@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Vim help file
" Maintainer: The Vim Project <https://github.com/vim/vim>
" Last Change: 2024 Oct 16
" Last Change: 2024 Dec 15
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" Quit when a (custom) syntax file was already loaded
@@ -12,15 +12,36 @@ endif
let s:cpo_save = &cpo
set cpo&vim
if !exists('g:help_example_languages')
let g:help_example_languages = #{ vim: 'vim' }
endif
syn match helpHeadline "^[A-Z.][-A-Z0-9 .,()_']*?\=\ze\(\s\+\*\|$\)"
syn match helpSectionDelim "^===.*===$"
syn match helpSectionDelim "^---.*--$"
" Nvim: support language annotation in codeblocks
if has("conceal")
syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<" concealends
syn region helpExample matchgroup=helpIgnore
\ start="\%(^\| \)>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<" concealends
else
syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<"
syn region helpExample matchgroup=helpIgnore
\ start="\%(^\| \)>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<"
endif
for [s:lang, s:syntax] in g:help_example_languages->items()
unlet! b:current_syntax
" silent! to prevent E403
execute 'silent! syn include' $'@helpExampleHighlight_{s:lang}'
\ $'syntax/{s:syntax}.vim'
execute $'syn region helpExampleHighlight_{s:lang} matchgroup=helpIgnore'
\ $'start=/\%(^\| \)>{s:lang}$/'
\ 'end=/^[^ \t]/me=e-1 end=/^</'
\ (has("conceal") ? 'concealends' : '')
\ $'contains=@helpExampleHighlight_{s:lang} keepend'
endfor
unlet! s:lang s:syntax
syn match helpHyperTextJump "\\\@<!|[#-)!+-~]\+|" contains=helpBar
syn match helpHyperTextEntry "\*[#-)!+-~]\+\*\s"he=e-1 contains=helpStar
syn match helpHyperTextEntry "\*[#-)!+-~]\+\*$" contains=helpStar

View File

@@ -19,7 +19,7 @@ syn match qfError "error" contained
syn cluster qfType contains=qfError
" Hide file name and line number for help outline (TOC).
if has_key(w:, 'qf_toc') || get(w:, 'quickfix_title') =~# '\<TOC$'
if has_key(w:, 'qf_toc') || get(w:, 'quickfix_title') =~# '\<TOC$\|\<Table of contents\>'
setlocal conceallevel=3 concealcursor=nc
syn match Ignore "^[^|]*|[^|]*| " conceal
endif

3
runtime/syntax/tutor.lua Normal file
View File

@@ -0,0 +1,3 @@
vim.cmd [[
syntax match tutorExpect /^--->.*$/
]]

View File

@@ -1,5 +1,7 @@
# Welcome to the Neovim Tutorial
# Chapter 1
Neovim is a very powerful editor that has many commands, too many to explain in
a tutorial such as this. This tutorial is designed to describe enough of the
commands that you will be able to easily use Neovim as an all-purpose editor.
@@ -302,7 +304,7 @@ it would be easier to simply type two d's to delete a line.
3. Now move to the fourth line.
4. Type `2dd`{normal} to delete two lines, then press `u`{normal} twice to undo all three lines.
4. Type `2dd`{normal} to delete two lines.
1) Roses are red,
2) Mud is fun,
@@ -366,7 +368,7 @@ Fiix the errors oon thhis line and reeplace them witth undo.
** Type `p`{normal} to put previously deleted text after the cursor. **
1. Move the cursor to the first line below.
1. Move the cursor to the first `--->` line below.
2. Type `dd`{normal} to delete the line and store it in a Neovim register.
@@ -376,10 +378,10 @@ Fiix the errors oon thhis line and reeplace them witth undo.
5. Repeat steps 2 through 4 to put all the lines in correct order.
d) Can you learn too?
b) Violets are blue,
c) Intelligence is learned,
a) Roses are red,
---> d) Can you learn too?
---> b) Violets are blue,
---> c) Intelligence is learned,
---> a) Roses are red,
NOTE: You can also put the text before the cursor with `P`{normal} (capital P).
@@ -978,6 +980,9 @@ Run `:help nvim-quickstart`{vim} for more information on extending Nvim.
# CONCLUSION
This concludes Chapter 1 of the Vim Tutor. Consider continuing with
[Chapter 2](@tutor:vim-02-beginner).
This was intended to give a brief overview of the Neovim editor, just enough to
allow you to use it fairly easily. It is far from complete as Neovim has
many many more commands. Consult the help often.

View File

@@ -1,44 +1,40 @@
{
"expect": {
"103": "The cow jumped over the moon.",
"125": "There is some text missing from this line.",
"126": "There is some text missing from this line.",
"145": "There is some text missing from this line.",
"146": "There is some text missing from this line.",
"147": "There is also some text missing here.",
"148": "There is also some text missing here.",
"216": "There are some words that don't belong in this sentence.",
"232": "Somebody typed the end of this line twice.",
"271": -1,
"290": "This line of words is cleaned up.",
"307": "1) Roses are red,",
"308": "3) Violets are blue,",
"309": "6) Sugar is sweet",
"310": "7) And so are you.",
"311": "7) And so are you.",
"312": "7) And so are you.",
"313": "7) And so are you.",
"333": "Fix the errors on this line and replace them with undo.",
"379": -1,
"380": -1,
"381": -1,
"382": -1,
"398": "When this line was typed in, someone pressed some wrong keys!",
"399": "When this line was typed in, someone pressed some wrong keys!",
"419": "This line has a few words that need changing using the change operator.",
"420": "This line has a few words that need changing using the change operator.",
"440": "The end of this line needs to be corrected using the c$ command.",
"441": "The end of this line needs to be corrected using the c$ command.",
"504": -1,
"523": -1,
"546": "Usually the best time to see the flowers is in the spring.",
"741": -1,
"746": -1,
"762": "This line will allow you to practice appending text to a line.",
"763": "This line will allow you to practice appending text to a line.",
"783": "Adding 123 to 456 gives you 579.",
"784": "Adding 123 to 456 gives you 579.",
"810": "a) This is the first item.",
"811": "b) This is the second item."
"105": "The cow jumped over the moon.",
"127": "There is some text missing from this line.",
"128": "There is some text missing from this line.",
"147": "There is some text missing from this line.",
"148": "There is some text missing from this line.",
"149": "There is also some text missing here.",
"150": "There is also some text missing here.",
"218": "There are some words that don't belong in this sentence.",
"234": "Somebody typed the end of this line twice.",
"273": -1,
"292": "This line of words is cleaned up.",
"309": "1) Roses are red,",
"310": "",
"311": "3) Violets are blue,",
"312": "",
"313": "",
"314": "6) Sugar is sweet",
"315": "7) And so are you.",
"335": "Fix the errors on this line and replace them with undo.",
"400": "When this line was typed in, someone pressed some wrong keys!",
"401": "When this line was typed in, someone pressed some wrong keys!",
"421": "This line has a few words that need changing using the change operator.",
"422": "This line has a few words that need changing using the change operator.",
"442": "The end of this line needs to be corrected using the c$ command.",
"443": "The end of this line needs to be corrected using the c$ command.",
"506": -1,
"525": -1,
"548": "Usually the best time to see the flowers is in the spring.",
"743": -1,
"748": -1,
"764": "This line will allow you to practice appending text to a line.",
"765": "This line will allow you to practice appending text to a line.",
"785": "Adding 123 to 456 gives you 579.",
"786": "Adding 123 to 456 gives you 579.",
"812": "a) This is the first item.",
"813": "b) This is the second item."
}
}

View File

@@ -0,0 +1,194 @@
# Welcome to the Neovim Tutorial
# Chapter 2
Hic Sunt Dracones: if this is your first exposure to vim and you
intended to avail yourself of the introductory chapter, kindly type
on the command line of the Vim editor
~~~ cmd
:Tutor vim-01-beginner
~~~
Or just open the [first chapter](@tutor:vim-01-beginner) of the tutor at the link.
The approximate time required to complete this chapter is 8-10 minutes,
depending upon how much time is spent with experimentation.
# Lesson 2.1.1: THE NAMED REGISTERS
** Store two yanked words concurrently and then paste them **
1. Move the cursor to the line below marked ✓
2. Navigate to any point on the word 'Edward' and type `"ayiw`{normal}
**MNEMONIC**: *into register(") named (a) (y)ank (i)nner (w)ord*
3. Navigate forward to the word 'cookie' (`fk`{normal} or `2fc`{normal}
or `$2b`{normal} or `/co`{normal} `<ENTER>`{normal}) and type `"byiw`{normal}
4. Navigate to any point on the word 'Vince' and type `ciw<CTRL-r>a<ESC>`{normal}
**MNEMONIC**: *(c)hange (i)nner (w)ord with <contents of (r)egister> named (a)*
5. Navigate to any point on the word 'cake' and type `ciw<CTRL-r>b<ESC>`{normal}
a) Edward will henceforth be in charge of the cookie rations
b) In this capacity, Vince will have sole cake discretionary powers
NOTE: Delete also works into registers, i.e. `"sdiw`{normal} will delete
the word under the cursor into register s.
REFERENCE: [Registers](registers)
[Named Registers](quotea)
[Motion](text-objects)
[CTRL-R](i_CTRL-R)
# Lesson 2.1.2: THE EXPRESSION REGISTER
** Insert the results of calculations on the fly **
1. Move the cursor to the line below marked ✗
2. Navigate to any point on the supplied number
3. Type `ciw<CTRL-r>=`{normal}60\*60\*24 `<ENTER>`{normal}
4. On the next line, enter insert mode and add today's date with
`<CTRL-r>=`{normal}`system('date')`{vim} `<ENTER>`{normal}
NOTE: All calls to system are OS dependent, e.g. on Windows use
`system('date /t')`{vim} or `:r!date /t`{vim}
I have forgotten the exact number of seconds in a day, is it 84600?
Today's date is:
NOTE: the same can be achieved with `:pu=`{normal}`system('date')`{vim}
or, with fewer keystrokes `:r!date`{vim}
REFERENCE: [Expression Register](quote=)
# Lesson 2.1.3: THE NUMBERED REGISTERS
** Press `yy`{normal} and `dd`{normal} to witness their effect on the registers **
1. Move the cursor to the line below marked ✓
2. yank the zeroth line, then inspect registers with `:reg`{vim} `<ENTER>`{normal}
3. delete line 0. with `"cdd`{normal}, then inspect registers
(Where do you expect line 0 to be?)
4. continue deleting each successive line, inspecting `:reg`{vim} as you go
NOTE: You should notice that old full-line deletions move down the list
as new full-line deletions are added
5. Now (p)aste the following registers in order; c, 7, 4, 8, 2. i.e. `"7p`{normal}
0. This
9. wobble
8. secret
7. is
6. on
5. axis
4. a
3. war
2. message
1. tribute
NOTE: Whole line deletions (`dd`{normal}) are much longer lived in the
numbered registers than whole line yanks, or deletions involving
smaller movements
REFERENCE: [Numbered Registers](quote0)
# Lesson 2.1.4: THE BEAUTY OF MARKS
** Code monkey arithmetic avoidance **
NOTE: a common conundrum when coding is moving around large chunks of code.
The following technique helps avoid number line calculations associated
with operations like `"a147d`{normal} or `:945,1091d a`{vim} or even worse
using `i<CTRL-r>=`{normal}1091-945 `<ENTER>`{normal} first
1. Move the cursor to the line below marked ✓
2. Go to the first line of the function and mark it with `ma`{normal}
NOTE: exact position on line is NOT important!
3. Navigate to the end of the line and then the end of the code block
with `$%`{normal}
4. Delete the block into register a with `"ad'a`{normal}
**MNEMONIC**: *into register(") named (a) put the (d)eletion from the cursor to
the LINE containing mark(') (a)*
5. Paste the block between BBB and CCC `"ap`{normal}
NOTE: practice this operation multiple times to become fluent `ma$%"ad'a`{normal}
~~~ cmd
AAA
function itGotRealBigRealFast() {
if ( somethingIsTrue ) {
doIt()
}
// the taxonomy of our function has changed and it
// no longer makes alphabetical sense in its current position
// imagine hundreds of lines of code
// naively you could navigate to the start and end and record or
// remember each line number
}
BBB
CCC
~~~
NOTE: marks and registers do not share a namespace, therefore register a is
completely independent of mark a. This is not true of registers and
macros.
REFERENCE: [Marks](marks)
[Mark Motions](mark-motions) (difference between ' and \`)
# Lesson 2.1 SUMMARY
1. To store (yank, delete) text into, and retrieve (paste) from, a total of
26 registers (a-z)
2. Yank a whole word from anywhere within a word: `yiw`{normal}
3. Change a whole word from anywhere within a word: `ciw`{normal}
4. Insert text directly from registers in insert mode: `<CTRL-r>a`{normal}
5. Insert the results of simple arithmetic operations:
`<CTRL-r>=`{normal}60\*60 `<ENTER>`{normal} in insert mode
6. Insert the results of system calls:
`<CTRL-r>=`{normal}`system('ls -1')`{vim} in insert mode
7. Inspect registers with `:reg`{vim}
8. Learn the final destination of whole line deletions: `dd`{normal} in
the numbered registers, i.e. descending from register 1 - 9. Appreciate
that whole line deletions are preserved in the numbered registers longer
than any other operation
9. Learn the final destination of all yanks in the numbered registers and
how ephemeral they are
10. Place marks from command mode `m[a-zA-Z0-9]`{normal}
11. Move line-wise to a mark with `'`{normal}
# CONCLUSION
This concludes chapter two of the Vim Tutor. It is a work in progress.
This chapter was written by Paul D. Parker.
Modified for vim-tutor-mode by Restorer.

View File

@@ -0,0 +1,10 @@
{
"expect": {
"36": -1,
"37": "b) In this capacity, Edward will have sole cookie discretionary powers",
"64": "I have forgotten the exact number of seconds in a day, is it 86400",
"65": -1,
"91": -1,
"138": -1
}
}

View File

@@ -368,7 +368,7 @@ This is just a line with words you can move around in.
** 最後に削除された行をカーソルの後に貼り付ける(Put)には `p`{normal} をタイプします。 **
1. と示された以下の最初の行にカーソルを移動しましょう。
1. `--->` と示された以下の最初の行にカーソルを移動しましょう。
2. `dd`{normal} とタイプして行を削除し、Neovim のレジスタに格納しましょう。
@@ -378,10 +378,10 @@ This is just a line with words you can move around in.
5. 順番が正しくなる様にステップ 2 から 4 を繰り返しましょう。
d) 貴方も学ぶことができるか?
b) 菫は青く
c) 知恵とは学ぶもの
a) 薔薇は赤く
---> d) 貴方も学ぶことができるか?
---> b) 菫は青く
---> c) 知恵とは学ぶもの
---> a) 薔薇は赤く
NOTE: `P`{normal} (大文字 P)とタイプすることで、カーソルの前に貼り付ける事もできます。

View File

@@ -11,18 +11,14 @@
"233": "誰かがこの行の最後を2度タイプしました。",
"272": -1,
"291": "この行の単語は綺麗になった。",
"308": -1,
"309": -1,
"310": -1,
"311": -1,
"312": -1,
"313": -1,
"314": -1,
"308": "1) 薔薇は赤く",
"309": "",
"310": "3) 菫は青く",
"311": "",
"312": "",
"313": "6) 砂糖は甘く",
"314": "7) そして貴方も",
"335": "この行の間違いを修正し、後でそれらの修正を取り消します。",
"381": -1,
"382": -1,
"383": -1,
"384": -1,
"400": "この行を入力した時に、その人は幾つか間違ったキーを押しました!",
"401": "この行を入力した時に、その人は幾つか間違ったキーを押しました!",
"421": "This line has a few words that need changing using the change operator.",

View File

@@ -1,35 +1,35 @@
{
"expect": {
"63": "This is text with **important information**",
"64": "This is text with **important information**",
"71": "TODO: Document '&variable'",
"72": "TODO: Document '&variable'",
"78": "# This is a level 1 header",
"79": "# This is a level 1 header",
"80": "### This is a level 3 header",
"81": "### This is a level 3 header",
"82": "# This is a header with a label {*label*}",
"83": "# This is a header with a label {*label*}",
"108": "A link to help for the ['breakindent']('breakindent') option",
"109": "A link to help for the ['breakindent']('breakindent') option",
"123": "A link to the [Links](*links*) section",
"124": "A link to the [Links](*links*) section",
"139": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
"140": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
"157": "~~~ viml",
"158": "echom 'the value of &number is'.string(&number)",
"159": "~~~",
"161": "~~~ viml",
"162": "echom 'the value of &number is'.string(&number)",
"163": "~~~",
"188": "~~~ normal",
"189": "d2w",
"190": "~~~",
"192": "~~~ normal",
"193": "d2w",
"194": "~~~",
"206": "`d2w`{normal}",
"207": "`d2w`{normal}",
"244": -1
"56": "This is text with **important information**",
"57": "This is text with **important information**",
"64": "TODO: Document '&variable'",
"65": "TODO: Document '&variable'",
"71": "# This is a level 1 header",
"72": "# This is a level 1 header",
"73": "### This is a level 3 header",
"74": "### This is a level 3 header",
"75": "# This is a header with a label {*label*}",
"76": "# This is a header with a label {*label*}",
"101": "A link to help for the ['breakindent']('breakindent') option",
"102": "A link to help for the ['breakindent']('breakindent') option",
"116": "A link to the [Links](*links*) section",
"117": "A link to the [Links](*links*) section",
"132": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
"133": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
"150": "~~~ viml",
"151": "echom 'the value of &number is'.string(&number)",
"152": "~~~",
"154": "~~~ viml",
"155": "echom 'the value of &number is'.string(&number)",
"156": "~~~",
"181": "~~~ normal",
"182": "d2w",
"183": "~~~",
"185": "~~~ normal",
"186": "d2w",
"187": "~~~",
"199": "`d2w`{normal}",
"200": "`d2w`{normal}",
"237": -1
}
}

View File

@@ -0,0 +1,967 @@
# 欢迎来到 Neovim 教程
# 第 1 章
Neovim 是一个功能非常强大的编辑器,它的命令多到无法在这篇教程里一一讲解。本教程
旨在介绍足够多的基本命令,让你能轻松地将 Neovim 作为通用编辑器来使用。
请务必记住,本教程是为“在实践中学习”而设计的。这意味着你需要亲手完成这些练习才能
真正掌握它们。如果你只看不练,很快就会忘记最重要的内容!
现在请确保你的大写锁定键Caps-Lock是关闭状态然后多次按下 `j`{normal} 键,
直到光标移动到第 0 课完全充满屏幕为止。
# 第 0 课
NOTE: 课程中的命令会修改本文,但这些更改不会被保存。不用担心会搞乱什么;只要记住
按 [<Esc>](<Esc>) 键,再按 [u](u) 键,就可以撤销最近的更改。
本教程是交互式的,有几件事你需要知道。
- 在 [这样的](holy-grail ) 链接上按 [<Enter>](<Enter>) 键可以打开链接的帮助文档。
- 或者,你也可以在任意单词上按 [K](K) 键来查找它的文档!
- 你可以用 `:q`{vim} `<Enter>`{normal} 来关闭这个帮助窗口。
当左边出现 ✗ 标志时,你需要去修改文本。当你正确地完成修改后,左边的 ✗ 标志就会变
成 ✓。我想你已经能体会到 Neovim 的强大之处了。
其他时候,你会被提示运行一个命令(稍后会对此进行解释):
`:help`{vim} `<Enter>`{normal}
或者按下一系列按键:
~~~ normal
<Esc>0f<Space>d3wP$P
~~~
尖括号(<>)里的文本(如 `<Enter>`{normal})代表你需要按下的键,而不是要输入的文本。
现在,移动到下一课(使用 `j`{normal} 键向下滚动)。
# 第 1.1 课:移动光标
** 按 `h`、`j`、`k`、`l` 键来移动光标,如下所示。 **
k 提示:`h`{normal} 键在左边,向左移动。
← h l → `l`{normal} 键在右边,向右移动。
j `j`{normal} 键看起来像一个向下的箭头。
1. 在屏幕上四处移动光标,直到你习惯这种操作。
2. 按住向下键(`j`{normal})直到光标开始连续向下移动。
现在你知道如何移动到下一课了。
3. 使用下移键,移动到第 1.2 课。
NOTE: 如果你不确定自己输入了什么,随时可以按 <Esc> 键回到普通模式Normal mode
然后重新输入你想要的命令。
NOTE: 键盘上的方向键通常也能用。但一旦你习惯了,使用 hjkl 可以让你移动得更快。
# 第 1.2 课:退出 Neovim
!! NOTE: 在执行以下任何步骤之前,请先阅读完本课的全部内容 !!
1. 按下 <Esc> 键(确保你处于普通模式)。
2. 输入:
`:q!`{vim} `<Enter>`{normal}
这个命令会退出编辑器,并“丢弃”你所做的所有更改。
3. 重新打开 Neovim并通过执行带你进入本教程的命令回到这里。这个命令也许是
`:Tutor`{vim} `<Enter>`{normal}
4. 如果你已经记住了这些步骤并充满信心,请执行第 1 到 3 步来退出并重新进入编辑器。
NOTE: [:q!](:q) `<Enter>`{normal} 会丢弃你做的任何更改。在接下来的几课中,你将学习如何
将更改保存到文件中。
5. 向下移动光标到第 1.3 课。
# 第 1.3 课文本编辑——删除Deletion
** 按 `x`{normal} 键可以删除光标下的单个字符。 **
1. 将光标移动到下面标有 ✗ 的那一行。
2. 为了修正错误,请将光标移动到需要被删除的字符上。
3. 按下 [x 键](x) 来删除那个多余的字符。
4. 重复第 2 到 4 步,直到句子正确为止。
The ccow jumpedd ovverr thhe mooon.
The cow jumped over the moon.
5. 现在句子已经正确了,请继续学习第 1.4 课。
NOTE: 在学习本教程时,不必试图记住所有内容,你的 Neovim 词汇量会随着使用而增长。
可以考虑定期回到本教程进行复习。
# 第 1.4 课文本编辑——插入Insertion
** 按 `i`{normal} 键可以插入文本。 **
1. 将光标移动到下面标有 ✗ 的第一行。
2. 为了让第一行和第二行变得一样,请将光标移动到需要插入文本位置“之后”的那个字符上。
3. 按下 `i`{normal} 键,然后输入需要补充的内容。
4. 每个错误修正后,按 `<Esc>`{normal} 键回到普通模式。
重复第 2 到 4 步来修正整个句子。
There is text misng this .
There is some text missing from this line.
5. 当你熟练掌握插入文本后,请继续学习第 1.5 课。
# 第 1.5 课文本编辑——追加Appending
** 按 `A`{normal} 键可以在行末追加文本。 **
1. 将光标移动到下面标有 ✗ 的第一行。
光标在该行的哪个字符上并不重要。
2. 按下 [A](A) 键,然后输入需要补充的内容。
3. 文本追加完成后,按 `<Esc>`{normal} 键回到普通模式。
4. 将光标移动到标有 ✗ 的第二行,并重复第 2 和第 3 步来修正这个句子。
There is some text missing from th
There is some text missing from this line.
There is also some text miss
There is also some text missing here.
5. 当你熟练掌握追加文本后,请继续学习第 1.6 课。
# 第 1.6 课:编辑文件
** 使用 `:wq`{vim} 来写入(保存)文件并退出。 **
!! NOTE: 在执行以下任何步骤之前,请先阅读完本课的全部内容 !!
1. 像第 1.2 课那样退出本教程:`:q!`{vim}
或者,如果你能打开另一个终端,就在那个终端里执行以下操作。
2. 在 shell 提示符下输入这个命令:
~~~ sh
$ nvim tutor
~~~
'nvim' 是启动 Neovim 编辑器的命令,'tutor' 是你希望编辑的文件名。
请使用一个可以被修改的文件。
3. 像在前面课程中学到的那样,插入和删除一些文本。
4. 保存更改并退出 Neovim
~~~ cmd
:wq
~~~
注意,你需要按 `<Enter>` 键来执行该命令。
5. 如果你在第 1 步退出了本教程,请重启教程并移动到下面的总结部分。
6. 在阅读并理解了以上步骤后,亲手操作一遍。
# 第 1 课总结
1. 移动光标可以使用方向键,也可以使用 hjkl 键。
h (左) j (下) k (上) l (右)
2. 从 shell 提示符启动 Neovim输入
~~~ sh
$ nvim 文件名
~~~
3. 退出 Neovim输入`<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} 放弃所有更改。
或者输入:`<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} 保存所有更改。
4. 删除光标下的字符,输入:`x`{normal}
5. 插入或追加文本,输入:
`i`{normal} 插入文本 `<Esc>`{normal} 在光标前插入。
`A`{normal} 追加文本 `<Esc>`{normal} 在当前行末尾追加。
NOTE: 按 `<Esc>`{normal} 键会让你进入普通模式,或者取消一个不想继续输入的、未完成的命令。
现在,请继续学习第 2 课。
# 第 2.1 课删除类命令Deletion
** 输入 `dw`{normal} 来删除一个单词word。 **
1. 按 `<Esc>`{normal} 键确保你处于普通模式。
2. 将光标移动到下面标有 ✗ 的那一行。
3. 将光标移动到需要被删除的单词的开头。
4. 输入 [d](d)[w](w) 让这个单词消失。
There are a some words fun that don't belong paper in this sentence.
There are some words that don't belong in this sentence.
5. 重复第 3 和第 4 步,直到句子正确,然后继续学习第 2.2 课。
# 第 2.2 课:更多删除类命令
** 输入 `d$`{normal} 来删除从光标到行尾的内容。 **
1. 按 `<Esc>`{normal} 键确保你处于普通模式。
2. 将光标移动到下面标有 ✗ 的那一行。
3. 将光标移动到正确句子的末尾(在第一个 . 之后)。
4. 输入 `d$`{normal} 来删除到行尾的所有内容。
Somebody typed the end of this line twice. end of this line twice.
5. 继续学习第 2.3 课,来理解这背后的原理。
# 第 2.3 课关于操作符operator和移动motion
许多修改文本的命令都由一个 [操作符](operator) 和一个 [移动](navigation) 组成。
使用 [d](d) 删除操作符的命令格式如下:
d 移动
其中:
d - 是删除操作符。
移动 - 是操作符将要作用的范围(如下所列)。
一些常用的移动:
[w](w) - 到下一个单词的开头,但不包括其第一个字符。
[e](e) - 到当前单词的末尾,并包括最后一个字符。
[$]($) - 到当前行的末尾,并包括最后一个字符。
因此,输入 `de`{normal} 将会删除从光标到当前单词末尾的内容。
NOTE: 在普通模式下,不带操作符、只按下移动键,将会像预期的那样移动光标。
# 第 2.4 课:为移动增加计数
** 在移动命令前输入一个数字,可以将其重复执行那么多次。 **
1. 将光标移动到下面标有 ✓ 的那行的开头。
2. 输入 `2w`{normal},光标会向前移动两个单词。
3. 输入 `3e`{normal},光标会移动到前方第三个单词的末尾。
4. 输入 `0`{normal}[零](0))可以移动到行首。
5. 用不同的数字重复第 2 和第 3 步。
This is just a line with words you can move around in.
6. 继续学习第 2.5 课。
# 第 2.5 课:使用计数删除更多内容
** 在操作符和移动之间加上一个数字,可以将其重复执行那么多次。 **
在前面提到的“删除操作符 + 移动”组合中,你可以在移动前插入一个计数来删除更多内容:
d 数字 移动
1. 将光标移动到下面标有 ✗ 的那一行中第一个全大写单词上。
2. 输入 `d2w`{normal} 来删除两个全大写单词。
3. 重复第 1 和第 2 步,但使用不同的计数,用一个命令删除剩余连续的全大写单词。
This ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
# 第 2.6 课:行操作
** 输入 `dd`{normal} 来删除一整行。 **
由于删除整行的操作非常频繁Vi 的设计者们决定,输入两个 d 来删除一行会更方便。
1. 将光标移动到下面短语的第 2 行。
2. 输入 [dd](dd) 来删除该行。
3. 现在移动到第 4 行。
4. 输入 `2dd`{normal} 来删除两行。
1) Roses are red,
2) Mud is fun,
3) Violets are blue,
4) I have a car,
5) Clocks tell time,
6) Sugar is sweet
7) And so are you.
# 第 2.7 课撤销命令Undo
** 按 `u`{normal} 撤销上一个命令,按 `U`{normal} 恢复一整行。 **
1. 将光标移动到下面标有 ✗ 的那一行,并把它放在第一个错误上。
2. 输入 `x`{normal} 来删除第一个多余的字符。
3. 现在输入 `u`{normal} 来撤销上一次执行的命令。
4. 这次,使用 `x`{normal} 命令修正该行所有的错误。
5. 现在输入大写的 `U`{normal},将该行恢复到它最初始的状态。
6. 现在多次输入 `u`{normal},来撤销 `U`{normal} 以及之前的命令。
7. 现在多次输入 `<C-r>`{normal}Ctrl + R来重做redo那些被撤销的命令。
Fiix the errors oon thhis line and reeplace them witth undo.
Fix the errors on this line and replace them with undo.
8. 这些都是非常有用的命令。现在请继续学习第 2 课的总结。
# 第 2 课总结
1. 删除从光标到下一个单词开头的内容,输入: `dw`{normal}
2. 删除从光标到行尾的内容,输入: `d$`{normal}
3. 删除一整行,输入: `dd`{normal}
4. 重复一个移动,在它前面加上数字: `2w`{normal}
5. 修改类命令的格式是:
操作符 [数字] 移动
其中:
操作符 - 是要做什么,比如 [d](d) 代表删除。
[数字] - 是一个可选的计数,用来重复移动。
移动 - 定义了操作符要作用的文本范围,例如:
[w](w)(单词),
[$]($)(到行尾),等等。
6. 移动到行首,使用零:[0](0)
7. 撤销之前的操作,输入: `u`{normal}(小写 u
撤销对一整行的所有更改,输入: `U`{normal}(大写 U
重做被撤销的操作,输入: `<C-r>`{normal}
# 第 3.1 课粘贴命令Put
** 输入 `p`{normal} 可以将之前删除的文本粘贴到光标之后。 **
1. 将光标移动到下面第一个标有 `--->` 的行。
2. 输入 `dd`{normal} 来删除该行,并将其存入 Neovim 的一个寄存器中。
3. 将光标移动到 c) 行,也就是被删除那一行的“上一行”。
4. 输入 `p`{normal},将之前删除的行粘贴到光标下方。
5. 重复第 2 到 4 步将所有行按正确的顺序abcd排列。
---> d) Can you learn too?
---> b) Violets are blue,
---> c) Intelligence is learned,
---> a) Roses are red,
NOTE: 你也可以用 `P`{normal}(大写 P将文本粘贴到光标之前。
# 第 3.2 课替换命令Replace
** 输入 `rx`{normal} 可以将光标下的字符替换为 x。 **
1. 将光标移动到下面标有 ✗ 的第一行。
2. 将光标移动到第一个错误字符上。
3. 输入 `r`{normal},然后输入正确的字符。
4. 重复第 2 和第 3 步,直到第一行和第二行完全一样。
Whan this lime was tuoed in, someone presswd some wrojg keys!
When this line was typed in, someone pressed some wrong keys!
5. 现在请继续学习第 3.3 课。
NOTE: 请记住,你应该通过实践来学习,而不是死记硬背。
# 第 3.3 课更改操作符Change
** 要更改到单词末尾,输入 `ce`{normal}。 **
1. 将光标移动到下面标有 ✗ 的第一行。
2. 将光标放在 "lubw" 的 "u" 上。
3. 输入 `ce`{normal},然后输入正确的单词(在这种情况是输入 "ine")。
4. 按下 `<Esc>`{normal},然后移动到下一个需要修改的字符。
5. 重复第 3 和第 4 步,直到第一句和第二句完全一样。
This lubw has a few wptfd that mrrf changing usf the change operator.
This line has a few words that need changing using the change operator.
请注意,[c](c)e 会删除单词并让你进入插入模式Insert mode
# 第 3.4 课:使用 `c`{normal} 进行更多更改
** 更改操作符可以和删除操作符使用相同的移动。 **
1. 更改操作符的工作方式和删除操作符一样。格式是:
c [数字] 移动
2. 移动也是一样的,比如 `w`{normal}(单词)和 `$`{normal}(到行尾)。
3. 移动到下面标有 ✗ 的第一行。
4. 将光标移动到第一个错误处。
5. 输入 `c$`{normal},然后输入该行余下正确的内容(参照第二行),最后按 `<Esc>`{normal}。
The end of this line needs some help to make it like the second.
The end of this line needs to be corrected using the c$ command.
NOTE: 在输入时你可以使用退格键Backspace来修正错误。
# 第 3 课总结
1. 要粘贴刚刚删除的文本,输入 [p](p)。这会把删除的文本放在光标“之后”
(如果删除的是一整行,它会被粘贴到光标所在行的下一行)。
2. 要替换光标下的单个字符,输入 [r](r) 然后输入你想要的那个字符。
3. [更改操作符](c) 允许你更改从光标开始到一个移动命令结束位置的文本。
输入 `ce`{normal} 来更改到单词末尾,`c$`{normal} 来更改到行尾,等等。
4. 更改命令的格式是:
c [数字] 移动
现在请继续学习下一课。
# 第 4.1 课:光标位置和文件状态
** 输入 `<C-g>`{normal} 来显示你在文件中的位置和文件状态。
输入 `{count}G`{normal} 来移动到文件中的第 {count} 行。 **
NOTE: 在执行任何步骤之前,请先阅读完本课的全部内容!!
1. 按住 `<Ctrl>`{normal} 键并按下 `g`{normal}。我们称这个操作为 `<C-g>`{normal}。
屏幕底部会出现一条消息,包含文件名和你在文件中的位置。
请记住行号,第 3 步会用到。
NOTE: 你可能会在屏幕右下角看到光标位置。这是因为设置了 ['ruler']('ruler') 选项。
2. 按 [G](G) 移动到文件的末尾。
输入 [gg](gg) 移动到文件的开头。
3. 输入你之前记住的行号,然后按 `G`{normal}。这会让你回到你第一次按 `<C-g>`{normal} 时所在的行。
4. 如果你觉得没问题,就执行第 1 到 3 步。
# 第 4.2 课:搜索命令
** 输入 `/`{normal} 后面跟一个短语,来搜索这个短语。 **
1. 在普通模式下,输入 `/`{normal} 字符。注意它和光标会出现在屏幕底部,
就像 `:`{normal} 命令一样。
2. 现在输入 errroor `<Enter>`{normal}。这是你想要搜索的词。
3. 要再次搜索同一个短语,只需输入 [n](n)。
要反向搜索同一个短语,输入 [N](N)。
4. 要反向搜索一个短语,使用 [?](?) 而不是 `/`{normal}。
5. 要回到你之前的位置,按 `<C-o>`{normal}。(按住 `<Ctrl>`{normal} 键的同时按字母 `o`{normal})。
重复按可以回到更早的位置。`<C-i>`{normal} 则会前进。
"errroor" is not the way to spell error; errroor is an error.
NOTE: 当搜索到达文件末尾时,它会从头开始继续搜索,除非 ['wrapscan']('wrapscan') 选项被关闭了。
# 第 4.3 课:括号匹配搜索
** 输入 `%`{normal} 来查找匹配的 ), ], }。 **
1. 将光标放在下面标有 ✓ 的那一行中的任意一个 (, [, { 上。
2. 现在输入 [%](%) 字符。
3. 光标会移动到与之匹配的括号上。
4. 再次输入 `%`{normal},光标会移回另一半匹配的括号。
5. 将光标移动到另一个 (, ), [, ], {, } 上,看看 `%`{normal} 的效果。
This ( is a test line with ('s, ['s, ] and {'s } in it. ))
NOTE: 这在调试程序中不匹配的括号时非常有用!
# 第 4.4 课替换命令Substitute
** 输入 `:s/old/new/g` 来用 "new" 替换 "old"。 **
1. 将光标移动到下面标有 ✗ 的那一行。
2. 输入
~~~ cmd
:s/thee/the/
~~~
NOTE: [:s](:s) 命令只改变了行中第一个匹配的 "thee"。
3. 现在输入
~~~ cmd
:s/thee/the/g
~~~
加上 g [标志](:s_flags) 意味着在行内进行全局替换,也就是改变该行中所有出现的 "thee"。
Usually thee best time to see thee flowers is in thee spring.
4. 要在两行之间替换一个字符串的所有出现,输入
~~~ cmd
:#,#s/old/new/g
~~~
其中 # 是要进行替换的行号范围(例如,`1,3` 表示从第 1 行到第 3 行,包含这两行)。
输入
~~~ cmd
:%s/old/new/g
~~~
可以在整个文件中进行替换。
输入
~~~ cmd
:%s/old/new/gc
~~~
可以在整个文件中查找所有出现,并对每一次替换进行确认。
NOTE: 你也可以先用可视模式Visual mode选中你想替换的行。这在后面的课程中会详细解释。
# 第 4 课总结
1. `<C-g>`{normal} 显示你的位置和文件状态。
`G`{normal} 移动到文件末尾。
数字 `G`{normal} 移动到指定的行号。
`gg`{normal} 移动到第一行。
2. 输入 `/`{normal} 后跟一个短语,会“向前”搜索该短语。
输入 `?`{normal} 后跟一个短语,会“向后”搜索该短语。
搜索后,输入 `n`{normal} 查找下一个(同方向),或 `N`{normal} 查找下一个(反方向)。
`<C-o>`{normal} 带你回到旧的光标位置,`<C-i>`{normal} 则去往新的位置。
3. 当光标在 (, ), [, ], {, } 上时,输入 `%`{normal} 会跳转到其匹配的另一半。
4. 将行内第一个 old 替换为 new输入
~~~ cmd
:s/old/new
~~~
将行内所有 old 替换为 new输入
~~~ cmd
:s/old/new/g
~~~
在两行 # 之间进行替换,输入
~~~ cmd
:#,#s/old/new/g
~~~
在整个文件中进行替换,输入
~~~ cmd
:%s/old/new/g
~~~
每次替换前进行确认,添加 'c' 标志
~~~ cmd
:%s/old/new/gc
~~~
# 第 5.1 课:如何执行外部命令
** 输入 `:!`{vim} 后面跟一个外部命令,来执行该命令。 **
1. 输入你熟悉的命令 `:`{normal},让光标定位到屏幕底部。这允许你输入一个
命令行命令。
2. 现在输入 [!](!cmd)(感叹号)字符。这允许你执行任何外部的 shell 命令。
3. 举个例子,在 "!" 后面输入 "ls",然后按 `<Enter>`{normal}。
这会显示你的目录列表,就像你在 shell 提示符下一样。
NOTE: 你可以用这种方式执行任何外部命令,并且可以带参数。
NOTE: 所有 `:`{vim} 命令都在你按下 `<Enter>`{normal} 后执行。
# 第 5.2 课:更多关于写入文件的知识
** 要保存对文本的更改,输入 `:w`{vim} 文件名。 **
1. 输入 `:!{unix:(ls),win:(dir)}`{vim} 来获取你的目录列表。
你已经知道在这之后必须按 `<Enter>`{normal}。
2. 选择一个还不存在的文件名,比如 TEST。
3. 现在输入:
~~~ cmd
:w TEST
~~~
(这里的 TEST 是你选择的文件名。)
4. 这会将当前文件以 TEST 的名字保存。
要验证这一点,再次输入 `:!{unix:(ls),win:(dir)}`{vim} 来查看你的目录。
NOTE: 如果你退出 Neovim然后用 `nvim TEST` 再次启动它,这个文件将会是你保存时教程
内容的一个精确副本。
5. 现在通过输入以下命令来删除该文件:
~~~ cmd
:!{unix:(rm),win:(del)} TEST
~~~
# 第 5.3 课:选择要写入的文本
** 要保存文件的一部分,输入 `v`{normal} 移动 `:w 文件名`{vim}。 **
1. 将光标移动到这一行。
2. 按下 [v](v) 并将光标移动到下面的第五项。注意文本被高亮了。
3. 按下 `:`{normal} 字符。在屏幕底部会出现:
`:'<,'>`{vim}
4. 输入
`w TEST`{vim}
这里的 TEST 是一个还不存在的文件名。在按 `<Enter>`{normal} 之前,
确认你看到的是:
`:'<,'>w TEST`{vim}
5. Neovim 会将被选中的行写入文件 TEST。使用 `:!{unix:(ls),win:(dir)}`{vim} 来查看它。
先不要删除它!我们将在下一课用到它。
NOTE: 按下 [v](v) 会启动 [可视选择Visual selection](visual-mode)。你可以移动光标来扩大或缩小选择范围。
然后你可以使用一个操作符来对选中的文本
做些什么。例如,`d`{normal} 会删除选中的文本。
# 第 5.4 课:读取和合并文件
** 要读取一个文件的内容,输入 `:r 文件名`{vim}。 **
1. 将光标放在这一行的正上方。
NOTE: 执行第 2 步后,你会看到第 5.3 课的文本。然后向下移动
才能再次看到本课内容。完成后按 `u`{normal} 撤销。
2. 现在使用以下命令读取你的 TEST 文件:
`:r TEST`{vim}
这里的 TEST 是你之前使用的文件名。
你读取的文件内容会被放在光标所在行的下方。
3. 要验证文件是否被读取,向上移动光标,你会注意到现在有两份第 5.3 课的内容,
一份是原始的,一份是读取进来的。
NOTE: 你也可以读取一个外部命令的输出。例如:
`:r !{unix:(ls),win:(dir)}`{vim}
会读取 `ls` 命令的输出,并将其放在光标下方。
# 第 5 课总结
1. [:!command](:!cmd) 执行一个外部命令。
一些有用的例子:
`:!{unix:(ls ),win:(dir)}`{vim} - 显示目录列表
`:!{unix:(rm ),win:(del)} 文件`{vim} - 删除文件
2. [:w](:w) 文件名 将当前 Neovim 文件以“文件名”这个名字写入磁盘。
3. [v](v) 移动 :w 文件名 将可视模式选中的行保存到文件“文件名”中。
4. [:r](:r) 文件名 读取磁盘文件“文件名”的内容,并将其放在光标位置的下方。
5. {unix:([:r !ls](:r!) ),win:([:r !dir](:r!))} 读取 {unix:(ls),win:(dir)} 命令的输出,并将其放在光标位置的下方。
# 第 6.1 课开启新行命令Open
** 输入 `o`{normal} 可以在光标下方开启一个新行,并进入插入模式。 **
1. 将光标移动到下面标有 ✓ 的那一行。
2. 输入小写字母 `o`{normal},在光标“下方”[开启](o)一个新行,并让你进入插入模式。
3. 现在输入一些文本,然后按 `<Esc>`{normal} 退出插入模式。完成后请删除你开启的行。
After typing `o`{normal} the cursor is placed on the open line in Insert mode.
4. 要在光标“上方”开启一个新行,只需输入[大写 O](O),而不是小写 `o`{normal}。
在下面这行上试试。完成后请删除你开启的行。
Open up a line above this by typing O while the cursor is on this line.
# 第 6.2 课追加命令Append
** 输入 `a`{normal} 可以在光标“之后”插入文本。 **
1. 将光标移动到下面标有 ✗ 的那行的开头。
2. 按 `e`{normal} 直到光标移动到 "li" 的末尾。
3. 输入小写字母 `a`{normal},在光标“之后”[追加](a)文本。
4. 参照下一行,补全这个单词。按 `<Esc>`{normal} 退出插入模式。
5. 使用 `e`{normal} 移动到下一个不完整的单词,并重复第 3 和第 4 步。
This li will allow you to pract appendi text to a line.
This line will allow you to practice appending text to a line.
NOTE: [a](a)、[i](i) 和 [A](A) 都会进入同一个插入模式,唯一的区别是字符被插入的位置不同。
# 第 6.3 课:另一种替换方式
** 输入大写 `R`{normal} 可以替换多个字符。 **
1. 将光标移动到下面标有 ✗ 的第一行。将光标移动到第一个 "xxx" 的开头。
2. 现在按下 `R`{normal}[大写 R](R))并输入第二行中对应的数字,用它来替换 "xxx"。
3. 按下 `<Esc>`{normal} 离开[替换模式Replace mode](mode-replace)。注意该行余下的部分保持不变。
4. 重复以上步骤来替换剩下的 "xxx"。
Adding 123 to xxx gives you xxx.
Adding 123 to 456 gives you 579.
NOTE: 替换模式很像插入模式,但你输入的每个字符都会替换掉一个已有的字符。
# 第 6.4 课:复制和粘贴文本
** 使用 `y`{normal} 操作符来复制yank文本用 `p`{normal} 来粘贴put它。 **
1. 去到下面标有 ✓ 的那一行,并将光标放在 "a)" 之后。
2. 用 `v`{normal} 启动可视模式,并将光标移动到 "first" 之前。
3. 输入 `y`{normal} 来 [复制yank](yank) 高亮的文本。
4. 将光标移动到下一行的末尾:`j$`{normal}
5. 输入 `p`{normal} 来 [粘贴put](put) 文本。
6. 按下 `a`{normal} 然后输入 "second"。按 `<Esc>`{normal} 离开
插入模式。
7. 使用可视模式选中 "item.",用 `y`{normal} 复制它,用 `j$`{normal} 移动到
下一行的末尾,然后用 `p`{normal} 在那里粘贴文本。
a) This is the first item.
b)
NOTE: 你可以把 `y`{normal} 当作一个操作符来使用:`yw`{normal} 会复制一个单词。
NOTE: 你可以用 `P`{normal} 在光标前粘贴,而不是在光标后。
# 第 6.5 课设置选项Set
** 设置一个选项,让搜索和替换命令忽略大小写。 **
Neovim 中有许多设置,你可以配置它们来满足你的需求。
1. 通过输入 `/ignore` 来搜索 'ignore'。
多按几次 `n`{normal} 来重复搜索。
2. 通过输入以下命令来设置 'ic'Ignore case即忽略大小写 选项:
~~~ cmd
:set ic
~~~
3. 现在再次按 `n`{normal} 搜索 'ignore'。
注意 Ignore 和 IGNORE 现在也能被找到了。
4. 设置 'hlsearch' 和 'incsearch' 选项:
~~~ cmd
:set hls is
~~~
5. 现在再次输入搜索命令,看看会发生什么:/ignore <Enter>
6. 要禁用忽略大小写,输入:
~~~ cmd
:set noic
~~~
7. 要反转一个设置的值,在它前面加上 "inv"
~~~ cmd
:set invic
~~~
NOTE: 要移除匹配项的高亮,输入:
~~~ cmd
:nohlsearch
~~~
NOTE: 如果你只想在某一次搜索命令中忽略大小写,在短语中使用 [\c](/\c)
/ignore\c <Enter>
# 第 6 课总结
1. 输入 `o`{normal} 在光标“下方”开启一个新行并进入插入模式。
输入 `O`{normal} 在光标“上方”开启一个新行。
2. 输入 `a`{normal} 在光标“之后”插入文本。
输入 `A`{normal} 在行尾之后插入文本。
3. `e`{normal} 命令移动到一个单词的末尾。
4. `y`{normal} 操作符复制文本,`p`{normal} 粘贴它。
5. 输入大写 `R`{normal} 会进入替换模式,直到按下 `<Esc>`{normal}。
6. 输入 "[:set](:set) xxx" 可以设置选项 "xxx"。一些有用的选项是:
'ic' 'ignorecase' 搜索时忽略大小写
'is' 'incsearch' 实时显示搜索短语的部分匹配
'hls' 'hlsearch' 高亮所有匹配的短语
你可以使用长选项名或短选项名。
7. 在选项前加上 "no" 来关闭一个选项:
~~~ cmd
:set noic
~~~
8. 在选项前加上 "inv" 来反转一个选项的值:
~~~ cmd
:set invic
~~~
# 第 7.1 课:获取帮助
** 使用在线帮助系统。 **
Neovim 有一个全面的在线帮助系统。
要开始使用,试试下面两种方法之一:
- 按下 `<F1>`{normal} 键(如果你有的话)
- 输入 `:help`{vim}
阅读帮助窗口中的文本,了解帮助系统是如何工作的。
输入 `<C-w><C-w>`{normal} 可以在窗口之间跳转。
输入 `:q`{vim} 关闭帮助窗口。
你几乎可以找到任何主题的帮助,只需给 ":help" 命令传递一个参数。
试试这些(别忘了按 <Enter>:
~~~ cmd
:help w
:help c_CTRL-D
:help insert-index
:help user-manual
~~~
# 第 7.2 课:补全功能
** 使用 `<C-d>`{normal} 和 `<Tab>`{normal} 进行命令行补全。 **
1. 列出当前目录的内容:`:!{unix:(ls),win:(dir)}`{vim}
2. 输入一个命令的开头:`:e`{vim}
3. 按下 `<C-d>`{normal}Neovim 会显示一个以 "e" 开头的命令列表。
4. 按下 `<Tab>`{normal}Neovim 会显示一个包含可能补全项的菜单
(或者如果输入的命令是唯一的,则直接补全,例如 ":ed`<Tab>`{normal}" 会被补全为 ":edit")。
5. 使用 `<Tab>`{normal} 或 `<C-n>`{normal} 移动到下一个匹配项。或者
使用 `<S-Tab>`{normal} 或 `<C-p>`{normal} 移动到上一个匹配项。
6. 选择 `edit`{vim} 条目。现在你可以看到 `edit`{vim} 这个词已经被自动插入到命令行了。
7. 现在加上一个空格和一个已存在文件名的开头:`:edit FIL`{vim}
8. 按下 `<Tab>`{normal}。Vim 会显示一个补全菜单,列出以 `FIL` 开头的文件名。
NOTE: 补全功能对许多命令都有效。它对 `:help`{vim} 命令尤其有用。
# 第 7.3 课:配置 NVIM
Neovim 是一个高度可配置的编辑器。你可以根据自己的需求进行定制。要开始使用更多功能,
可以创建一个 vimrc 文件,如果你想用 Lua文件名可以是 "init.lua",如果你想用
Vimscript文件名可以是 "init.vim"。在本课中,我们将使用 "init.lua"。
1. 开始编辑 "init.lua" 文件。
`:exe 'edit' stdpath('config')..'/init.lua'`{vim}
2. 将 Lua 的示例配置复制到你的 "init.lua" 文件中。
`:read $VIMRUNTIME/example_init.lua`{vim}
3. 写入文件(这也会创建任何缺失的父目录):
`:w ++p`{vim}
4. 下次你启动 Neovim 时,可以用以下命令快速打开这个 vimrc 文件:
`:e $MYVIMRC`{vim}
# 第 7 课总结
1. 输入 `:help`{vim}
或按下 `<F1>`{normal} 或 `<Help>`{normal} 来打开一个帮助窗口。
2. 输入 `:help 主题`{vim} 来查找关于“主题”的帮助。
3. 输入 `<C-w><C-w>`{normal} 来跳转到另一个窗口。
4. 输入 `:q`{vim} 来关闭帮助窗口。
5. 在命令模式下,按 `<C-d>`{normal} 查看可能的补全。按 `<Tab>`{normal} 使用补全菜单并选择一个匹配项。
6. 创建你的配置文件来保存你的偏好设置。你可以用 `:e $MYVIMRC`{vim} 再次访问它。
# 接下来呢?
运行 `:help nvim-quickstart`{vim} 获取更多关于扩展 Nvim 的信息。
# 结语
这就是 Vim 教程第一章的全部内容。你可以继续学习[第二章](@tutor:vim-02-beginner)。
本教程旨在简要介绍 Neovim 编辑器,内容刚好足够让你能比较轻松地使用它。它远非完
整,因为 Neovim 还有许许多多其他的命令。请经常查阅帮助文档。网上也有无数优秀的
教程和视频可供学习。这里有一些推荐:
- *Learn Vim Progressively*:
https://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/
- *Learning Vim in 2014*:
https://benmccormick.org/learning-vim-in-2014/
- *Vimcasts*:
http://vimcasts.org/
- *Vim Video-Tutorials by Derek Wyatt*:
http://derekwyatt.org/vim/tutorials/
- *Learn Vimscript the Hard Way*:
https://learnvimscriptthehardway.stevelosh.com/
- *7 Habits of Effective Text Editing*:
https://www.moolenaar.net/habits.html
- *vim-galore*:
https://github.com/mhinz/vim-galore
如果你更喜欢书籍Drew Neil 的 *Practical Vim* 经常被推荐
(其续作 *Modern Vim* 包含了 Neovim 的特有内容)。
本教程由 Michael C. Pierce 和 Robert K. Ware科罗拉多矿业大学编写采用了
Charles Smith科罗拉多州立大学提供的想法。电子邮件: bware@mines.colorado.edu。
由 Bram Moolenaar 为 Vim 修改。
由 Felipe Morales 为 vim-tutor-mode 修改。
由 Rory Nesbitt 为 Neovim 修改。
Neovim Tutor 简体中文翻译版由 PilgrimLyieu <pilgrimlyieu@outlook.com> 译制并校对。
变更记录:
- 2025-07-06 PilgrimLyieu <pilgrimlyieu@outlook.com>
译制并校对
// vim: nowrap

View File

@@ -0,0 +1,43 @@
{
"expect": {
"96": "The cow jumped over the moon.",
"97": "The cow jumped over the moon.",
"117": "There is some text missing from this line.",
"118": "There is some text missing from this line.",
"135": "There is some text missing from this line.",
"136": "There is some text missing from this line.",
"137": "There is also some text missing here.",
"138": "There is also some text missing here.",
"204": "There are some words that don't belong in this sentence.",
"205": "There are some words that don't belong in this sentence.",
"221": "Somebody typed the end of this line twice.",
"259": -1,
"276": "This line of words is cleaned up.",
"292": "1) Roses are red,",
"293": "",
"294": "3) Violets are blue,",
"295": "",
"296": "",
"297": "6) Sugar is sweet",
"298": "7) And so are you.",
"318": "Fix the errors on this line and replace them with undo.",
"319": "Fix the errors on this line and replace them with undo.",
"384": "When this line was typed in, someone pressed some wrong keys!",
"385": "When this line was typed in, someone pressed some wrong keys!",
"405": "This line has a few words that need changing using the change operator.",
"406": "This line has a few words that need changing using the change operator.",
"426": "The end of this line needs to be corrected using the c$ command.",
"427": "The end of this line needs to be corrected using the c$ command.",
"484": -1,
"502": -1,
"524": "Usually the best time to see the flowers is in the spring.",
"702": -1,
"707": -1,
"723": "This line will allow you to practice appending text to a line.",
"724": "This line will allow you to practice appending text to a line.",
"740": "Adding 123 to 456 gives you 579.",
"741": "Adding 123 to 456 gives you 579.",
"765": "a) This is the first item.",
"766": "b) This is the second item."
}
}

View File

@@ -0,0 +1,189 @@
# 欢迎来到 Neovim 教程
# 第 2 章
此处有龙(拉丁语 Hic Sunt Dracones表示有危险如果这是您第一次接触 vim
且您希望从入门章节开始,请在 Vim 编辑器的命令行中输入:
~~~ cmd
:Tutor vim-01-beginner
~~~
或者直接点击链接打开教程的[第一章](@tutor:vim-01-beginner)。
完成本章大约需要 8-10 分钟,具体取决于您在实践探索上花费的时间。
# 第 2.1.1 课:命名寄存器
** 同时复制两个单词,然后分别粘贴它们 **
1. 将光标移动到下面标有 ✓ 的那一行。
2. 导航到 'Edward' 单词的任意位置,然后输入 `"ayiw`{normal}
**助记***将 (i)nner (w)ord内部单词(y)ank复制到名为 (a) 的寄存器(")中*
3. 向前导航到 'cookie' 单词(可以使用 `fk`{normal} 或 `2fc`{normal}
或 `$2b`{normal} 或 `/co`{normal} `<Enter>`{normal}),然后输入 `"byiw`{normal}
4. 导航到 'Vince' 单词的任意位置,然后输入 `ciw<CTRL-r>a<ESC>`{normal}
**助记***用名为 (a) 的寄存器(<contents of (r)egister>)的内容 (c)hange (i)
nner (w)ord修改内部单词*
5. 导航到 'cake' 单词的任意位置,然后输入 `ciw<CTRL-r>b<ESC>`{normal}
a) Edward will henceforth be in charge of the cookie rations
b) In this capacity, Vince will have sole cake discretionary powers
NOTE: 删除操作同样可以存入寄存器,例如 `"sdiw`{normal} 会将被光标下的单词删除并存入寄存器 s。
参考:[寄存器](registers)
[命名寄存器](quotea)
[移动与文本对象](text-objects)
[CTRL-R](i_CTRL-R)
# 第 2.1.2 课:表达式寄存器
** 即时插入计算结果 **
1. 将光标移动到下面标有 ✗ 的那一行。
2. 导航到所给数字的任意位置。
3. 输入 `ciw<CTRL-r>=`{normal}60\*60\*24 `<Enter>`{normal}
4. 在下一行,进入插入模式,并使用
`<CTRL-r>=`{normal}`system('date')`{vim} `<Enter>`{normal} 来添加今天的日期。
NOTE: 所有对 `system` 的调用都依赖于操作系统,例如在 Windows 上应使用
`system('date /t')`{vim} 或 `:r!date /t`{vim}
I have forgotten the exact number of seconds in a day, is it 84600?
Today's date is:
NOTE: 同样效果也可以通过 `:pu=`{normal}`system('date')`{vim} 实现,
或者用更少的按键 `:r!date`{vim}
参考:[表达式寄存器](quote=)
# 第 2.1.3 课:数字寄存器
** 按下 `yy`{normal} 和 `dd`{normal} 来观察它们对寄存器的影响 **
1. 将光标移动到下面标有 ✓ 的那一行。
2. 复制yank第 0 行,然后用 `:reg`{vim} `<Enter>`{normal} 查看寄存器。
3. 用 `"cdd`{normal} 删除第 0 行,然后再次查看寄存器。
(你觉得第 0 行的内容会出现在哪里?)
4. 继续删除后续的每一行,并在每次删除后用 `:reg`{vim} 查看寄存器。
NOTE: 你应该会发现,当新的整行删除内容被添加进来时,之前删除的内容会在寄存器列
表中依次下移。
5. 现在,按顺序 (p)aste粘贴以下寄存器中的内容c, 7, 4, 8, 2。例如使用 `"7p`{normal}
0. This
9. wobble
8. secret
7. is
6. on
5. axis
4. a
3. war
2. message
1. tribute
NOTE: 在数字寄存器中,整行删除(`dd`{normal})的内容比整行复制或涉及更小范围移动
的删除操作“存活”得更久。
参考:[数字寄存器](quote0)
# 第 2.1.4 课:标记之美
** 避免“码农式”的行号计算 **
NOTE: 在写代码时,一个常见的难题是移动大块的代码。
下面的技巧可以帮助你避免进行行号计算,比如 `"a147d`{normal} 或 `:945,1091d a`{vim}
甚至是更麻烦的先用 `i<CTRL-r>=`{normal}1091-945 `<Enter>`{normal} 计算行数。
1. 将光标移动到下面标有 ✓ 的那一行。
2. 跳转到函数的第一行,并用 `ma`{normal} 将其标记为 a。
NOTE: 光标在该行的确切位置并不重要!
3. 使用 `$%`{normal} 导航到行尾,然后再到代码块的末尾。
4. 使用 `"ad'a`{normal} 将该代码块删除并存入寄存器 a。
**助记***将从光标位置到包含标记('(a) 的那一行的内容 (d)elete删除到名为 (a) 的寄存器(")中*
5. 在 BBB 和 CCC 之间用 `"ap`{normal} 粘贴该代码块。
NOTE: 多次练习这个操作以达到熟练:`ma$%"ad'a`{normal}
~~~ cmd
AAA
function itGotRealBigRealFast() {
if ( somethingIsTrue ) {
doIt()
}
// the taxonomy of our function has changed and it
// no longer makes alphabetical sense in its current position
// imagine hundreds of lines of code
// naively you could navigate to the start and end and record or
// remember each line number
}
BBB
CCC
~~~
NOTE: 标记和寄存器不共享命名空间,因此寄存器 a 和标记 a 是完全独立的。
但寄存器和宏并非如此。
参考:[标记](marks)
[标记移动](mark-motions)' 和 \` 的区别)
# 第 2.1 课总结
1. 将文本 存储(复制、删除)到 26 个寄存器a-z并从中提取粘贴出来。
2. 从单词内的任意位置复制整个单词:`yiw`{normal}
3. 从单词内的任意位置更改整个单词:`ciw`{normal}
4. 在插入模式下直接从寄存器插入文本:`<CTRL-r>a`{normal}
5. 插入简单算术运算的结果:
在插入模式下使用 `<CTRL-r>=`{normal}60\*60 `<Enter>`{normal}
6. 插入系统调用的结果:
在插入模式下使用 `<CTRL-r>=`{normal}`system('ls -1')`{vim}
7. 使用 `:reg`{vim} 查看寄存器。
8. 了解整行删除 (`dd`{normal}) 的最终去向:在数字寄存器中,即从寄存器 1 到 9
依次向下存放。要理解整行删除的内容在数字寄存器中比任何其他操作都保存得更久。
9. 了解所有复制操作在数字寄存器中的最终去向,以及它们是多么“短暂易逝”。
10. 在普通模式下设置标记:`m[a-zA-Z0-9]`{normal}
11. 按行移动到标记位置:`'`{normal}
# 结语
Neovim 教程第二章到此结束。本教程仍在不断完善中。
本章由 Paul D. Parker 编写。
由 Restorer 为 vim-tutor-mode 修改。
简体中文翻译版由 PilgrimLyieu <pilgrimlyieu@outlook.com> 译制并校对。
变更记录:
- 2025-07-07 PilgrimLyieu <pilgrimlyieu@outlook.com>
译制并校对

View File

@@ -0,0 +1,10 @@
{
"expect": {
"35": -1,
"36": "b) In this capacity, Edward will have sole cookie discretionary powers",
"62": "I have forgotten the exact number of seconds in a day, is it 86400",
"63": -1,
"89": -1,
"132": -1
}
}

View File

@@ -293,6 +293,10 @@ preprocess_patch() {
LC_ALL=C sed -Ee 's/( [ab]\/src\/nvim)\/option\.h/\1\/option_vars.h/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename runtime/doc/eval.txt to runtime/doc/vimeval.txt
LC_ALL=C sed -Ee 's/( [ab]\/runtime\/doc)\/eval\.txt/\1\/vimeval.txt/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename version*.txt to news.txt
LC_ALL=C sed -Ee 's/( [ab]\/runtime\/doc)\/version[0-9]+\.txt/\1\/news.txt/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"

View File

@@ -82,7 +82,7 @@
#define DEFAULT_ENCODE_INVALID_NUMBERS 0
#define DEFAULT_DECODE_INVALID_NUMBERS 1
#define DEFAULT_ENCODE_KEEP_BUFFER 1
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
#define DEFAULT_ENCODE_NUMBER_PRECISION 16
#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 0
#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1

View File

@@ -359,11 +359,11 @@ local function norm_text(x, special)
x = x:gsub([=[%|?(nvim_[^.()| ]+)%(?%)?%|?]=], 'vim.api.%1')
-- TODO: Remove backticks when LuaLS resolves: https://github.com/LuaLS/lua-language-server/issues/2889
-- "|foo|" => "`:help foo`"
x = x:gsub([=[|([^ ]+)|]=], '`:help %1`')
x = x:gsub([=[|([^%s|]+)|]=], '`:help %1`')
end
return (
x:gsub('|([^ ]+)|', '`%1`')
x:gsub('|([^%s|]+)|', '`%1`')
:gsub('\n*>lua', '\n\n```lua')
:gsub('\n*>vim', '\n\n```vim')
:gsub('\n+<$', '\n```')
@@ -949,17 +949,17 @@ local CONFIG = {
render = render_api_keyset_meta,
},
{
path = 'runtime/doc/builtin.txt',
path = 'runtime/doc/vimfn.txt',
funcs = get_eval_meta,
render = render_eval_doc,
header = {
'*builtin.txt* Nvim',
'*vimfn.txt* Nvim',
'',
'',
'\t\t NVIM REFERENCE MANUAL',
'',
'',
'Builtin functions\t\t*vimscript-functions* *builtin-functions*',
'Vimscript functions\t*vimscript-functions* *builtin-functions* *builtin.txt*',
'',
'For functions grouped by what they are used for see |function-list|.',
'',

View File

@@ -92,7 +92,7 @@ local redirects = {
-- TODO: These known invalid |links| require an update to the relevant docs.
local exclude_invalid = {
["'string'"] = 'eval.txt',
["'string'"] = 'vimeval.txt',
Query = 'treesitter.txt',
matchit = 'vim_diff.txt',
['set!'] = 'treesitter.txt',

View File

@@ -742,7 +742,11 @@ endif()
target_sources(nlua0 PUBLIC ${NLUA0_SOURCES})
if(STATIC_BUILD)
target_link_options(nvim_bin PRIVATE -static-libgcc -static-libstdc++ -static)
endif()
target_link_libraries(nvim_bin PRIVATE main_lib PUBLIC libuv)
install_helper(TARGETS nvim_bin)
if(MSVC)
install(FILES $<TARGET_PDB_FILE:nvim_bin> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
@@ -975,7 +979,7 @@ add_target(doc-eval
${PROJECT_SOURCE_DIR}/src/nvim/eval.lua
${PROJECT_SOURCE_DIR}/src/nvim/options.lua
${PROJECT_SOURCE_DIR}/src/nvim/vvars.lua
${NVIM_RUNTIME_DIR}/doc/builtin.txt
${NVIM_RUNTIME_DIR}/doc/vimfn.txt
)
add_custom_target(doc)

View File

@@ -636,6 +636,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
garray_T capture_local;
const int save_msg_silent = msg_silent;
const bool save_redir_off = redir_off;
garray_T * const save_capture_ga = capture_ga;
const int save_msg_col = msg_col;
@@ -647,6 +648,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
TRY_WRAP(err, {
if (opts->output) {
msg_silent++;
redir_off = false;
msg_col = 0; // prevent leading spaces
}
@@ -657,6 +659,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
if (opts->output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
redir_off = save_redir_off;
// Put msg_col back where it was, since nothing should have been written.
msg_col = save_msg_col;
}
@@ -1073,6 +1076,7 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
goto err;
});
argt |= EX_RANGE;
if (addr_type_arg != ADDR_LINES) {
argt |= EX_ZEROR;
}

View File

@@ -62,7 +62,7 @@ Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(7)
FUNC_API_REMOTE_ONLY
{
return nlua_exec(code, args, kRetObject, arena, err);
return nlua_exec(code, NULL, args, kRetObject, arena, err);
}
/// Gets the buffer number

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