Compare commits

...

46 Commits

Author SHA1 Message Date
5b839ced69 NVIM 0.6.1 (2021-12-31)
Bug Fixes

  * api: allow nvim_buf_set_extmark to accept end_row key #16686 1b54344
  * diagnostic: assert that diagnostics have line number and column #16687 9dae939
  * diagnostic: clamp diagnostics on negative line numbers #16497 096f841
  * diagnostic: escape special chars in file names #16588 beac24d
  * diagnostic: respect "if_many" source option for virtual text #16697 060eeaa
  * diagnostic: set effective buffer number for DiagnosticChanged autocmd #16485 84784a8
  * diagnostic: set effective buffer number in autocmd (again) #16590 08ddfa9, closes #16474
  * lua: do not cast offset to char_u 93f1ec0
  * lsp: avoid attaching to unloaded buffers #16726 0088994
  * lsp: call config on_exit handler before context is cleared #16781 571609f
  * lsp: fix `nil`-index behavior for UTF-8 in `_str_*index_enc` methods #16785 03bd914
  * lsp: handle offset encoding #16783 7b60ec7
  * lsp: progress handlers should return vim.NIL on error #16476 fb11ef0
  * options: disallow empty 'fdc' and 'scl' #16776 37a00be
  * quickfix: avoid O(N^2) when filling from string typval #16663 aa0ddc6
  * screenpos, float: add top and left border adjustment 8f68548
  * terminal: fix resize crash with pending scrollback #16665 ae249d8
  * ui: close floating window on BufLeave event #16664 785bace
  * uri: change scheme pattern to not include the comma character #16798 0e96f7d

Features

  * lsp,diagnostic: open folds in jump-related functions #16784 ee9e342
  * lsp: add buf_detach_client #16741 ec101b9
  * lsp: use `vim.ui.select` for selecting lsp client #16782 14357c8
  * runtime: new checkhealth filetype #16708 09306f0
2021-12-31 18:02:21 +01:00
844872cc0a chore: fix typos in doc and Lua files
Co-authored-by: Gregory Anders <8965202+gpanders@users.noreply.github.com>
Co-authored-by: Evgeni Chasnovski <evgeni.chasnovski@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Co-authored-by: Christoph Hasse <hassec@users.noreply.github.com>
Co-authored-by: Alef Pereira <ealefpereira@gmail.com>
Co-authored-by: AusCyber <willp@outlook.com.au>
Co-authored-by: kylo252 <59826753+kylo252@users.noreply.github.com>
2021-12-29 17:02:46 +01:00
0e96f7d04c fix(uri): change scheme pattern to not include the comma character (#16798)
(cherry picked from commit efbe306d2d)

Co-authored-by: Dmytro Meleshko <dmytro.meleshko@gmail.com>
2021-12-26 16:39:03 -05:00
1cc63abf4a Merge pull request #16667 from neovim/backport-16420-to-release-0.6
[Backport release-0.6] vim-patch:8.1.2136,8.2.2465
2021-12-26 18:36:04 +01:00
9f51da3442 Merge pull request #16679 from neovim/backport-16134-to-release-0.6
[Backport release-0.6] fix(screenpos, float): add top and left border adjustment
2021-12-26 18:35:25 +01:00
857678c2ec refactor(lsp): remove usage of deprecated function (#16787)
(cherry picked from commit bb31d3f21a)

Co-authored-by: Gregory Anders <greg@gpanders.com>
2021-12-25 11:42:29 -05:00
03bd9147f0 fix(lsp): fix nil-index behavior for UTF-8 in _str_*index_enc methods (#16785)
Previously, the `_str_utfindex_enc` and `_str_byteindex_enc` helper functions would return `nil` when `offset_encoding == "utf-8"` and `index == nil`. Clearly, this doesn't reflect the expected behavior of the functions they're wrapping which would return the length of the line in this case. This should fix behavior with servers that use UTF-8 `offset_encoding` when applying text edits, formatting a range, and doing range code actions (though this isn't tested currently).

(cherry picked from commit 5f4c501cf1)

Co-authored-by: Rishikesh Vaishnav <rishhvaishnav@gmail.com>
2021-12-25 10:52:45 -05:00
ee9e3420fd feat(lsp,diagnostic): open folds in jump-related functions (#16784)
(cherry picked from commit ddf545c152)

Co-authored-by: Dmytro Meleshko <dmytro.meleshko@gmail.com>
2021-12-25 10:43:28 -05:00
7b60ec79ea fix(lsp): handle offset encoding (#16783)
Co-authored-by: black-desk <clx814727823@gmail.com>
Co-authored-by: Mathias Fußenegger <mfussenegger@users.noreply.github.com>
(cherry picked from commit 4687d853a5)

Co-authored-by: Rishikesh Vaishnav <rishhvaishnav@gmail.com>
2021-12-25 10:27:55 -05:00
14357c83c5 feat(lsp): use vim.ui.select for selecting lsp client (#16782)
(cherry picked from commit 43cdcba476)

Co-authored-by: Anshuman <amedhi@connect.ust.hk>
2021-12-25 10:17:57 -05:00
571609fb89 fix(lsp): call config on_exit handler before context is cleared (#16781)
The on_exit handler provided to the client configuration is called after
the client's context is cleared (e.g. which buffers the client was
attached to). Calling the handler sooner allows these handlers to access
the client object and do their own cleanup with the full context.

(cherry picked from commit 5ab49430ee)

Co-authored-by: Gregory Anders <greg@gpanders.com>
2021-12-25 10:10:18 -05:00
37a00be7c0 fix(options): disallow empty 'fdc' and 'scl' (#16776)
Empty string values for these options aren't actually allowed, but
check_opt_strings allows empty string options.

It so happens that 'scl' handles empty string like "auto", but empty 'fdc'
causes glitchiness (win_fdccol_count returns an incorrect value).

Just disallow empty string values for these options completely.
2021-12-24 22:38:15 -07:00
09306f07c4 feat(runtime): new checkhealth filetype (#16708) 2021-12-23 08:38:56 -07:00
ec101b9fd9 feat(lsp): add buf_detach_client (#16741)
This allows the user to detach an active buffer from the language
client. If no clients remain attached to a buffer, the on_lines callback
is used to cancel nvim_buf_attach.

(cherry picked from commit 6db2155032)

Co-authored-by: Michael Lingelbach <m.j.lbach@gmail.com>
2021-12-21 11:12:58 -08:00
222d1414dc fix(lsp): correctly align start and end range to codepoints during incremental sync (#16670)
Closes #16624

Fixes two issues with aligning the start position and end position to
codepoints when calculating the start and end range.

When aligning the start position:
* use aligned byte index to calculate character index rather than 
  the unadjusted byte

When aligning the end position:
* do not adjust the end byte if it falls on a UTF-8 codepoint
* align byte to the first byte of the next codepoint rather than the
  last byte of the current codepoint
* compute character character end range on the aligned byte index

This commit also adds additional test coverage, including multibyte operations
that previously failed before this commit.
2021-12-21 09:24:50 -08:00
7f4fa077cd fix: do not cast offset to char_u (#16672)
[Backport release-0.6] fix: do not cast offset to char_u
2021-12-19 13:26:59 -08:00
00889948dd fix(lsp): avoid attaching to unloaded buffers (#16726)
Closes https://github.com/neovim/neovim/issues/16562 https://github.com/neovim/neovim/issues/16249 https://github.com/neovim/neovim/issues/16297

* buf_attach_client can be called on an unloaded buffer
* on_attach will prematurely fail, while the language server client
  tracks this buffer as attached
* The language server client will track this buffer as attached despite
  textDocument/didChange notifications not being sent to the server
* Instead, check if the buffer is loaded and return early, warning via
  the lsp logger that buf_attach_client was called on an invalid buffer

(cherry picked from commit 6d63cb8f6a)

Co-authored-by: Michael Lingelbach <m.j.lbach@gmail.com>
2021-12-19 13:24:08 -08:00
1bb861c7a1 Merge pull request #16713 from neovim/backport-16707-to-release-0.6
[Backport release-0.6] test: allow excluding functional/unti tests using TEST_FILTER_OUT
2021-12-18 13:39:00 -05:00
d547d874af test: allow excluding functional/unit tests using TEST_FILTER_OUT
Although this can already be done using `BUSTED_ARGS`, it complements
our existing shortcut of `TEST_FILTER.`

[skip ci]

(cherry picked from commit 267ec67fab)
2021-12-18 18:16:03 +00:00
060eeaa14c fix(diagnostic): respect "if_many" source option for virtual text (#16697)
The `prefix_source` function only evaluates the sources from the
diagnostics passed to it; however, because each namespace draws its own
virtual text, its diagnostics will never contain more than a single
source (by definition). This requires changing the semantics of what
"if_many" means from "multiple sources in a single 'batch' of
diagnostics" to "multiple sources of all diagnostics within a buffer".
2021-12-17 19:44:16 -07:00
9dae939b1f fix(diagnostic): assert that diagnostics have line number and column (#16687)
Line number and column are required and much of the diagnostic API
assumes that these are both present. When one of the two is missing,
cryptic errors pop up in other parts of the diagnostic subsystem.
Instead, assert that diagnostics are well formed when they are entered
into the cache, which provides a clearer error.
2021-12-17 12:55:39 -07:00
8c6a4fab66 refactor(diagnostic): remove hack (#16689)
No longer required since #16548.
2021-12-16 12:18:45 -07:00
1b54344c11 fix(api): allow nvim_buf_set_extmark to accept end_row key (#16686)
nvim_buf_get_extmark uses "end_row" rather than "end_line" in its
'details' dict, which means callers must modify the key names if they
want to re-use the information. Allow nvim_buf_set_extmark to take
"end_row" as an alias to "end_line" to make this more compatible.

See [1].

[1]: https://github.com/neovim/neovim/pull/15011#discussion_r665336968
2021-12-16 11:05:58 -07:00
8f68548384 fix(screenpos, float): add top and left border adjustment
(cherry picked from commit ffe3003e02)
2021-12-16 11:42:14 +00:00
25da2430f9 chore: improve naming consistency in str_utf_start
(cherry picked from commit fcbffcd92a)
2021-12-15 21:57:11 +00:00
93f1ec0a59 fix: do not cast offset to char_u
* str_utf_start/end both cast the offset into the utf string
to a char_u, a pointer + long is well-defined and the cast is
unnecessary. This previously resulted in issues for offsets greater than
256.

(cherry picked from commit 1a887293ef)
2021-12-15 21:57:10 +00:00
5c8e5432c0 docs(options): mention how to disable inccommand preview (#16671)
Co-authored-by: Hitarth Thummar <47787284+gtlsgamr@users.noreply.github.com>
2021-12-15 22:43:45 +01:00
e86997a812 vim-patch:8.2.2465: using freed memory in :psearch
Problem:    Using freed memory in :psearch. (houyunsong)
Solution:   Check the current window is still valid.  Fix flaky test.
92bb83e41c

Test_cursorhold_insert timer's 100ms delay was already LoadAdjusted, but change
to 200ms (still LoadAdjust) to match Vim anyway.

(cherry picked from commit dac52e6d04)
2021-12-15 17:27:32 +00:00
a0ebba7052 vim-patch:8.1.2136: using freed memory with autocmd from fuzzer
Problem:    using freed memory with autocmd from fuzzer. (Dhiraj Mishra,
            Dominique Pelle)
Solution:   Avoid using "wp" after autocommands. (closes vim/vim#5041)
ec66c41d84

Nvim doesn't use Vim's terminal implementation.
Despite this, Nvim has its own *exclusive* way of crashing here.

Requires 'winwidth' > winwidth() and 'nowinfixwidth' to crash; adjust
the test ('nowfw' is the default, but ensure its disabled anyway).

(cherry picked from commit c366c944c2)
2021-12-15 17:27:32 +00:00
ae249d81fb fix(terminal): fix resize crash with pending scrollback (#16665)
refresh_scrollback assumes pending scrollback rows exist only if the
terminal window height decreased (or the screen was full).

However, after accumulating scrollback, it's possible in some cases for
the terminal height to increase before refresh_scrollback is called via
invalidation (especially when the terminal buffer isn't initially
displayed in a window before nvim_open_term), which may crash.

As we'll have enough room for some scrollback rows, just append them to
the top of the buffer until it fills the window, then continue with the
previous logic for any remaining scrollback rows if necessary.
2021-12-15 08:29:26 -07:00
785baceaee fix(ui): close floating window on BufLeave event (#16664)
When buffer is visible in two splits simultaneously, BufHidden event is
not triggered, causing the floating window to remain on screen after
switching to another buffer.

Remove BufHidden event from close_events defaults, and close the window
if we changed the buffer to something other than the buffer that spawned
the floating window or the floating window buffer itself.
2021-12-15 08:12:35 -07:00
aa0ddc6690 fix(quickfix): avoid O(N^2) when filling from string typval (#16663)
When filling a quickfix/loclist from a string-typed VimL variable, the
complexity is O(N^2) in the number of lines in the variable.

The problem is caused by using `xstrlcpy(3)` to copy the characters from
the current position up to the next newline into the quickfix/loclist
buffer in a loop.

strlcpy(3) returns the length of `src`, so by necessity it has to
compute `strlen(src)`. This means scanning the full rest of the typval
on every iteration while only copying a small fraction (up to the next
'\n').

This is not a problem whenever the srclen-to-copylen ratio is close to
1, which it usually is. But not in this case. Since we already
calculated exactly how many bytes we want to copy, we should be using
memcpy(3).

This problem is not present in Vim, as it uses `vim_strncpy`, a
`strncpy(3)`-alike, which stops at either `\0` or `n`, whichever comes
first.

The quickfix/loclist window can be filled using a:

  1. File (used by commands like :grep/:make/... to source directly
     from their errorfile)
  2. Buffer (used by :cbuffer and its variants)
  3. Typval
   a. String (used by :cexpr and its variants)
   b. List of strings (used by setqflist(), setloclist(), :cepxr and its
   variants)

This commit optimizes case (3a), especially when the typval is a long
string.

The pathological path is triggered by (e.g.) :grep enhancements as found
in https://gist.github.com/romainl/56f0c28ef953ffc157f36cc495947ab3:

    function! Grep(...)
        return system(join([&grepprg] + a:000), ' '))
    endfunction
    :cgetexpr Grep('foo')

It would've been better for Neovim to use `systemlist` here, before this
commit.
2021-12-15 08:08:53 -07:00
2cd272decb refactor: replace deprecated lua functions with their new versions (#16609)
Calling vim.lsp.buf.definition() sometimes gives a deprecation warning.
This will likely solve that.

Co-authored-by: Christian Clason <christian.clason@uni-due.de>
2021-12-10 13:24:49 -07:00
08ddfa9851 fix(diagnostic): set effective buffer number in autocmd (again) (#16590)
Follow up to #16474.
2021-12-08 21:15:04 -07:00
beac24d6f3 fix(diagnostic): escape special chars in file names (#16588) 2021-12-08 19:20:55 -07:00
5dcf2c77a9 refactor(diagnostic): remove bufnr parameter from open_float (#16587)
The overwhelming majority of use cases for `open_float` are to view
diagnostics from the current buffer in a floating window. Thus, most use
cases will just `0` or `nil` as the first argument, which makes the
argument effectively useless and wasteful.

In the cause of optimizing for the primary use case, make the `bufnr`
parameter an optional parameter in the options table. This still allows
using an alternative buffer for those that wish to do so, but makes the
"primary" use case much easier.

The old signature is preserved for backward compatibility, though it can
likely be fully deprecated at some point.
2021-12-08 18:46:30 -07:00
ce4c8010cc docs(lsp): fix resolve_capabilities docstring (#16580) 2021-12-08 11:02:14 -07:00
270f71b571 docs: update missing help sections from main help page (#16578)
Also replace non-existent help section remote.txt to remote_plugins.txt
2021-12-08 10:00:25 -07:00
3c0f239c3e ci: install doxygen with conda instead of apt (#16576)
This provides a newer version of Doxygen (1.9.0 or later)
that fixes a bug in the documentation generator.

Closes #16498
2021-12-08 09:30:16 -07:00
1d32521135 refactor(diagnostic): add warning to deprecated functions (#16575) 2021-12-08 09:15:24 -07:00
bd057d4b40 docs(lsp): re-add client.requests documentation (#16533)
Added in this PR: d1c470957b (diff-6b5f3071d65558aab177912061ac6a2f5312660655a449276c83697686f28e72R627)

Removed by regeneration in this PR: 2d340a3746 (diff-6b5f3071d65558aab177912061ac6a2f5312660655a449276c83697686f28e72L631)

(cherry picked from commit ae9c9b817d)

Co-authored-by: Anshuman <amedhi@connect.ust.hk>
2021-12-05 14:37:08 +01:00
096f8418c5 fix(diagnostic): clamp diagnostics on negative line numbers (#16497)
Closes https://github.com/neovim/neovim/issues/16492

Despite having logic for setting the maximum diagnostic line
number to at minimum 0, previously the conditional statement only
checked if lnum and end_lnum were greater than the line count.

Fix: also check if lnum and end_lnum are less than 0.

(cherry picked from commit 2799463ba2)

Co-authored-by: Michael Lingelbach <m.j.lbach@gmail.com>
2021-12-02 07:02:30 -08:00
84784a8391 fix(diagnostic): set effective buffer number for DiagnosticChanged autocmd (#16485)
This enables use of <abuf> in autocommand handlers for
DiagnosticChanged.
2021-12-01 07:35:46 -07:00
fb11ef0aad fix(lsp): progress handlers should return vim.NIL on error (#16476) 2021-12-01 07:17:15 -05:00
faf3159ab0 Merge pull request #16479 from jamessan/fragile-server-notifications-test
[Backport release-0.6] test(api): mark "notify cancels stale events on channel close" fragile
2021-11-30 22:41:15 -05:00
a4699892af version bump 2021-11-30 18:40:33 +01:00
49 changed files with 1242 additions and 526 deletions

View File

@ -26,7 +26,9 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack luajit
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y python3 luajit
conda install -c conda-forge doxygen=1.9.2 msgpack-python
echo "$CONDA/bin" >> $GITHUB_PATH
- name: Setup git config
run: |

View File

@ -137,7 +137,7 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
# version string, else they are combined with the result of `git describe`.
set(NVIM_VERSION_MAJOR 0)
set(NVIM_VERSION_MINOR 6)
set(NVIM_VERSION_PATCH 0)
set(NVIM_VERSION_PATCH 1)
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
# API level

View File

@ -46,6 +46,10 @@ if(DEFINED ENV{TEST_FILTER} AND NOT "$ENV{TEST_FILTER}" STREQUAL "")
list(APPEND BUSTED_ARGS --filter $ENV{TEST_FILTER})
endif()
if(DEFINED ENV{TEST_FILTER_OUT} AND NOT "$ENV{TEST_FILTER_OUT}" STREQUAL "")
list(APPEND BUSTED_ARGS --filter-out $ENV{TEST_FILTER_OUT})
endif()
# TMPDIR: use relative test path (for parallel test runs / isolation).
set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir/${TEST_PATH}")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR})

View File

@ -1,27 +1,3 @@
function! s:enhance_syntax() abort
syntax case match
syntax keyword healthError ERROR[:]
\ containedin=markdownCodeBlock,mkdListItemLine
highlight default link healthError Error
syntax keyword healthWarning WARNING[:]
\ containedin=markdownCodeBlock,mkdListItemLine
highlight default link healthWarning WarningMsg
syntax keyword healthSuccess OK[:]
\ containedin=markdownCodeBlock,mkdListItemLine
highlight default healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232
syntax match healthHelp "|.\{-}|" contains=healthBar
\ containedin=markdownCodeBlock,mkdListItemLine
syntax match healthBar "|" contained conceal
highlight default link healthHelp Identifier
" We do not care about markdown syntax errors in :checkhealth output.
highlight! link markdownError Normal
endfunction
" Runs the specified healthchecks.
" Runs all discovered healthchecks if a:plugin_names is empty.
function! health#check(plugin_names) abort
@ -29,13 +5,9 @@ function! health#check(plugin_names) abort
\ ? s:discover_healthchecks()
\ : s:get_healthcheck(a:plugin_names)
tabnew
setlocal wrap breakindent linebreak
setlocal filetype=markdown
setlocal conceallevel=2 concealcursor=nc
setlocal keywordprg=:help
let &l:iskeyword='!-~,^*,^|,^",192-255'
call s:enhance_syntax()
" create scratch-buffer
execute 'tab sbuffer' nvim_create_buf(v:true, v:true)
setfiletype checkhealth
if empty(healthchecks)
call setline(1, 'ERROR: No healthchecks found.')
@ -70,8 +42,6 @@ function! health#check(plugin_names) abort
" needed for plasticboy/vim-markdown, because it uses fdm=expr
normal! zR
setlocal nomodified
setlocal bufhidden=hide
redraw|echo ''
endfunction

View File

@ -523,7 +523,7 @@ function! s:check_virtualenv() abort
let hint = '$PATH ambiguities in subshells typically are '
\.'caused by your shell config overriding the $PATH previously set by the '
\.'virtualenv. Either prevent them from doing so, or use this workaround: '
\.'https://vi.stackexchange.com/a/7654'
\.'https://vi.stackexchange.com/a/34996'
let hints[hint] = v:true
endif
endfor

View File

@ -361,7 +361,7 @@ UTF-32 and UTF-16 sizes of the deleted region is also passed as additional
arguments {old_utf32_size} and {old_utf16_size}.
"on_changedtick" is invoked when |b:changedtick| was incremented but no text
was changed. The parameters recieved are ("changedtick", {buf}, {changedtick}).
was changed. The parameters received are ("changedtick", {buf}, {changedtick}).
*api-lua-detach*
In-process Lua callbacks can detach by returning `true`. This will detach all
@ -634,7 +634,7 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
atomically, i.e. without interleaving redraws, RPC requests
from other clients, or user interactions (however API
methods may trigger autocommands or event processing which
have such side-effects, e.g. |:sleep| may wake timers).
have such side effects, e.g. |:sleep| may wake timers).
2. To minimize RPC overhead (roundtrips) of a sequence of many
requests.
@ -698,7 +698,7 @@ nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()*
|nvim_set_keymap()|
nvim_del_mark({name}) *nvim_del_mark()*
Deletes a uppercase/file named mark. See |mark-motions|.
Deletes an uppercase/file named mark. See |mark-motions|.
Note:
fails with error if a lowercase or buffer local named mark
@ -2353,7 +2353,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
|api-indexing|
{opts} Optional parameters.
• id : id of the extmark to edit.
• end_line : ending line of the mark, 0-based
• end_row : ending line of the mark, 0-based
inclusive.
• end_col : ending col of the mark, 0-based
exclusive.
@ -2655,7 +2655,7 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()*
true if the window is valid, false otherwise
nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()*
Sets the current buffer in a window, without side-effects
Sets the current buffer in a window, without side effects
Attributes: ~
not allowed when |textlock| is active

View File

@ -130,7 +130,7 @@ with |vim.notify()|: >
In this example, there is nothing to do when diagnostics are hidden, so we
omit the "hide" function.
Existing handlers can be overriden. For example, use the following to only
Existing handlers can be overridden. For example, use the following to only
show a sign for the highest severity diagnostic on a given line: >
-- Create a custom namespace. This will aggregate signs from all other
@ -175,8 +175,9 @@ All highlights defined for diagnostics begin with `Diagnostic` followed by
the type of highlight (e.g., `Sign`, `Underline`, etc.) and the severity (e.g.
`Error`, `Warn`, etc.)
Sign, underline and virtual text highlights (by default) are linked to their
corresponding default highlight.
By default, highlights for signs, floating windows, and virtual text are linked to the
corresponding default highlight. Underline highlights are not linked and use their
own default highlight groups.
For example, the default highlighting for |hl-DiagnosticSignError| is linked
to |hl-DiagnosticError|. To change the default (and therefore the linked
@ -296,7 +297,6 @@ Example: >
autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({open = false })
<
==============================================================================
==============================================================================
Lua module: vim.diagnostic *diagnostic-api*
config({opts}, {namespace}) *vim.diagnostic.config()*
@ -341,13 +341,24 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|diagnostic-severity|
• virtual_text: (default true) Use virtual
text for diagnostics. Options:
text for diagnostics. If multiple
diagnostics are set for a namespace, one
prefix per diagnostic + the last diagnostic
message are shown. Options:
• severity: Only show virtual text for
diagnostics matching the given severity
|diagnostic-severity|
• source: (string) Include the diagnostic
source in virtual text. One of "always"
or "if_many".
• source: (boolean or string) Include the
diagnostic source in virtual text. Use
"if_many" to only show sources if there
is more than one diagnostic source in the
buffer. Otherwise, any truthy value means
to always show the diagnostic source.
• spacing: (number) Amount of empty spaces
inserted at the beginning of the virtual
text.
• prefix: (string) Prepend diagnostic
message with prefix.
• format: (function) A function that takes
a diagnostic as input and returns a
string. The return value is the text used
@ -574,61 +585,67 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
diagnostic |diagnostic-structure| or `nil` if {pat} fails
to match {str}.
open_float({bufnr}, {opts}) *vim.diagnostic.open_float()*
open_float({opts}, {...}) *vim.diagnostic.open_float()*
Show diagnostics in a floating window.
Parameters: ~
{bufnr} number|nil Buffer number. Defaults to the current
buffer.
{opts} table|nil Configuration table with the same keys
as |vim.lsp.util.open_floating_preview()| in
addition to the following:
• namespace: (number) Limit diagnostics to the
given namespace
• scope: (string, default "line") Show
diagnostics from the whole buffer ("buffer"),
the current cursor line ("line"), or the
current cursor position ("cursor").
pos: (number or table) If {scope} is "line" or
"cursor", use this position rather than the
cursor position. If a number, interpreted as a
line number; otherwise, a (row, col) tuple.
• severity_sort: (default false) Sort diagnostics
by severity. Overrides the setting from
|vim.diagnostic.config()|.
• severity: See |diagnostic-severity|. Overrides
the setting from |vim.diagnostic.config()|.
• header: (string or table) String to use as the
header for the floating window. If a table, it
is interpreted as a [text, hl_group] tuple.
Overrides the setting from
|vim.diagnostic.config()|.
• source: (string) Include the diagnostic source
in the message. One of "always" or "if_many".
Overrides the setting from
|vim.diagnostic.config()|.
• format: (function) A function that takes a
diagnostic as input and returns a string. The
return value is the text used to display the
diagnostic. Overrides the setting from
|vim.diagnostic.config()|.
• prefix: (function, string, or table) Prefix
each diagnostic in the floating window. If a
function, it must have the signature
(diagnostic, i, total) -> (string, string),
where {i} is the index of the diagnostic being
evaluated and {total} is the total number of
diagnostics displayed in the window. The
function should return a string which is
prepended to each diagnostic in the window as
well as an (optional) highlight group which
will be used to highlight the prefix. If
{prefix} is a table, it is interpreted as a
[text, hl_group] tuple as in |nvim_echo()|;
otherwise, if {prefix} is a string, it is
prepended to each diagnostic in the window with
no highlight. Overrides the setting from
|vim.diagnostic.config()|.
{opts} table|nil Configuration table with the same keys
as |vim.lsp.util.open_floating_preview()| in
addition to the following:
• bufnr: (number) Buffer number to show
diagnostics from. Defaults to the current
buffer.
namespace: (number) Limit diagnostics to the
given namespace
• scope: (string, default "line") Show diagnostics
from the whole buffer ("buffer"), the current
cursor line ("line"), or the current cursor
position ("cursor"). Shorthand versions are also
accepted ("c" for "cursor", "l" for "line", "b"
for "buffer").
• pos: (number or table) If {scope} is "line" or
"cursor", use this position rather than the
cursor position. If a number, interpreted as a
line number; otherwise, a (row, col) tuple.
• severity_sort: (default false) Sort diagnostics
by severity. Overrides the setting from
|vim.diagnostic.config()|.
• severity: See |diagnostic-severity|. Overrides
the setting from |vim.diagnostic.config()|.
• header: (string or table) String to use as the
header for the floating window. If a table, it
is interpreted as a [text, hl_group] tuple.
Overrides the setting from
|vim.diagnostic.config()|.
• source: (boolean or string) Include the
diagnostic source in the message. Use "if_many"
to only show sources if there is more than one
source of diagnostics in the buffer. Otherwise,
any truthy value means to always show the
diagnostic source. Overrides the setting from
|vim.diagnostic.config()|.
• format: (function) A function that takes a
diagnostic as input and returns a string. The
return value is the text used to display the
diagnostic. Overrides the setting from
|vim.diagnostic.config()|.
• prefix: (function, string, or table) Prefix each
diagnostic in the floating window. If a
function, it must have the signature
(diagnostic, i, total) -> (string, string),
where {i} is the index of the diagnostic being
evaluated and {total} is the total number of
diagnostics displayed in the window. The
function should return a string which is
prepended to each diagnostic in the window as
well as an (optional) highlight group which will
be used to highlight the prefix. If {prefix} is
a table, it is interpreted as a [text, hl_group]
tuple as in |nvim_echo()|; otherwise, if
{prefix} is a string, it is prepended to each
diagnostic in the window with no highlight.
Overrides the setting from
|vim.diagnostic.config()|.
Return: ~
tuple ({float_bufnr}, {win_id})

View File

@ -2259,7 +2259,7 @@ USAGE RESULT DESCRIPTION ~
abs({expr}) Float or Number absolute value of {expr}
acos({expr}) Float arc cosine of {expr}
add({object}, {item}) List/Blob append {item} to {object}
and({expr}, {expr}) Number bitwise AND
and({expr}, {expr}) Number bitwise AND
api_info() Dict api metadata
append({lnum}, {string}) Number append {string} below line {lnum}
append({lnum}, {list}) Number append lines {list} below line {lnum}
@ -2291,7 +2291,7 @@ assert_notmatch({pat}, {text} [, {msg}])
assert_report({msg}) Number report a test failure
assert_true({actual} [, {msg}]) Number assert {actual} is true
atan({expr}) Float arc tangent of {expr}
atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
browse({save}, {title}, {initdir}, {default})
String put up a file requester
browsedir({title}, {initdir}) String put up a directory requester
@ -2317,7 +2317,7 @@ char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
@ -2330,7 +2330,7 @@ copy({expr}) any make a shallow copy of {expr}
cos({expr}) Float cosine of {expr}
cosh({expr}) Float hyperbolic cosine of {expr}
count({list}, {expr} [, {ic} [, {start}]])
Number count how many {expr} are in {list}
Number count how many {expr} are in {list}
cscope_connection([{num}, {dbpath} [, {prepend}]])
Number checks existence of cscope connection
ctxget([{index}]) Dict return the |context| dict at {index}
@ -2343,7 +2343,7 @@ ctxsize() Number return |context-stack| size
cursor({lnum}, {col} [, {off}])
Number move cursor to {lnum}, {col}, {off}
cursor({list}) Number move cursor to position in {list}
debugbreak({pid}) Number interrupt process being debugged
debugbreak({pid}) Number interrupt process being debugged
deepcopy({expr} [, {noref}]) any make a full copy of {expr}
delete({fname} [, {flags}]) Number delete the file or directory {fname}
deletebufline({buf}, {first}[, {last}])
@ -2362,7 +2362,7 @@ eval({string}) any evaluate {string} into its value
eventhandler() Number |TRUE| if inside an event handler
executable({expr}) Number 1 if executable {expr} exists
execute({command}) String execute and capture output of {command}
exepath({expr}) String full path of the command {expr}
exepath({expr}) String full path of the command {expr}
exists({expr}) Number |TRUE| if {expr} exists
extend({expr1}, {expr2} [, {expr3}])
List/Dict insert items of {expr2} into {expr1}
@ -2482,11 +2482,11 @@ inputlist({textlist}) Number let the user pick from a choice list
inputrestore() Number restore typeahead
inputsave() Number save and clear typeahead
inputsecret({prompt} [, {text}])
String like input() but hiding the text
String like input() but hiding the text
insert({object}, {item} [, {idx}])
List insert {item} in {object} [before {idx}]
interrupt() none interrupt script execution
invert({expr}) Number bitwise invert
invert({expr}) Number bitwise invert
isdirectory({directory}) Number |TRUE| if {directory} is a directory
isinf({expr}) Number determine if {expr} is infinity value
(positive or negative)
@ -2506,7 +2506,7 @@ json_encode({expr}) String Convert {expr} to JSON
keys({dict}) List keys in {dict}
len({expr}) Number the length of {expr}
libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg}
libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
line({expr} [, {winid}]) Number line nr of cursor, last line or mark
line2byte({lnum}) Number byte count of line {lnum}
lispindent({lnum}) Number Lisp indent for line {lnum}
@ -2548,7 +2548,7 @@ msgpackparse({data}) List parse msgpack to a list of objects
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
nr2char({expr}[, {utf8}]) String single char with ASCII/UTF-8 value {expr}
nvim_...({args}...) any call nvim |api| functions
or({expr}, {expr}) Number bitwise OR
or({expr}, {expr}) Number bitwise OR
pathshorten({expr}) String shorten directory names in a path
perleval({expr}) any evaluate |perl| expression
pow({x}, {y}) Float {x} to the power of {y}
@ -2607,7 +2607,7 @@ screenrow() Number current cursor row
screenstring({row}, {col}) String characters at screen position
search({pattern} [, {flags} [, {stopline} [, {timeout}]]])
Number search for {pattern}
searchcount([{options}]) Dict Get or update the last search count
searchcount([{options}]) Dict Get or update the last search count
searchdecl({name} [, {global} [, {thisblock}]])
Number search for variable declaration
searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
@ -2679,7 +2679,7 @@ split({expr} [, {pat} [, {keepempty}]])
List make |List| from {pat} separated {expr}
sqrt({expr}) Float square root of {expr}
stdioopen({dict}) Number open stdio in a headless instance.
stdpath({what}) String/List returns the standard path(s) for {what}
stdpath({what}) String/List returns the standard path(s) for {what}
str2float({expr} [, {quoted}]) Float convert String to Float
str2list({expr} [, {utf8}]) List convert each character of {expr} to
ASCII/UTF-8 value
@ -2715,7 +2715,7 @@ synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col}
synIDattr({synID}, {what} [, {mode}])
String attribute {what} of syntax ID {synID}
synIDtrans({synID}) Number translated syntax ID of {synID}
synconcealed({lnum}, {col}) List info about concealing
synconcealed({lnum}, {col}) List info about concealing
synstack({lnum}, {col}) List stack of syntax IDs at {lnum} and {col}
system({cmd} [, {input}]) String output of shell command/filter {cmd}
systemlist({cmd} [, {input}]) List output of shell command/filter {cmd}
@ -2751,7 +2751,7 @@ values({dict}) List values in {dict}
virtcol({expr}) Number screen column of cursor or mark
visualmode([expr]) String last visual mode used
wait({timeout}, {condition}[, {interval}])
Number Wait until {condition} is satisfied
Number Wait until {condition} is satisfied
wildmenumode() Number whether 'wildmenu' mode is active
win_execute({id}, {command} [, {silent}])
String execute {command} in window {id}

View File

@ -93,6 +93,7 @@ REFERENCE MANUAL: These files explain every detail of Vim. *reference_toc*
General subjects ~
|intro.txt| general introduction to Vim; notation used in help files
|nvim.txt| Transitioning from Vim
|help.txt| overview and quick reference (this file)
|helphelp.txt| about using the help files
|index.txt| alphabetical index of all commands
@ -133,17 +134,19 @@ Advanced editing ~
|api.txt| Nvim API via RPC, Lua and VimL
Special issues ~
|testing.txt| testing Vim and Vim scripts
|print.txt| printing
|remote.txt| using Vim as a server or client
|testing.txt| testing Vim and Vim scripts
|print.txt| printing
|remote_plugin.txt| Nvim support for remote plugins
Programming language support ~
|indent.txt| automatic indenting for C and other languages
|lsp.txt| Language Server Protocol (LSP)
|treesitter.txt| tree-sitter library for incremental parsing of buffers
|diagnostic.txt| Diagnostic framework
|syntax.txt| syntax highlighting
|filetype.txt| settings done specifically for a type of file
|quickfix.txt| commands for a quick edit-compile-fix cycle
|provider.txt| Built-in remote plugin hosts
|ft_ada.txt| Ada (the programming language) support
|ft_ps1.txt| Filetype plugin for Windows PowerShell
|ft_raku.txt| Filetype plugin for Raku
@ -164,6 +167,7 @@ GUI ~
Interfaces ~
|if_cscop.txt| using Cscope with Vim
|if_perl.txt| Perl interface
|if_pyth.txt| Python interface
|if_ruby.txt| Ruby interface
|sign.txt| debugging signs
@ -171,6 +175,16 @@ Interfaces ~
Versions ~
|vim_diff.txt| Main differences between Nvim and Vim
|vi_diff.txt| Main differences between Vim and Vi
|deprecated.txt| Deprecated items that have been or will be removed
Other ~
|terminal_emulator.txt| Terminal buffers
|term.txt| Terminal UI
|ui.txt| Nvim UI protocol
|channel.txt| Nvim asynchronous IO
|dev_style.txt| Nvim style guide
|job_control.txt| Spawn and control multiple processes
*standard-plugin-list*
Standard plugins ~
|matchit.txt| Extended |%| matching

View File

@ -214,7 +214,7 @@ For |lsp-request|, each |lsp-handler| has this signature: >
request, a table with information about the error
is sent. Otherwise, it is `nil`. See |lsp-response|.
{result} (Result | Params | nil)
When the language server is able to succesfully
When the language server is able to successfully
complete a request, this contains the `result` key
of the response. See |lsp-response|.
{ctx} (table)
@ -236,7 +236,7 @@ For |lsp-request|, each |lsp-handler| has this signature: >
{config} (table)
Configuration for the handler.
Each handler can define it's own configuration
Each handler can define its own configuration
table that allows users to customize the behavior
of a particular handler.
@ -274,7 +274,7 @@ For |lsp-notification|, each |lsp-handler| has this signature: >
{config} (table)
Configuration for the handler.
Each handler can define it's own configuration
Each handler can define its own configuration
table that allows users to customize the behavior
of a particular handler.
@ -369,7 +369,7 @@ Handlers can be set by:
For example: >
vim.lsp.start_client {
..., -- Other configuration ommitted.
..., -- Other configuration omitted.
handlers = {
["textDocument/definition"] = my_custom_server_definition
},
@ -394,6 +394,9 @@ in the following order:
2. Handler defined in |vim.lsp.start_client()|, if any.
3. Handler defined in |vim.lsp.handlers|, if any.
*vim.lsp.log_levels*
Log levels are defined in |vim.log.levels|
VIM.LSP.PROTOCOL *vim.lsp.protocol*
@ -444,7 +447,7 @@ LspCodeLens
|nvim_buf_set_extmark()|.
LspCodeLensSeparator *hl-LspCodeLensSeparator*
Used to color the seperator between two or more code lens.
Used to color the separator between two or more code lens.
*lsp-highlight-signature*
@ -819,10 +822,10 @@ start_client({config}) *vim.lsp.start_client()*
throws an error. `code` is a number
describing the error. Other arguments
may be passed depending on the error
kind. See |vim.lsp.client_errors| for
possible errors. Use
`vim.lsp.client_errors[code]` to get
human-friendly name.
kind. See |vim.lsp.rpc.client_errors|
for possible errors. Use
`vim.lsp.rpc.client_errors[code]` to
get human-friendly name.
{before_init} Callback with parameters
(initialize_params, config) invoked
before the LSP "initialize" phase,
@ -1285,7 +1288,7 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
{config} table Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |vim.api.nvim_open_win()|
• See |nvim_open_win()|
*vim.lsp.handlers.signature_help()*
signature_help({_}, {result}, {ctx}, {config})
@ -1377,18 +1380,6 @@ character_offset({bufnr}, {row}, {col})
(number, number) UTF-32 and UTF-16 index of the character
in line {row} column {col} in buffer {buf}
*vim.lsp.util.close_preview_autocmd()*
close_preview_autocmd({events}, {winnr})
Creates autocommands to close a preview window when events
happen.
Parameters: ~
{events} (table) list of events
{winnr} (number) window id of preview window
See also: ~
|autocmd-events|
*vim.lsp.util.convert_input_to_markdown_lines()*
convert_input_to_markdown_lines({input}, {contents})
Converts any of `MarkedString` | `MarkedString[]` |
@ -1909,14 +1900,14 @@ make_client_capabilities()
*vim.lsp.protocol.resolve_capabilities()*
resolve_capabilities({server_capabilities})
`*` to match one or more characters in a path segment `?` to
match on one character in a path segment `**` to match any
number of path segments, including none `{}` to group
conditions (e.g. `**/*.{ts,js}` matches all TypeScript and
JavaScript files) `[]` to declare a range of characters to
match in a path segment (e.g., `example.[0-9]` to match on
`example.0` , `example.1` , …) `[!...]` to negate a range of
characters to match in a path segment (e.g., `example.[!0-9]`
to match on `example.a` , `example.b` , but not `example.0` )
Creates a normalized object describing LSP server
capabilities.
Parameters: ~
{server_capabilities} table Table of capabilities
supported by the server
Return: ~
table Normalized table of capabilities
vim:tw=78:ts=8:ft=help:norl:

View File

@ -759,9 +759,9 @@ vim.stricmp({a}, {b}) *vim.stricmp()*
respectively.
vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not
supplied, the length of the string is used. All indicies are zero-based.
Returns two values: the UTF-32 and UTF-16 indicies respectively.
Convert byte index to UTF-32 and UTF-16 indices. If {index} is not
supplied, the length of the string is used. All indices are zero-based.
Returns two values: the UTF-32 and UTF-16 indices respectively.
Embedded NUL bytes are treated as terminating the string. Invalid
UTF-8 bytes, and embedded surrogates are counted as one code
@ -881,6 +881,15 @@ vim.types *vim.types*
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
*log_levels* *vim.log.levels*
Log levels are one of the values defined in `vim.log.levels`:
vim.log.levels.DEBUG
vim.log.levels.ERROR
vim.log.levels.INFO
vim.log.levels.TRACE
vim.log.levels.WARN
------------------------------------------------------------------------------
LUA-VIMSCRIPT BRIDGE *lua-vimscript*

View File

@ -90,7 +90,7 @@ Mouse input has the following behavior:
- If another window is clicked, terminal focus will be lost and nvim will jump
to the clicked window
- If the mouse wheel is used while the mouse is positioned in another window,
the terminal wont lose focus and the hovered window will be scrolled.
the terminal won't lose focus and the hovered window will be scrolled.
==============================================================================
Configuration *terminal-config*
@ -426,7 +426,7 @@ When 'background' is "dark":
hi debugBreakpoint term=reverse ctermbg=red guibg=red
Shorcuts *termdebug_shortcuts*
Shortcuts *termdebug_shortcuts*
You can define your own shortcuts (mappings) to control gdb, that can work in
any window, using the TermDebugSendCommand() function. Example: >

View File

@ -3226,10 +3226,14 @@ A jump table for the options with a short description can be found at |Q_op|.
'inccommand' 'icm' string (default "nosplit")
global
"nosplit": Shows the effects of a command incrementally, as you type.
"split" : Also shows partial off-screen results in a preview window.
When nonempty, shows the effects of |:substitute|, |:smagic|, and
|:snomagic| as you type.
Works for |:substitute|, |:smagic|, |:snomagic|. |hl-Substitute|
Possible values:
nosplit Shows the effects of a command incrementally in the
buffer.
split Like "nosplit", but also shows partial off-screen
results in a preview window.
If the preview is too slow (exceeds 'redrawtime') then 'inccommand' is
automatically disabled until |Command-line-mode| is done.

View File

@ -100,7 +100,7 @@ tsnode:prev_named_sibling() *tsnode:prev_named_sibling()*
tsnode:iter_children() *tsnode:iter_children()*
Iterates over all the direct children of {tsnode}, regardless of
wether they are named or not.
whether they are named or not.
Returns the child node plus the eventual field name corresponding to
this child node.
@ -155,9 +155,9 @@ tsnode:sexpr() *tsnode:sexpr()*
Get an S-expression representing the node as a string.
tsnode:id() *tsnode:id()*
Get an unique identier for the node inside its own tree.
Get an unique identifier for the node inside its own tree.
No guarantees are made about this identifer's internal representation,
No guarantees are made about this identifier's internal representation,
except for being a primitive lua type with value equality (so not a table).
Presently it is a (non-printable) string.
@ -195,7 +195,7 @@ to a match.
Treesitter Query Predicates *lua-treesitter-predicates*
When writing queries for treesitter, one might use `predicates`, that is,
special scheme nodes that are evaluted to verify things on a captured node for
special scheme nodes that are evaluated to verify things on a captured node for
example, the |eq?| predicate : >
((identifier) @foo (#eq? @foo "foo"))
@ -203,7 +203,7 @@ This will only match identifier corresponding to the `"foo"` text.
Here is a list of built-in predicates :
`eq?` *ts-predicate-eq?*
This predicate will check text correspondance between nodes or
This predicate will check text correspondence between nodes or
strings : >
((identifier) @foo (#eq? @foo "foo"))
((node1) @left (node2) @right (#eq? @left @right))
@ -212,7 +212,7 @@ Here is a list of built-in predicates :
`vim-match?` *ts-predicate-vim-match?*
This will match if the provived vim regex matches the text
corresponding to a node : >
((idenfitier) @constant (#match? @constant "^[A-Z_]+$"))
((identifier) @constant (#match? @constant "^[A-Z_]+$"))
< Note: the `^` and `$` anchors will respectively match the
start and end of the node's text.
@ -267,7 +267,7 @@ Here is a list of built-in directives:
`offset!` *ts-predicate-offset!*
Takes the range of the captured node and applies the offsets
to it's range : >
((idenfitier) @constant (#offset! @constant 0 1 0 -1))
((identifier) @constant (#offset! @constant 0 1 0 -1))
< This will generate a range object for the captured node with the
offsets applied. The arguments are
`({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})`

View File

@ -0,0 +1,20 @@
" Vim filetype plugin
" Language: Neovim checkhealth buffer
" Last Change: 2021 Dec 15
if exists("b:did_ftplugin")
finish
endif
runtime! ftplugin/markdown.vim ftplugin/markdown_*.vim ftplugin/markdown/*.vim
setlocal wrap breakindent linebreak
setlocal conceallevel=2 concealcursor=nc
setlocal keywordprg=:help
let &l:iskeyword='!-~,^*,^|,^",192-255'
if exists("b:undo_ftplugin")
let b:undo_ftplugin .= "|setl wrap< bri< lbr< cole< cocu< kp< isk<"
else
let b:undo_ftplugin = "setl wrap< bri< lbr< cole< cocu< kp< isk<"
endif

View File

@ -91,23 +91,22 @@ local function filter_by_severity(severity, diagnostics)
end
---@private
local function prefix_source(source, diagnostics)
vim.validate { source = {source, function(v)
return v == "always" or v == "if_many"
end, "'always' or 'if_many'" } }
if source == "if_many" then
local sources = {}
for _, d in pairs(diagnostics) do
if d.source then
sources[d.source] = true
local function count_sources(bufnr)
local seen = {}
local count = 0
for _, namespace_diagnostics in pairs(diagnostic_cache[bufnr]) do
for _, diagnostic in ipairs(namespace_diagnostics) do
if diagnostic.source and not seen[diagnostic.source] then
seen[diagnostic.source] = true
count = count + 1
end
end
if #vim.tbl_keys(sources) <= 1 then
return diagnostics
end
end
return count
end
---@private
local function prefix_source(diagnostics)
return vim.tbl_map(function(d)
if not d.source then
return d
@ -272,6 +271,8 @@ end
---@private
local function set_diagnostic_cache(namespace, bufnr, diagnostics)
for _, diagnostic in ipairs(diagnostics) do
assert(diagnostic.lnum, "Diagnostic line number is required")
assert(diagnostic.col, "Diagnostic column is required")
diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
diagnostic.end_col = diagnostic.end_col or diagnostic.col
@ -297,11 +298,6 @@ local function restore_extmarks(bufnr, last)
if not found[extmark[1]] then
local opts = extmark[4]
opts.id = extmark[1]
-- HACK: end_row should be end_line
if opts.end_row then
opts.end_line = opts.end_row
opts.end_row = nil
end
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
end
end
@ -386,7 +382,7 @@ local function get_diagnostics(bufnr, opts, clamp)
if not opts.lnum or d.lnum == opts.lnum then
if clamp and vim.api.nvim_buf_is_loaded(b) then
local line_count = buf_line_count[b] - 1
if (d.lnum > line_count or d.end_lnum > line_count) then
if (d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0) then
d = vim.deepcopy(d)
d.lnum = math.max(math.min(d.lnum, line_count), 0)
d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0)
@ -508,17 +504,20 @@ local function diagnostic_move_pos(opts, pos)
return
end
-- Save position in the window's jumplist
vim.api.nvim_win_call(win_id, function() vim.cmd("normal! m'") end)
vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]})
vim.api.nvim_win_call(win_id, function()
-- Save position in the window's jumplist
vim.cmd("normal! m'")
vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]})
-- Open folds under the cursor
vim.cmd("normal! zv")
end)
if float then
local float_opts = type(float) == "table" and float or {}
vim.schedule(function()
M.open_float(
vim.api.nvim_win_get_buf(win_id),
vim.tbl_extend("keep", float_opts, {
bufnr = vim.api.nvim_win_get_buf(win_id),
scope = "cursor",
focus = false,
})
@ -557,11 +556,19 @@ end
--- - underline: (default true) Use underline for diagnostics. Options:
--- * severity: Only underline diagnostics matching the given severity
--- |diagnostic-severity|
--- - virtual_text: (default true) Use virtual text for diagnostics. Options:
--- - virtual_text: (default true) Use virtual text for diagnostics. If multiple diagnostics
--- are set for a namespace, one prefix per diagnostic + the last diagnostic
--- message are shown.
--- Options:
--- * severity: Only show virtual text for diagnostics matching the given
--- severity |diagnostic-severity|
--- * source: (string) Include the diagnostic source in virtual
--- text. One of "always" or "if_many".
--- * source: (boolean or string) Include the diagnostic source in virtual
--- text. Use "if_many" to only show sources if there is more than
--- one diagnostic source in the buffer. Otherwise, any truthy value
--- means to always show the diagnostic source.
--- * spacing: (number) Amount of empty spaces inserted at the beginning
--- of the virtual text.
--- * prefix: (string) Prepend diagnostic message with prefix.
--- * format: (function) A function that takes a diagnostic as input and
--- returns a string. The return value is the text used to display
--- the diagnostic. Example:
@ -653,9 +660,14 @@ function M.set(namespace, bufnr, diagnostics, opts)
M.show(namespace, bufnr, nil, opts)
end
vim.api.nvim_command(
string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr))
)
vim.api.nvim_buf_call(bufnr, function()
vim.api.nvim_command(
string.format(
"doautocmd <nomodeline> DiagnosticChanged %s",
vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr))
)
)
end)
end
--- Get namespace metadata.
@ -920,8 +932,11 @@ M.handlers.virtual_text = {
if opts.virtual_text.format then
diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics)
end
if opts.virtual_text.source then
diagnostics = prefix_source(opts.virtual_text.source, diagnostics)
if
opts.virtual_text.source
and (opts.virtual_text.source ~= "if_many" or count_sources(bufnr) > 1)
then
diagnostics = prefix_source(diagnostics)
end
if opts.virtual_text.severity then
severity = opts.virtual_text.severity
@ -1127,12 +1142,15 @@ end
--- Show diagnostics in a floating window.
---
---@param bufnr number|nil Buffer number. Defaults to the current buffer.
---@param opts table|nil Configuration table with the same keys as
--- |vim.lsp.util.open_floating_preview()| in addition to the following:
--- - bufnr: (number) Buffer number to show diagnostics from.
--- Defaults to the current buffer.
--- - namespace: (number) Limit diagnostics to the given namespace
--- - scope: (string, default "line") Show diagnostics from the whole buffer ("buffer"),
--- the current cursor line ("line"), or the current cursor position ("cursor").
--- Shorthand versions are also accepted ("c" for "cursor", "l" for "line", "b"
--- for "buffer").
--- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather
--- than the cursor position. If a number, interpreted as a line number;
--- otherwise, a (row, col) tuple.
@ -1143,8 +1161,11 @@ end
--- - header: (string or table) String to use as the header for the floating window. If a
--- table, it is interpreted as a [text, hl_group] tuple. Overrides the setting
--- from |vim.diagnostic.config()|.
--- - source: (string) Include the diagnostic source in the message. One of "always" or
--- "if_many". Overrides the setting from |vim.diagnostic.config()|.
--- - source: (boolean or string) Include the diagnostic source in the message.
--- Use "if_many" to only show sources if there is more than one source of
--- diagnostics in the buffer. Otherwise, any truthy value means to always show
--- the diagnostic source. Overrides the setting from
--- |vim.diagnostic.config()|.
--- - format: (function) A function that takes a diagnostic as input and returns a
--- string. The return value is the text used to display the diagnostic.
--- Overrides the setting from |vim.diagnostic.config()|.
@ -1161,15 +1182,21 @@ end
--- highlight.
--- Overrides the setting from |vim.diagnostic.config()|.
---@return tuple ({float_bufnr}, {win_id})
function M.open_float(bufnr, opts)
vim.validate {
bufnr = { bufnr, 'n', true },
opts = { opts, 't', true },
}
function M.open_float(opts, ...)
-- Support old (bufnr, opts) signature
local bufnr
if opts == nil or type(opts) == "number" then
bufnr = opts
opts = ...
else
vim.validate {
opts = { opts, 't', true },
}
end
opts = opts or {}
bufnr = get_bufnr(bufnr)
local scope = opts.scope or "line"
bufnr = get_bufnr(bufnr or opts.bufnr)
local scope = ({l = "line", c = "cursor", b = "buffer"})[opts.scope] or opts.scope or "line"
local lnum, col
if scope == "line" or scope == "cursor" then
if not opts.pos then
@ -1250,8 +1277,8 @@ function M.open_float(bufnr, opts)
diagnostics = reformat_diagnostics(opts.format, diagnostics)
end
if opts.source then
diagnostics = prefix_source(opts.source, diagnostics)
if opts.source and (opts.source ~= "if_many" or count_sources(bufnr) > 1) then
diagnostics = prefix_source(diagnostics)
end
local prefix_opt = if_nil(opts.prefix, (scope == "cursor" and #diagnostics <= 1) and "" or function(_, i)
@ -1327,9 +1354,14 @@ function M.reset(namespace, bufnr)
end
end
vim.api.nvim_command(
string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr))
)
vim.api.nvim_buf_call(bufnr, function()
vim.api.nvim_command(
string.format(
"doautocmd <nomodeline> DiagnosticChanged %s",
vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr))
)
)
end)
end
--- Add all diagnostics to the quickfix list.

View File

@ -290,7 +290,7 @@ end
--- Memoizes a function. On first run, the function return value is saved and
--- immediately returned on subsequent runs. If the function returns a multival,
--- only the first returned value will be memoized and returned. The function will only be run once,
--- even if it has side-effects.
--- even if it has side effects.
---
---@param fn (function) Function to run
---@returns (function) Memoized function
@ -489,7 +489,8 @@ local function text_document_did_open_handler(bufnr, client)
-- Protect against a race where the buffer disappears
-- between `did_open_handler` and the scheduled function firing.
if vim.api.nvim_buf_is_valid(bufnr) then
vim.lsp.diagnostic.redraw(bufnr, client.id)
local namespace = vim.lsp.diagnostic.get_namespace(client.id)
vim.diagnostic.show(namespace, bufnr)
end
end)
end
@ -559,6 +560,12 @@ end
---
--- - {handlers} (table): The handlers used by the client as described in |lsp-handler|.
---
--- - {requests} (table): The current pending requests in flight
--- to the server. Entries are key-value pairs with the key
--- being the request ID while the value is a table with `type`,
--- `bufnr`, and `method` key-value pairs. `type` is either "pending"
--- for an active request, or "cancel" for a cancel request.
---
--- - {config} (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
---
@ -638,8 +645,8 @@ end
---@param on_error Callback with parameters (code, ...), invoked
--- when the client operation throws an error. `code` is a number describing
--- the error. Other arguments may be passed depending on the error kind. See
--- |vim.lsp.client_errors| for possible errors.
--- Use `vim.lsp.client_errors[code]` to get human-friendly name.
--- |vim.lsp.rpc.client_errors| for possible errors.
--- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
---
---@param before_init Callback with parameters (initialize_params, config)
--- invoked before the LSP "initialize" phase, where `params` contains the
@ -750,8 +757,8 @@ function lsp.start_client(config)
---
---@param code (number) Error code
---@param err (...) Other arguments may be passed depending on the error kind
---@see |vim.lsp.client_errors| for possible errors. Use
---`vim.lsp.client_errors[code]` to get a human-friendly name.
---@see |vim.lsp.rpc.client_errors| for possible errors. Use
---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
function dispatch.on_error(code, err)
local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
@ -770,6 +777,10 @@ function lsp.start_client(config)
---@param code (number) exit code of the process
---@param signal (number) the signal used to terminate (if any)
function dispatch.on_exit(code, signal)
if config.on_exit then
pcall(config.on_exit, code, signal, client_id)
end
active_clients[client_id] = nil
uninitialized_clients[client_id] = nil
@ -785,10 +796,6 @@ function lsp.start_client(config)
vim.notify(msg, vim.log.levels.WARN)
end)
end
if config.on_exit then
pcall(config.on_exit, code, signal, client_id)
end
end
-- Start the RPC client.
@ -1105,9 +1112,9 @@ local text_document_did_change_handler
do
text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
-- Don't do anything if there are no clients attached.
-- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
return
return true
end
util.buf_versions[bufnr] = changedtick
local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline)
@ -1149,6 +1156,12 @@ function lsp.buf_attach_client(bufnr, client_id)
client_id = {client_id, 'n'};
}
bufnr = resolve_bufnr(bufnr)
if not vim.api.nvim_buf_is_loaded(bufnr) then
local _ = log.warn() and log.warn(
string.format("buf_attach_client called on unloaded buffer (id: %d): ", bufnr)
)
return false
end
local buffer_client_ids = all_buffer_active_clients[bufnr]
-- This is our first time attaching to this buffer.
if not buffer_client_ids then
@ -1207,6 +1220,50 @@ function lsp.buf_attach_client(bufnr, client_id)
return true
end
--- Detaches client from the specified buffer.
--- Note: While the server is notified that the text document (buffer)
--- was closed, it is still able to send notifications should it ignore this notification.
---
---@param bufnr number Buffer handle, or 0 for current
---@param client_id number Client id
function lsp.buf_detach_client(bufnr, client_id)
validate {
bufnr = {bufnr, 'n', true};
client_id = {client_id, 'n'};
}
bufnr = resolve_bufnr(bufnr)
local client = lsp.get_client_by_id(client_id)
if not client or not client.attached_buffers[bufnr] then
vim.notify(
string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)
)
return
end
changetracking.reset_buf(client, bufnr)
if client.resolved_capabilities.text_document_open_close then
local uri = vim.uri_from_bufnr(bufnr)
local params = { textDocument = { uri = uri; } }
client.notify('textDocument/didClose', params)
end
client.attached_buffers[bufnr] = nil
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr][client_id] = nil
if #vim.tbl_keys(all_buffer_active_clients[bufnr]) == 0 then
all_buffer_active_clients[bufnr] = nil
end
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
vim.diagnostic.reset(namespace, bufnr)
vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
end
--- Checks if a buffer is attached for a particular client.
---
---@param bufnr (number) Buffer handle, or 0 for current
@ -1486,11 +1543,7 @@ local function adjust_start_col(lnum, line, items, encoding)
end
end
if min_start_char then
if encoding == 'utf-8' then
return min_start_char
else
return vim.str_byteindex(line, min_start_char, encoding == 'utf-16')
end
return util._str_byteindex_enc(line, min_start_char, encoding)
else
return nil
end
@ -1655,14 +1708,14 @@ end
--
-- Can be used to lookup the number from the name or the
-- name from the number.
-- Levels by name: "trace", "debug", "info", "warn", "error"
-- Level numbers begin with "trace" at 0
-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
-- Level numbers begin with "TRACE" at 0
lsp.log_levels = log.levels
--- Sets the global log level for LSP logging.
---
--- Levels by name: "trace", "debug", "info", "warn", "error"
--- Level numbers begin with "trace" at 0
--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
--- Level numbers begin with "TRACE" at 0
---
--- Use `lsp.log_levels` for reverse lookup.
---

View File

@ -116,31 +116,30 @@ end
--- asks the user to select one.
--
---@returns The client that the user selected or nil
local function select_client(method)
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
clients = vim.tbl_filter(function (client)
local function select_client(method, on_choice)
validate {
on_choice = { on_choice, 'function', false },
}
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
clients = vim.tbl_filter(function(client)
return client.supports_method(method)
end, clients)
-- better UX when choices are always in the same order (between restarts)
table.sort(clients, function (a, b) return a.name < b.name end)
table.sort(clients, function(a, b)
return a.name < b.name
end)
if #clients > 1 then
local choices = {}
for k,v in pairs(clients) do
table.insert(choices, string.format("%d %s", k, v.name))
end
local user_choice = vim.fn.confirm(
"Select a language server:",
table.concat(choices, "\n"),
0,
"Question"
)
if user_choice == 0 then return nil end
return clients[user_choice]
vim.ui.select(clients, {
prompt = 'Select a language server:',
format_item = function(client)
return client.name
end,
}, on_choice)
elseif #clients < 1 then
return nil
on_choice(nil)
else
return clients[1]
on_choice(clients[1])
end
end
@ -152,11 +151,15 @@ end
--
---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
function M.formatting(options)
local client = select_client("textDocument/formatting")
if client == nil then return end
local params = util.make_formatting_params(options)
return client.request("textDocument/formatting", params, nil, vim.api.nvim_get_current_buf())
local bufnr = vim.api.nvim_get_current_buf()
select_client('textDocument/formatting', function(client)
if client == nil then
return
end
return client.request('textDocument/formatting', params, nil, bufnr)
end)
end
--- Performs |vim.lsp.buf.formatting()| synchronously.
@ -172,17 +175,20 @@ end
---@param timeout_ms (number) Request timeout
---@see |vim.lsp.buf.formatting_seq_sync|
function M.formatting_sync(options, timeout_ms)
local client = select_client("textDocument/formatting")
if client == nil then return end
local params = util.make_formatting_params(options)
local bufnr = vim.api.nvim_get_current_buf()
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr)
elseif err then
vim.notify("vim.lsp.buf.formatting_sync: " .. err, vim.log.levels.WARN)
end
select_client('textDocument/formatting', function(client)
if client == nil then
return
end
local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr)
elseif err then
vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN)
end
end)
end
--- Formats the current buffer by sequentially requesting formatting from attached clients.
@ -238,12 +244,15 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_formatting(options, start_pos, end_pos)
local client = select_client("textDocument/rangeFormatting")
if client == nil then return end
local params = util.make_given_range_params(start_pos, end_pos)
params.options = util.make_formatting_params(options).options
return client.request("textDocument/rangeFormatting", params)
select_client('textDocument/rangeFormatting', function(client)
if client == nil then
return
end
return client.request('textDocument/rangeFormatting', params)
end)
end
--- Renames all references to the symbol under the cursor.

View File

@ -153,19 +153,6 @@ function M.get_namespace(client_id)
return _client_namespaces[client_id]
end
--- Save diagnostics to the current buffer.
---
--- Handles saving diagnostics from multiple clients in the same buffer.
---@param diagnostics Diagnostic[]
---@param bufnr number
---@param client_id number
---@private
function M.save(diagnostics, bufnr, client_id)
local namespace = M.get_namespace(client_id)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end
-- }}}
--- |lsp-handler| for the method "textDocument/publishDiagnostics"
---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific
@ -245,6 +232,23 @@ end
-- Deprecated Functions {{{
--- Save diagnostics to the current buffer.
---
---@deprecated Prefer |vim.diagnostic.set()|
---
--- Handles saving diagnostics from multiple clients in the same buffer.
---@param diagnostics Diagnostic[]
---@param bufnr number
---@param client_id number
---@private
function M.save(diagnostics, bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.save is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
local namespace = M.get_namespace(client_id)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end
-- }}}
--- Get all diagnostics for clients
---
---@deprecated Prefer |vim.diagnostic.get()|
@ -253,6 +257,7 @@ end
--- If nil, diagnostics of all clients are included.
---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[])
function M.get_all(client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
local result = {}
local namespace
if client_id then
@ -274,6 +279,7 @@ end
--- Else, return just the diagnostics associated with the client_id.
---@param predicate function|nil Optional function for filtering diagnostics
function M.get(bufnr, client_id, predicate)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
predicate = predicate or function() return true end
if client_id == nil then
local all_diagnostics = {}
@ -335,6 +341,7 @@ end
---@param severity DiagnosticSeverity
---@param client_id number the client id
function M.get_count(bufnr, severity, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
severity = severity_lsp_to_vim(severity)
local opts = { severity = severity }
if client_id ~= nil then
@ -351,6 +358,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic
function M.get_prev(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -368,6 +376,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic position
function M.get_prev_pos(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -384,6 +393,7 @@ end
---
---@param opts table See |vim.lsp.diagnostic.goto_next()|
function M.goto_prev(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -401,6 +411,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic
function M.get_next(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -418,6 +429,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic position
function M.get_next_pos(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -432,6 +444,7 @@ end
---
---@deprecated Prefer |vim.diagnostic.goto_next()|
function M.goto_next(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -455,6 +468,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_signs(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@ -475,6 +489,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_underline(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@ -496,6 +511,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@ -514,6 +530,7 @@ end
---@return an array of [text, hl_group] arrays. This can be passed directly to
--- the {virt_text} option of |nvim_buf_set_extmark()|.
function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts)
end
@ -531,6 +548,7 @@ end
---@param position table|nil The (0,0)-indexed position
---@return table {popup_bufnr, win_id}
function M.show_position_diagnostics(opts, buf_nr, position)
vim.api.nvim_echo({{'vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
opts = opts or {}
opts.scope = "cursor"
opts.pos = position
@ -554,6 +572,7 @@ end
---@param client_id number|nil the client id
---@return table {popup_bufnr, win_id}
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
opts = opts or {}
opts.scope = "line"
opts.pos = line_nr
@ -565,7 +584,7 @@ end
--- Redraw diagnostics for the given buffer and client
---
---@deprecated Prefer |vim.diagnostic.redraw()|
---@deprecated Prefer |vim.diagnostic.show()|
---
--- This calls the "textDocument/publishDiagnostics" handler manually using
--- the cached diagnostics already received from the server. This can be useful
@ -577,6 +596,7 @@ end
--- client. The default is to redraw diagnostics for all attached
--- clients.
function M.redraw(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
bufnr = get_bufnr(bufnr)
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
@ -604,6 +624,7 @@ end
--- - {workspace}: (boolean, default true)
--- - Set the list with workspace diagnostics
function M.set_qflist(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -635,6 +656,7 @@ end
--- - {workspace}: (boolean, default false)
--- - Set the list with workspace diagnostics
function M.set_loclist(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@ -662,6 +684,7 @@ end
-- send diagnostic information and the client will still process it. The
-- diagnostics are simply not displayed to the user.
function M.disable(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.disable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.disable(bufnr, client.id)
@ -682,6 +705,7 @@ end
--- client. The default is to enable diagnostics for all attached
--- clients.
function M.enable(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.enable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.enable(bufnr, client.id)

View File

@ -28,7 +28,7 @@ local function progress_handler(_, result, ctx, _)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
err_message("LSP[", client_name, "] client has shut down after sending the message")
return
return vim.NIL
end
local val = result.value -- unspecified yet
local token = result.token -- string or number
@ -70,7 +70,7 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
err_message("LSP[", client_name, "] client has shut down after sending the message")
return
return vim.NIL
end
client.messages.progress[token] = {}
return vim.NIL
@ -246,7 +246,7 @@ end
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
--- - See |vim.api.nvim_open_win()|
--- - See |nvim_open_win()|
function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
@ -285,7 +285,7 @@ local function location_handler(_, result, ctx, _)
util.jump_to_location(result[1])
if #result > 1 then
util.set_qflist(util.locations_to_items(result))
vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)})
api.nvim_command("copen")
end
else
@ -379,7 +379,7 @@ local make_call_hierarchy_handler = function(direction)
})
end
end
util.set_qflist(items)
vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items})
api.nvim_command("copen")
end
end

View File

@ -8,8 +8,8 @@ local log = {}
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the name from the number.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error'
-- Level numbers begin with 'trace' at 0
-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
-- Level numbers begin with "TRACE" at 0
log.levels = vim.deepcopy(vim.log.levels)
-- Default log level is warn.

View File

@ -776,149 +776,9 @@ function protocol.make_client_capabilities()
}
end
--[=[
export interface DocumentFilter {
--A language id, like `typescript`.
language?: string;
--A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
scheme?: string;
--A glob pattern, like `*.{ts,js}`.
--
--Glob patterns can have the following syntax:
--- `*` to match one or more characters in a path segment
--- `?` to match on one character in a path segment
--- `**` to match any number of path segments, including none
--- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
--- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
--- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
pattern?: string;
}
--]=]
--[[
--Static registration options to be returned in the initialize request.
interface StaticRegistrationOptions {
--The id used to register the request. The id can be used to deregister
--the request again. See also Registration#id.
id?: string;
}
export interface DocumentFilter {
--A language id, like `typescript`.
language?: string;
--A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
scheme?: string;
--A glob pattern, like `*.{ts,js}`.
--
--Glob patterns can have the following syntax:
--- `*` to match one or more characters in a path segment
--- `?` to match on one character in a path segment
--- `**` to match any number of path segments, including none
--- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
--- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
--- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
pattern?: string;
}
export type DocumentSelector = DocumentFilter[];
export interface TextDocumentRegistrationOptions {
--A document selector to identify the scope of the registration. If set to null
--the document selector provided on the client side will be used.
documentSelector: DocumentSelector | null;
}
--Code Action options.
export interface CodeActionOptions {
--CodeActionKinds that this server may return.
--
--The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
--may list out every specific kind they provide.
codeActionKinds?: CodeActionKind[];
}
interface ServerCapabilities {
--Defines how text documents are synced. Is either a detailed structure defining each notification or
--for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`.
textDocumentSync?: TextDocumentSyncOptions | number;
--The server provides hover support.
hoverProvider?: boolean;
--The server provides completion support.
completionProvider?: CompletionOptions;
--The server provides signature help support.
signatureHelpProvider?: SignatureHelpOptions;
--The server provides goto definition support.
definitionProvider?: boolean;
--The server provides Goto Type Definition support.
--
--Since 3.6.0
typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides Goto Implementation support.
--
--Since 3.6.0
implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides find references support.
referencesProvider?: boolean;
--The server provides document highlight support.
documentHighlightProvider?: boolean;
--The server provides document symbol support.
documentSymbolProvider?: boolean;
--The server provides workspace symbol support.
workspaceSymbolProvider?: boolean;
--The server provides code actions. The `CodeActionOptions` return type is only
--valid if the client signals code action literal support via the property
--`textDocument.codeAction.codeActionLiteralSupport`.
codeActionProvider?: boolean | CodeActionOptions;
--The server provides code lens.
codeLensProvider?: CodeLensOptions;
--The server provides document formatting.
documentFormattingProvider?: boolean;
--The server provides document range formatting.
documentRangeFormattingProvider?: boolean;
--The server provides document formatting on typing.
documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions;
--The server provides rename support. RenameOptions may only be
--specified if the client states that it supports
--`prepareSupport` in its initial `initialize` request.
renameProvider?: boolean | RenameOptions;
--The server provides document link support.
documentLinkProvider?: DocumentLinkOptions;
--The server provides color provider support.
--
--Since 3.6.0
colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides folding provider support.
--
--Since 3.10.0
foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides go to declaration support.
--
--Since 3.14.0
declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides execute command support.
executeCommandProvider?: ExecuteCommandOptions;
--Workspace specific server capabilities
workspace?: {
--The server supports workspace folder.
--
--Since 3.6.0
workspaceFolders?: {
* The server has support for workspace folders
supported?: boolean;
* Whether the server wants to receive workspace folder
* change notifications.
*
* If a strings is provided the string is treated as a ID
* under which the notification is registered on the client
* side. The ID can be used to unregister for these events
* using the `client/unregisterCapability` request.
changeNotifications?: string | boolean;
}
}
--Experimental server capabilities.
experimental?: any;
}
--]]
--- Creates a normalized object describing LSP server capabilities.
---@param server_capabilities table Table of capabilities supported by the server
---@return table Normalized table of capabilities
function protocol.resolve_capabilities(server_capabilities)
local general_properties = {}
local text_document_sync_properties

View File

@ -133,7 +133,8 @@ local function request_parser_loop()
end
end
local client_errors = vim.tbl_add_reverse_lookup {
--- Mapping of error codes used by the client
local client_errors = {
INVALID_SERVER_MESSAGE = 1;
INVALID_SERVER_JSON = 2;
NO_RESULT_CALLBACK_FOUND = 3;
@ -143,6 +144,8 @@ local client_errors = vim.tbl_add_reverse_lookup {
SERVER_RESULT_CALLBACK_ERROR = 7;
}
client_errors = vim.tbl_add_reverse_lookup(client_errors)
--- Constructs an error message from an LSP error object.
---
---@param err (table) The error object

View File

@ -105,15 +105,16 @@ local function align_end_position(line, byte, offset_encoding)
char = compute_line_length(line, offset_encoding) + 1
else
-- Modifying line, find the nearest utf codepoint
local offset = str_utf_end(line, byte)
local offset = str_utf_start(line, byte)
-- If the byte does not fall on the start of the character, then
-- align to the start of the next character.
if offset > 0 then
char = byte_to_utf(line, byte, offset_encoding) + 1
byte = byte + offset
else
if offset < 0 then
byte = byte + str_utf_end(line, byte) + 1
end
if byte <= #line then
char = byte_to_utf(line, byte, offset_encoding)
byte = byte + offset
else
char = compute_line_length(line, offset_encoding) + 1
end
-- Extending line, find the nearest utf codepoint for the last valid character
end
@ -167,7 +168,7 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline,
char_idx = compute_line_length(prev_line, offset_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
char_idx = byte_to_utf(prev_line, start_byte_idx, offset_encoding)
char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding)
end
-- Return the start difference (shared for new and prev lines)

View File

@ -90,6 +90,49 @@ local function split_lines(value)
return split(value, '\n', true)
end
--- Convert byte index to `encoding` index.
--- Convenience wrapper around vim.str_utfindex
---@param line string line to be indexed
---@param index number byte index (utf-8), or `nil` for length
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number `encoding` index of `index` in `line`
function M._str_utfindex_enc(line, index, encoding)
if not encoding then encoding = 'utf-16' end
if encoding == 'utf-8' then
if index then return index else return #line end
elseif encoding == 'utf-16' then
local _, col16 = vim.str_utfindex(line, index)
return col16
elseif encoding == 'utf-32' then
local col32, _ = vim.str_utfindex(line, index)
return col32
else
error("Invalid encoding: " .. vim.inspect(encoding))
end
end
--- Convert UTF index to `encoding` index.
--- Convenience wrapper around vim.str_byteindex
---Alternative to vim.str_byteindex that takes an encoding.
---@param line string line to be indexed
---@param index number UTF index
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
if not encoding then encoding = 'utf-16' end
if encoding == 'utf-8' then
if index then return index else return #line end
elseif encoding == 'utf-16' then
return vim.str_byteindex(line, index, true)
elseif encoding == 'utf-32' then
return vim.str_byteindex(line, index)
else
error("Invalid encoding: " .. vim.inspect(encoding))
end
end
local _str_utfindex_enc = M._str_utfindex_enc
local _str_byteindex_enc = M._str_byteindex_enc
--- Replaces text in a range with new text.
---
--- CAUTION: Changes in-place!
@ -237,6 +280,7 @@ end
---@private
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
--- Returns a zero-indexed column, since set_lines() does the conversion to
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
--- 1-indexed
local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- LSP's line and characters are 0-indexed
@ -247,13 +291,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
if col > 0 then
local line = get_line(bufnr, position.line)
local ok, result
if offset_encoding == "utf-16" or not offset_encoding then
ok, result = pcall(vim.str_byteindex, line, col, true)
elseif offset_encoding == "utf-32" then
ok, result = pcall(vim.str_byteindex, line, col, false)
end
ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding)
if ok then
return result
end
@ -325,12 +363,15 @@ end
--- Applies a list of text edits to a buffer.
---@param text_edits table list of `TextEdit` objects
---@param bufnr number Buffer id
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr`
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
function M.apply_text_edits(text_edits, bufnr)
function M.apply_text_edits(text_edits, bufnr, offset_encoding)
validate {
text_edits = { text_edits, 't', false };
bufnr = { bufnr, 'number', false };
offset_encoding = { offset_encoding, 'string', true };
}
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
if not next(text_edits) then return end
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
@ -367,8 +408,7 @@ function M.apply_text_edits(text_edits, bufnr)
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
local has_eol_text_edit = false
local max = vim.api.nvim_buf_line_count(bufnr)
-- TODO handle offset_encoding
local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '')
local len = _str_utfindex_enc(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '', nil, offset_encoding)
text_edits = vim.tbl_map(function(text_edit)
if max <= text_edit.range.start.line then
text_edit.range.start.line = max - 1
@ -965,6 +1005,8 @@ function M.jump_to_location(location)
local row = range.start.line
local col = get_line_byte_from_position(0, range.start)
api.nvim_win_set_cursor(0, {row + 1, col})
-- Open folds under the cursor
vim.cmd("normal! zv")
return true
end
@ -1218,17 +1260,57 @@ function M.stylize_markdown(bufnr, contents, opts)
return stripped
end
---@private
--- Creates autocommands to close a preview window when events happen.
---
---@param events (table) list of events
---@param winnr (number) window id of preview window
---@param events table list of events
---@param winnr number window id of preview window
---@param bufnrs table list of buffers where the preview window will remain visible
---@see |autocmd-events|
function M.close_preview_autocmd(events, winnr)
local function close_preview_autocmd(events, winnr, bufnrs)
local augroup = 'preview_window_'..winnr
-- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it
vim.cmd(string.format([[
augroup %s
autocmd!
autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s})
augroup end
]], augroup, winnr, table.concat(bufnrs, ',')))
if #events > 0 then
api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)")
vim.cmd(string.format([[
augroup %s
autocmd %s <buffer> lua vim.lsp.util._close_preview_window(%d)
augroup end
]], augroup, table.concat(events, ','), winnr))
end
end
---@private
--- Closes the preview window
---
---@param winnr number window id of preview window
---@param bufnrs table|nil optional list of ignored buffers
function M._close_preview_window(winnr, bufnrs)
vim.schedule(function()
-- exit if we are in one of ignored buffers
if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then
return
end
local augroup = 'preview_window_'..winnr
vim.cmd(string.format([[
augroup %s
autocmd!
augroup end
augroup! %s
]], augroup, augroup))
pcall(vim.api.nvim_win_close, winnr, true)
end)
end
---@internal
--- Computes size of float needed to show contents (with optional wrapping)
---
@ -1335,7 +1417,7 @@ function M.open_floating_preview(contents, syntax, opts)
opts.wrap = opts.wrap ~= false -- wrapping by default
opts.stylize_markdown = opts.stylize_markdown ~= false
opts.focus = opts.focus ~= false
opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre"}
opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "InsertCharPre"}
local bufnr = api.nvim_get_current_buf()
@ -1404,7 +1486,7 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_buf_set_option(floating_bufnr, 'modifiable', false)
api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe')
api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true, nowait = true})
M.close_preview_autocmd(opts.close_events, floating_winnr)
close_preview_autocmd(opts.close_events, floating_winnr, {floating_bufnr, bufnr})
-- save focus_id
if opts.focus_id then
@ -1430,11 +1512,11 @@ do --[[ References ]]
---
---@param bufnr number Buffer id
---@param references table List of `DocumentHighlight` objects to highlight
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to utf-16
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr`
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
function M.buf_highlight_references(bufnr, references, offset_encoding)
validate { bufnr = {bufnr, 'n', true} }
offset_encoding = offset_encoding or 'utf-16'
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
for _, reference in ipairs(references) do
local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"]
local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"]
@ -1529,6 +1611,7 @@ end
---
---@param items (table) list of items
function M.set_loclist(items, win_id)
vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.fn.setloclist(win_id or 0, {}, ' ', {
title = 'Language Server';
items = items;
@ -1542,6 +1625,7 @@ end
---
---@param items (table) list of items
function M.set_qflist(items)
vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.fn.setqflist({}, ' ', {
title = 'Language Server';
items = items;
@ -1645,43 +1729,78 @@ function M.try_trim_markdown_code_blocks(lines)
return 'markdown'
end
local str_utfindex = vim.str_utfindex
---@private
local function make_position_param()
local row, col = unpack(api.nvim_win_get_cursor(0))
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
local function make_position_param(window, offset_encoding)
window = window or 0
local buf = vim.api.nvim_win_get_buf(window)
local row, col = unpack(api.nvim_win_get_cursor(window))
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
row = row - 1
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1]
if not line then
return { line = 0; character = 0; }
end
-- TODO handle offset_encoding
local _
_, col = str_utfindex(line, col)
col = _str_utfindex_enc(line, col, offset_encoding)
return { line = row; character = col; }
end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
---@returns `TextDocumentPositionParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params()
function M.make_position_params(window, offset_encoding)
window = window or 0
local buf = vim.api.nvim_win_get_buf(window)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
return {
textDocument = M.make_text_document_params();
position = make_position_param()
textDocument = M.make_text_document_params(buf);
position = make_position_param(window, offset_encoding)
}
end
--- Utility function for getting the encoding of the first LSP client on the given buffer.
---@param bufnr (number) buffer handle or 0 for current, defaults to current
---@returns (string) encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
validate {
bufnr = {bufnr, 'n', true};
}
local offset_encoding
for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
local this_offset_encoding = client.offset_encoding or "utf-16"
if not offset_encoding then
offset_encoding = this_offset_encoding
elseif offset_encoding ~= this_offset_encoding then
vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN)
end
end
return offset_encoding
end
--- Using the current position in the current buffer, creates an object that
--- can be used as a building block for several LSP requests, such as
--- `textDocument/codeAction`, `textDocument/colorPresentation`,
--- `textDocument/rangeFormatting`.
---
---@param window (optional, number): window handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`current_position`, end = `current_position` } }
function M.make_range_params()
local position = make_position_param()
function M.make_range_params(window, offset_encoding)
local buf = vim.api.nvim_win_get_buf(window)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
local position = make_position_param(window, offset_encoding)
return {
textDocument = M.make_text_document_params(),
textDocument = M.make_text_document_params(buf),
range = { start = position; ["end"] = position; }
}
end
@ -1693,27 +1812,29 @@ end
---Defaults to the start of the last visual selection.
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
---@param bufnr (optional, number): buffer handle or 0 for current, defaults to current
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr`
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`start_position`, end = `end_position` } }
function M.make_given_range_params(start_pos, end_pos)
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
validate {
start_pos = {start_pos, 't', true};
end_pos = {end_pos, 't', true};
offset_encoding = {offset_encoding, 's', true};
}
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
bufnr = bufnr or 0
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<'))
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>'))
-- convert to 0-index
A[1] = A[1] - 1
B[1] = B[1] - 1
-- account for encoding.
-- TODO handle offset_encoding
-- account for offset_encoding.
if A[2] > 0 then
local _, char = M.character_offset(0, A[1], A[2])
A = {A[1], char}
A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)}
end
if B[2] > 0 then
local _, char = M.character_offset(0, B[1], B[2])
B = {B[1], char}
B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)}
end
-- we need to offset the end character position otherwise we loose the last
-- character of the selection, as LSP end position is exclusive
@ -1722,7 +1843,7 @@ function M.make_given_range_params(start_pos, end_pos)
B[2] = B[2] + 1
end
return {
textDocument = M.make_text_document_params(),
textDocument = M.make_text_document_params(bufnr),
range = {
start = {line = A[1], character = A[2]},
['end'] = {line = B[1], character = B[2]}
@ -1732,10 +1853,11 @@ end
--- Creates a `TextDocumentIdentifier` object for the current buffer.
---
---@param bufnr (optional, number): Buffer handle, defaults to current
---@returns `TextDocumentIdentifier`
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier
function M.make_text_document_params()
return { uri = vim.uri_from_bufnr(0) }
function M.make_text_document_params(bufnr)
return { uri = vim.uri_from_bufnr(bufnr or 0) }
end
--- Create the workspace params
@ -1778,14 +1900,16 @@ end
---@param buf buffer id (0 for current)
---@param row 0-indexed line
---@param col 0-indexed byte offset in line
---@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf}
function M.character_offset(bufnr, row, col)
local line = get_line(bufnr, row)
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf`
---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf}
function M.character_offset(buf, row, col, offset_encoding)
local line = get_line(buf, row)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
-- If the col is past the EOL, use the line length.
if col > #line then
return str_utfindex(line)
return _str_utfindex_enc(line, nil, offset_encoding)
end
return str_utfindex(line, col)
return _str_utfindex_enc(line, col, offset_encoding)
end
--- Helper function to return nested values in language server settings

View File

@ -74,8 +74,8 @@ local function uri_from_fname(path)
return table.concat(uri_parts)
end
local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):.*'
local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):[a-zA-Z]:.*'
local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):.*'
local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
--- Get a URI from a bufnr
---@param bufnr number

View File

@ -26,7 +26,9 @@
</screenshots>
<releases>
<release date="2021-12-31" version="0.6.1"/>
<release date="2021-11-30" version="0.6.0"/>
<release date="2021-09-26" version="0.5.1"/>
<release date="2021-07-02" version="0.5.0"/>
<release date="2020-08-04" version="0.4.4"/>
<release date="2019-11-06" version="0.4.3"/>

View File

@ -0,0 +1,28 @@
" Vim syntax file
" Language: Neovim checkhealth buffer
" Last Change: 2021 Dec 15
if exists("b:current_syntax")
finish
endif
runtime! syntax/markdown.vim
unlet! b:current_syntax
syn case match
" We do not care about markdown syntax errors
syn clear markdownError
syn keyword healthError ERROR[:] containedin=markdownCodeBlock,mkdListItemLine
syn keyword healthWarning WARNING[:] containedin=markdownCodeBlock,mkdListItemLine
syn keyword healthSuccess OK[:] containedin=markdownCodeBlock,mkdListItemLine
syn match healthHelp "|.\{-}|" containedin=markdownCodeBlock,mkdListItemLine contains=healthBar
syn match healthBar "|" contained conceal
hi def link healthError Error
hi def link healthWarning WarningMsg
hi def healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232
hi def link healthHelp Identifier
let b:current_syntax = "checkhealth"

View File

@ -339,7 +339,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// @param col Column where to place the mark, 0-based. |api-indexing|
/// @param opts Optional parameters.
/// - id : id of the extmark to edit.
/// - end_line : ending line of the mark, 0-based inclusive.
/// - end_row : ending line of the mark, 0-based inclusive.
/// - end_col : ending col of the mark, 0-based exclusive.
/// - hl_group : name of the highlight group used to highlight
/// this mark.
@ -431,16 +431,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
int line2 = -1;
if (opts->end_line.type == kObjectTypeInteger) {
Integer val = opts->end_line.data.integer;
// For backward compatibility we support "end_line" as an alias for "end_row"
if (HAS_KEY(opts->end_line)) {
if (HAS_KEY(opts->end_row)) {
api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line");
goto error;
}
opts->end_row = opts->end_line;
}
if (opts->end_row.type == kObjectTypeInteger) {
Integer val = opts->end_row.data.integer;
if (val < 0 || val > buf->b_ml.ml_line_count) {
api_set_error(err, kErrorTypeValidation, "end_line value outside range");
api_set_error(err, kErrorTypeValidation, "end_row value outside range");
goto error;
} else {
line2 = (int)val;
}
} else if (HAS_KEY(opts->end_line)) {
api_set_error(err, kErrorTypeValidation, "end_line is not an integer");
} else if (HAS_KEY(opts->end_row)) {
api_set_error(err, kErrorTypeValidation, "end_row is not an integer");
goto error;
}
@ -571,10 +581,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
OPTION_TO_BOOL(right_gravity, right_gravity, true);
// Only error out if they try to set end_right_gravity without
// setting end_col or end_line
// setting end_col or end_row
if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) {
api_set_error(err, kErrorTypeValidation,
"cannot set end_right_gravity without setting end_line or end_col");
"cannot set end_right_gravity without setting end_row or end_col");
goto error;
}

View File

@ -5,6 +5,7 @@ return {
set_extmark = {
"id";
"end_line";
"end_row";
"end_col";
"hl_group";
"virt_text";

View File

@ -231,8 +231,8 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
int tail_offset = mb_head_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
lua_pushinteger(lstate, tail_offset);
int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1);
lua_pushinteger(lstate, head_offset);
return 1;
}
@ -251,7 +251,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1);
lua_pushinteger(lstate, tail_offset);
return 1;
}

View File

@ -424,7 +424,7 @@ end
--- Without a runtime, writes to :Messages
---@see :help nvim_notify
---@param msg string Content of the notification to show to the user
---@param log_level number|nil enum from vim.log.levels
---@param log_level number|nil enum from |vim.log.levels|
---@param opts table|nil additional options (timeout, etc)
function vim.notify(msg, log_level, opts) -- luacheck: no unused
if log_level == vim.log.levels.ERROR then

View File

@ -1011,7 +1011,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
col -= wp->w_leftcol;
if (col >= 0 && col < wp->w_width) {
coloff = col - scol + (local ? 0 : wp->w_wincol) + 1;
coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1;
} else {
scol = ccol = ecol = 0;
// character is left or right of the window
@ -1022,7 +1022,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
}
}
}
*rowp = (local ? 0 : wp->w_winrow) + row + rowoff;
*rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;

View File

@ -2953,7 +2953,7 @@ ambw_end:
}
} else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
if (check_opt_strings(*varp, p_fdc_values, false) != OK) {
if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_pt) {
@ -3332,6 +3332,9 @@ static int int_cmp(const void *a, const void *b)
/// @return OK when the value is valid, FAIL otherwise
int check_signcolumn(char_u *val)
{
if (*val == NUL) {
return FAIL;
}
// check for basic match
if (check_opt_strings(val, p_scl_values, false) == OK) {
return OK;

View File

@ -658,7 +658,8 @@ static int qf_get_next_str_line(qfstate_T *state)
state->linebuf = IObuff;
state->linelen = len;
}
STRLCPY(state->linebuf, p_str, state->linelen + 1);
memcpy(state->linebuf, p_str, state->linelen);
state->linebuf[state->linelen] = '\0';
// Increment using len in order to discard the rest of the line if it
// exceeds LINE_MAXLEN.

View File

@ -5248,6 +5248,9 @@ search_line:
if (depth == -1) {
// match in current file
if (l_g_do_tagpreview != 0) {
if (!win_valid(curwin_save)) {
break;
}
if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL,
NULL, true, lnum, false))) {
break; // failed to jump to file

View File

@ -1466,6 +1466,17 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int width, height;
vterm_get_size(term->vt, &height, &width);
// May still have pending scrollback after increase in terminal height if the
// scrollback wasn't refreshed in time; append these to the top of the buffer.
int row_offset = term->sb_pending;
while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
fetch_row(term, term->sb_pending - row_offset - 1, width);
ml_append(0, (uint8_t *)term->textbuf, 0, false);
appended_lines(0, 1);
term->sb_pending--;
}
row_offset -= term->sb_pending;
while (term->sb_pending > 0) {
// This means that either the window height has decreased or the screen
// became full and libvterm had to push all rows up. Convert the first
@ -1476,7 +1487,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
ml_delete(1, false);
deleted_lines(1, 1);
}
fetch_row(term, -term->sb_pending, width);
fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
appended_lines(buf_index, 1);

View File

@ -33,7 +33,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
call timer_start(LoadAdjust(100), 'ExitInsertMode')
call timer_start(LoadAdjust(200), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
unlet g:triggered
@ -1897,6 +1897,26 @@ func Test_autocmd_CmdWinEnter()
call delete(filename)
endfunc
func Test_autocmd_was_using_freed_memory()
pedit xx
n x
augroup winenter
au WinEnter * if winnr('$') > 2 | quit | endif
augroup END
" Nvim needs large 'winwidth' and 'nowinfixwidth' to crash
set winwidth=99999 nowinfixwidth
split
augroup winenter
au! WinEnter
augroup END
set winwidth& winfixwidth&
bwipe xx
bwipe x
pclose
endfunc
func Test_FileChangedShell_reload()
if !has('unix')
return
@ -2125,6 +2145,19 @@ func Test_autocmd_closes_window()
au! BufWinLeave
endfunc
func Test_autocmd_quit_psearch()
sn aa bb
augroup aucmd_win_test
au!
au BufEnter,BufLeave,BufNew,WinEnter,WinLeave,WinNew * if winnr('$') > 1 | q | endif
augroup END
ps /
augroup aucmd_win_test
au!
augroup END
endfunc
func Test_autocmd_closing_cmdwin()
au BufWinLeave * nested q
call assert_fails("norm 7q?\n", 'E855:')

View File

@ -4525,6 +4525,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
fix_current_dir();
// Careful: autocommands may close the window and make "wp" invalid
if (flags & WEE_TRIGGER_NEW_AUTOCMDS) {
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
}
@ -4558,7 +4559,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
}
// set window width to desired minimal value
if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) {
if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !curwin->w_floating) {
win_setwidth((int)p_wiw);
}

View File

@ -116,7 +116,7 @@ Filtering Tests
### Filter by name
Another filter method is by setting a pattern of test name to `TEST_FILTER`.
Another filter method is by setting a pattern of test name to `TEST_FILTER` or `TEST_FILTER_OUT`.
``` lua
it('foo api',function()
@ -131,6 +131,10 @@ To run only test with filter name:
TEST_FILTER='foo.*api' make functionaltest
To run all tests except ones matching a filter:
TEST_FILTER_OUT='foo.*api' make functionaltest
### Filter by file
To run a *specific* unit test:

View File

@ -104,10 +104,10 @@ describe('API/extmarks', function()
it("can end extranges past final newline using end_col = 0", function()
set_extmark(ns, marks[1], 0, 0, {
end_col = 0,
end_line = 1
end_row = 1
})
eq("end_col value outside range",
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_line = 1 }))
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
end)
it('adds, updates and deletes marks', function()
@ -1424,6 +1424,14 @@ describe('API/extmarks', function()
eq({ {1, 0, 0}, {2, 0, 8} },
meths.buf_get_extmarks(0, ns, 0, -1, {}))
end)
it('can accept "end_row" or "end_line" #16548', function()
set_extmark(ns, marks[1], 0, 0, {
end_col = 0,
end_line = 1
})
eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true}))
end)
end)
describe('Extmarks buffer api with many marks', function()

View File

@ -83,4 +83,9 @@ describe('set', function()
Press ENTER or type command to continue^ |
]])
end)
it('foldcolumn and signcolumn to empty string is disallowed', function()
matches('E474: Invalid argument: fdc=', exc_exec('set fdc='))
matches('E474: Invalid argument: scl=', exc_exec('set scl='))
end)
end)

View File

@ -1343,7 +1343,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = "We're no strangers to love..."})
local float_bufnr, winnr = vim.diagnostic.open_float({header = "We're no strangers to love..."})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1355,7 +1355,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = {'You know the rules', 'Search'}})
local float_bufnr, winnr = vim.diagnostic.open_float({header = {'You know the rules', 'Search'}})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1370,7 +1370,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope="buffer"})
local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope="buffer"})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1387,7 +1387,7 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, {2, 1})
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false})
local float_bufnr, winnr = vim.diagnostic.open_float({header=false})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1402,7 +1402,7 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, {1, 1})
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, pos=1})
local float_bufnr, winnr = vim.diagnostic.open_float({header=false, pos=1})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1419,7 +1419,7 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, {2, 2})
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor"})
local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor"})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1434,7 +1434,7 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, {1, 1})
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor", pos={1,3}})
local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={1,3}})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1449,7 +1449,7 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, {1, 1})
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor", pos={0,first_line_len}})
local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={0,first_line_len}})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1665,7 +1665,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer"})
local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1678,7 +1678,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer", prefix = ""})
local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", prefix = ""})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
@ -1691,7 +1691,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {
local float_bufnr, winnr = vim.diagnostic.open_float({
header = false,
prefix = function(_, i, total)
-- Only show a number if there is more than one diagnostic
@ -1712,7 +1712,7 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, {
local float_bufnr, winnr = vim.diagnostic.open_float({
header = false,
prefix = function(_, i, total)
-- Only show a number if there is more than one diagnostic
@ -1728,7 +1728,21 @@ describe('vim.diagnostic', function()
]])
eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'table' or 'function', got 42",
pcall_err(exec_lua, [[ vim.diagnostic.open_float(0, { prefix = 42 }) ]]))
pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]]))
end)
it('works with the old signature', function()
eq({'1. Syntax error'}, exec_lua [[
local diagnostics = {
make_error("Syntax error", 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
]])
end)
end)
@ -1918,5 +1932,27 @@ describe('vim.diagnostic', function()
return {show_called, hide_called}
]])
end)
it('triggers the autocommand when diagnostics are set', function()
eq(1, exec_lua [[
vim.g.diagnostic_autocmd_triggered = 0
vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1')
vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test")
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic', 0, 0, 0, 0)
})
return vim.g.diagnostic_autocmd_triggered
]])
end)
it('triggers the autocommand when diagnostics are cleared', function()
eq(1, exec_lua [[
vim.g.diagnostic_autocmd_triggered = 0
vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1')
vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test")
vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr)
return vim.g.diagnostic_autocmd_triggered
]])
end)
end)
end)

View File

@ -155,6 +155,12 @@ describe('URI methods', function()
return pcall(vim.uri_to_fname, 'not_an_uri.txt')
]])
end)
it('uri_to_fname should not treat comma as a scheme character', function()
eq(false, exec_lua [[
return pcall(vim.uri_to_fname, 'foo,://bar')
]])
end)
end)
end)

View File

@ -156,7 +156,7 @@ describe('health.vim', function()
test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
========================================================================
- ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
function health#check, line 24]])
function health#check, line 20]])
eq(expected, received)
end)
@ -167,7 +167,7 @@ describe('health.vim', function()
broken: health#broken#check
========================================================================
- ERROR: Failed to run healthcheck for "broken" plugin. Exception:
function health#check[24]..health#broken#check, line 1
function health#check[20]..health#broken#check, line 1
caused an error
]])
end)
@ -186,7 +186,7 @@ describe('health.vim', function()
test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
========================================================================
- ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
function health#check, line 24]])
function health#check, line 20]])
eq(expected, received)
end)

View File

@ -164,6 +164,201 @@ describe('incremental synchronization', function()
}
test_edit({"a"}, {"rb"}, expected_text_changes, 'utf-16', '\n')
end)
it('deleting a line', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 0
},
['end'] = {
character = 0,
line = 1
}
},
rangeLength = 12,
text = ''
}
}
test_edit({"hello world"}, {"dd"}, expected_text_changes, 'utf-16', '\n')
end)
it('deleting an empty line', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 0,
line = 2
}
},
rangeLength = 1,
text = ''
}
}
test_edit({"hello world", ""}, {"jdd"}, expected_text_changes, 'utf-16', '\n')
end)
it('adding a line', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 0,
line = 1
}
},
rangeLength = 0,
text = 'hello world\n'
}
}
test_edit({"hello world"}, {"yyp"}, expected_text_changes, 'utf-16', '\n')
end)
it('adding an empty line', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 0,
line = 1
}
},
rangeLength = 0,
text = '\n'
}
}
test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n')
end)
end)
describe('multi line edit', function()
it('deletion and insertion', function()
local expected_text_changes = {
-- delete "_fsda" from end of line 1
{
range = {
['start'] = {
character = 4,
line = 1
},
['end'] = {
character = 9,
line = 1
}
},
rangeLength = 5,
text = ''
},
-- delete "hello world\n" from line 2
{
range = {
['start'] = {
character = 0,
line = 2
},
['end'] = {
character = 0,
line = 3
}
},
rangeLength = 12,
text = ''
},
-- delete "1234" from beginning of line 2
{
range = {
['start'] = {
character = 0,
line = 2
},
['end'] = {
character = 4,
line = 2
}
},
rangeLength = 4,
text = ''
},
-- add " asdf" to end of line 1
{
range = {
['start'] = {
character = 4,
line = 1
},
['end'] = {
character = 4,
line = 1
}
},
rangeLength = 0,
text = ' asdf'
},
-- delete " asdf\n" from line 2
{
range = {
['start'] = {
character = 0,
line = 2
},
['end'] = {
character = 0,
line = 3
}
},
rangeLength = 6,
text = ''
},
-- undo entire deletion
{
range = {
['start'] = {
character = 4,
line = 1
},
['end'] = {
character = 9,
line = 1
}
},
rangeLength = 5,
text = "_fdsa\nhello world\n1234 asdf"
},
-- redo entire deletion
{
range = {
['start'] = {
character = 4,
line = 1
},
['end'] = {
character = 9,
line = 3
}
},
rangeLength = 27,
text = ' asdf'
},
}
local original_lines = {
"\\begin{document}",
"test_fdsa",
"hello world",
"1234 asdf",
"\\end{document}"
}
test_edit(original_lines, {"jf_vejjbhhdu<C-R>"}, expected_text_changes, 'utf-16', '\n')
end)
end)
describe('multi-operation edits', function()
@ -297,6 +492,80 @@ describe('incremental synchronization', function()
}
test_edit({"🔥"}, {"x"}, expected_text_changes, 'utf-16', '\n')
end)
it('replacing a multibyte character with matching prefix', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 1,
line = 1
}
},
rangeLength = 1,
text = ''
}
}
-- ⟨ is e29fa8, ⟩ is e29fa9
local original_lines = {
"\\begin{document}",
"",
"\\end{document}",
}
test_edit(original_lines, {"jr⟩"}, expected_text_changes, 'utf-16', '\n')
end)
it('replacing a multibyte character with matching suffix', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 1,
line = 1
}
},
rangeLength = 1,
text = ''
}
}
-- ฟ is e0b89f, ḟ is e1b89f
local original_lines = {
"\\begin{document}",
"",
"\\end{document}",
}
test_edit(original_lines, {"jrḟ"}, expected_text_changes, 'utf-16', '\n')
end)
it('inserting before a multibyte character', function()
local expected_text_changes = {
{
range = {
['start'] = {
character = 0,
line = 1
},
['end'] = {
character = 0,
line = 1
}
},
rangeLength = 0,
text = ' '
}
}
local original_lines = {
"\\begin{document}",
"",
"\\end{document}",
}
test_edit(original_lines, {"ji "}, expected_text_changes, 'utf-16', '\n')
end)
it('deleting a multibyte character from a long line', function()
local expected_text_changes = {
{

View File

@ -301,6 +301,43 @@ describe('LSP', function()
}
end)
it('should detach buffer in response to nvim_buf_detach', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
{NIL, {}, {method="finish", client_id=1}};
}
local client
test_rpc_server {
test_name = "basic_finish";
on_setup = function()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
]]
eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
eq(true, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
exec_lua [[
vim.api.nvim_command(BUFFER.."bwipeout")
]]
end;
on_init = function(_client)
client = _client
client.notify('finish')
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if ctx.method == 'finish' then
exec_lua("return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)")
eq(false, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
client.stop()
end
end;
}
end)
it('client should return settings via workspace/configuration handler', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};

View File

@ -12,6 +12,8 @@ local curbufmeths = helpers.curbufmeths
local nvim = helpers.nvim
local feed_data = thelpers.feed_data
local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local assert_alive = helpers.assert_alive
describe(':terminal scrollback', function()
local screen
@ -527,3 +529,71 @@ describe("'scrollback' option", function()
end)
end)
describe("pending scrollback line handling", function()
local screen
before_each(function()
clear()
screen = Screen.new(30, 7)
screen:attach()
screen:set_default_attr_ids {
[1] = {foreground = Screen.colors.Brown},
[2] = {reverse = true},
[3] = {bold = true},
}
end)
it("does not crash after setting 'number' #14891", function()
exec_lua [[
local a = vim.api
local buf = a.nvim_create_buf(true, true)
local chan = a.nvim_open_term(buf, {})
a.nvim_win_set_option(0, "number", true)
a.nvim_chan_send(chan, ("a\n"):rep(11) .. "a")
a.nvim_win_set_buf(0, buf)
]]
screen:expect [[
{1: 1 }^a |
{1: 2 } a |
{1: 3 } a |
{1: 4 } a |
{1: 5 } a |
{1: 6 } a |
|
]]
feed('G')
screen:expect [[
{1: 7 } a |
{1: 8 } a |
{1: 9 } a |
{1: 10 } a |
{1: 11 } a |
{1: 12 } ^a |
|
]]
assert_alive()
end)
it("does not crash after nvim_buf_call #14891", function()
exec_lua [[
local a = vim.api
local bufnr = a.nvim_create_buf(false, true)
a.nvim_buf_call(bufnr, function()
vim.fn.termopen({"echo", ("hi\n"):rep(11)})
end)
a.nvim_win_set_buf(0, bufnr)
vim.cmd("startinsert")
]]
screen:expect [[
hi |
hi |
hi |
|
|
[Process exited 0]{2: } |
{3:-- TERMINAL --} |
]]
assert_alive()
end)
end)

View File

@ -0,0 +1,51 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, meths = helpers.clear, helpers.eq, helpers.meths
local command, funcs = helpers.command, helpers.funcs
before_each(clear)
describe('screenpos() function', function()
it('works in floating window with border', function()
local bufnr = meths.create_buf(false, true)
local opts = {
relative='editor',
height=8,
width=12,
row=6,
col=8,
anchor='NW',
style='minimal',
border='none',
focusable=1
}
local float = meths.open_win(bufnr, false, opts)
command('redraw')
local pos = funcs.screenpos(bufnr, 1, 1)
eq(7, pos.row)
eq(9, pos.col)
-- only left border
opts.border = {'', '', '', '', '', '', '', '|'}
meths.win_set_config(float, opts)
command('redraw')
pos = funcs.screenpos(bufnr, 1, 1)
eq(7, pos.row)
eq(10, pos.col)
-- only top border
opts.border = {'', '_', '', '', '', '', '', ''}
meths.win_set_config(float, opts)
command('redraw')
pos = funcs.screenpos(bufnr, 1, 1)
eq(8, pos.row)
eq(9, pos.col)
-- both left and top border
opts.border = 'single'
meths.win_set_config(float, opts)
command('redraw')
pos = funcs.screenpos(bufnr, 1, 1)
eq(8, pos.row)
eq(10, pos.col)
end)
end)