From 4f305fba14ec8c1919ed793b6e09e15ca54a8c1d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 12 Oct 2022 15:23:42 +0200 Subject: vim-patch:9.0.0731: clang-tidy configuration files are not recognized (#20620) Problem: clang-tidy configuration files are not recognized. Solution: Recognize clang-tidy files as yaml. (closes vim/vim#11350) https://github.com/vim/vim/commit/af40f9af335e0c8b167eac31ceace45b6a2e0565 --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e204fe8ae2..ea39f66393 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1666,6 +1666,7 @@ local filename = { fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', ['/etc/blkid.tab.old'] = 'xml', + ['.clang-tidy'] = 'yaml', ['/etc/zprofile'] = 'zsh', ['.zlogin'] = 'zsh', ['.zlogout'] = 'zsh', -- cgit From 81986a7349da7b88abde459194078e9893e8ae8b Mon Sep 17 00:00:00 2001 From: Daniel Zhang Date: Fri, 14 Oct 2022 17:12:46 +0800 Subject: fix(lua): on_yank error with blockwise multibyte region #20162 Prevent out of range error when calling `str_byteindex`. Use `vim.str_byteindex(bufline, #bufline)` to cacluate utf length of `bufline`. fix #20161 --- runtime/lua/vim/_editor.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 28e5897aa9..0f312f19f5 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -416,11 +416,16 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) c2 = c1 + regtype:sub(2) -- and adjust for non-ASCII characters bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] - if c1 < #bufline then + local utflen = vim.str_utfindex(bufline, #bufline) + if c1 <= utflen then c1 = vim.str_byteindex(bufline, c1) + else + c1 = #bufline + 1 end - if c2 < #bufline then + if c2 <= utflen then c2 = vim.str_byteindex(bufline, c2) + else + c2 = #bufline + 1 end else c1 = (l == pos1[1]) and pos1[2] or 0 -- cgit From e5cb3104d07228de4f2614c425355e8f2f99507d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 14 Oct 2022 11:01:13 -0400 Subject: docs: fix/remove invalid URLs #20647 --- runtime/lua/vim/lsp.lua | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 199964e24e..d717275ae4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -824,23 +824,16 @@ end --- --- See |vim.lsp.start_client()| for all available options. The most important are: --- ---- `name` is an arbitrary name for the LSP client. It should be unique per ---- language server. ---- ---- `cmd` the command as list - used to start the language server. ---- The command must be present in the `$PATH` environment variable or an ---- absolute path to the executable. Shell constructs like `~` are *NOT* expanded. ---- ---- `root_dir` path to the project root. ---- By default this is used to decide if an existing client should be re-used. ---- The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the ---- root by traversing the file system upwards starting ---- from the current directory until either a `pyproject.toml` or `setup.py` ---- file is found. ---- ---- `workspace_folders` a list of { uri:string, name: string } tables. ---- The project root folders used by the language server. ---- If `nil` the property is derived from the `root_dir` for convenience. +--- - `name` arbitrary name for the LSP client. Should be unique per language server. +--- - `cmd` command (in list form) used to start the language server. Must be absolute, or found on +--- `$PATH`. Shell constructs like `~` are not expanded. +--- - `root_dir` path to the project root. By default this is used to decide if an existing client +--- should be re-used. The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the +--- root by traversing the file system upwards starting from the current directory until either +--- a `pyproject.toml` or `setup.py` file is found. +--- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root +--- folders used by the language server. If `nil` the property is derived from `root_dir` for +--- convenience. --- --- Language servers use this information to discover metadata like the --- dependencies of your project and they tend to index the contents within the -- cgit From e26b48bde6e116eb288893454d2876cdad2db1f9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 15 Oct 2022 10:12:25 +0200 Subject: vim-patch:9.0.0752: Rprofile files are not recognized (#20658) Problem: Rprofile files are not recognized. Solution: Recognize Rprofile files as "r". (closes vim/vim#11369) https://github.com/vim/vim/commit/7e120ffccbf81ae8acac28f11fbd5eab79a1630d --- runtime/lua/vim/filetype.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index ea39f66393..d2840b3a7e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1540,6 +1540,9 @@ local filename = { ['.pythonstartup'] = 'python', ['.pythonrc'] = 'python', SConstruct = 'python', + ['.Rprofile'] = 'r', + ['Rprofile'] = 'r', + ['Rprofile.site'] = 'r', ratpoisonrc = 'ratpoison', ['.ratpoisonrc'] = 'ratpoison', inputrc = 'readline', -- cgit From 8f31a730c0dd76180648ba3b7c94aa5d059432e5 Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sun, 16 Oct 2022 23:24:39 +0100 Subject: fix(lsp): reporting bogus capabilities in CodeActionKind #20678 Problem: LSP client provides bogus capabilities in CodeActionKind. LSP logs show this in the "initialize" message: codeActionKind = { valueSet = { "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" } Solution: Only the values from the CodeActionKind table should be presented, not also the keys. fix #20657 --- runtime/lua/vim/lsp/protocol.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 4034753322..7442c8f005 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -637,7 +637,7 @@ function protocol.make_client_capabilities() codeActionLiteralSupport = { codeActionKind = { valueSet = (function() - local res = vim.tbl_values(protocol.CodeActionKind) + local res = vim.tbl_values(constants.CodeActionKind) table.sort(res) return res end)(), -- cgit From d44f088834081ee404db4459fdcfba82d14ef157 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Mon, 17 Oct 2022 08:18:57 +0200 Subject: vim-patch:9.0.0771: cannot always tell the difference beween tex and … (#20687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vim-patch:9.0.0771: cannot always tell the difference beween tex and rexx files Problem: Cannot always tell the difference beween tex and rexx files. Solution: Recognize tex by a leading backslash. (Martin Tournoij, closes vim/vim#11380) https://github.com/vim/vim/commit/bd053f894b0d7652928201faa68c53d1ce2acdc5 --- runtime/lua/vim/filetype/detect.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 7fc7f1b7ca..23fa1c5068 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -181,7 +181,7 @@ function M.cls(bufnr) return vim.g.filetype_cls end local line = getlines(bufnr, 1) - if line:find('^%%') then + if line:find('^[%%\\]') then return 'tex' elseif line:find('^#') and line:lower():find('rexx') then return 'rexx' -- cgit From 042eb74ff1ed63d79f8a642649cd6be6ec4b0eb9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 17 Oct 2022 08:52:40 +0200 Subject: feat(runtime)!: remove filetype.vim (#20428) Made obsolete by now graduated `filetype.lua` (enabled by default). Note that changes or additions to the filetype detection still need to be made through a PR to vim/vim as we port the _logic_ as well as tests. --- runtime/lua/vim/filetype.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index d2840b3a7e..fa792422ba 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2285,8 +2285,6 @@ end --- --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- ---- Note that Lua filetype detection is disabled when |g:do_legacy_filetype| is set. ---- --- Example: ---
 ---  vim.filetype.add({
@@ -2323,7 +2321,7 @@ end
 ---  })
 --- 
--- ---- To add a fallback match on contents (see |new-filetype-scripts|), use +--- To add a fallback match on contents, use ---
 --- vim.filetype.add {
 ---   pattern = {
-- 
cgit 


From e4273135455084bca54a484f88fd364af62bf69c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 17 Oct 2022 18:36:10 +0200
Subject: vim-patch:9.0.0782: OpenVPN files are not recognized (#20702)

Problem:    OpenVPN files are not recognized.
Solution:   Add patterns for OpenVPN files. (closes vim/vim#11391)
https://github.com/vim/vim/commit/4bf67ec52e938a3edaa4f452adab42a57505f940
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index fa792422ba..28d13cf270 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -735,6 +735,7 @@ local extension = {
   opam = 'opam',
   ['or'] = 'openroad',
   scad = 'openscad',
+  ovpn = 'openvpn',
   ora = 'ora',
   org = 'org',
   org_archive = 'org',
@@ -2043,6 +2044,7 @@ local pattern = {
   ['.*%.ml%.cppo'] = 'ocaml',
   ['.*%.mli%.cppo'] = 'ocaml',
   ['.*%.opam%.template'] = 'opam',
+  ['.*/openvpn/.*/.*%.conf'] = 'openvpn',
   ['.*%.[Oo][Pp][Ll]'] = 'opl',
   ['.*/etc/pam%.conf'] = 'pamconf',
   ['.*/etc/pam%.d/.*'] = starsetf('pamconf'),
-- 
cgit 


From 228a04070e94ca31884f304f3a8d34e67654025d Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Tue, 18 Oct 2022 20:05:50 +0200
Subject: vim-patch:9.0.0779: lsl and lm3 file extensions are not recognized
 (#20704)

Problem:    lsl and lm3 file extensions are not recognized.
Solution:   Add *.lsl and *.lm3 patterns. (Doug Kearns, closes vim/vim#11384)
https://github.com/vim/vim/commit/4ac8e7948cb3e07bc4598ede8b274891d14dfa7c
---
 runtime/lua/vim/filetype.lua        |  8 +++++++-
 runtime/lua/vim/filetype/detect.lua | 13 +++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 28d13cf270..7dd1d7cd25 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -611,7 +611,9 @@ local extension = {
   c = function(path, bufnr)
     return require('vim.filetype.detect').lpc(bufnr)
   end,
-  lsl = 'lsl',
+  lsl = function(path, bufnr)
+    return require('vim.filetype.detect').lsl(bufnr)
+  end,
   lss = 'lss',
   nse = 'lua',
   rockspec = 'lua',
@@ -674,6 +676,7 @@ local extension = {
   DEF = 'modula2',
   ['m2'] = 'modula2',
   mi = 'modula2',
+  lm3 = 'modula3',
   ssc = 'monk',
   monk = 'monk',
   tsc = 'monk',
@@ -1459,6 +1462,9 @@ local filename = {
   ['.sawfishrc'] = 'lisp',
   ['/etc/login.access'] = 'loginaccess',
   ['/etc/login.defs'] = 'logindefs',
+  ['.lsl'] = function(path, bufnr)
+    return require('vim.filetype.detect').lsl(bufnr)
+  end,
   ['.luacheckrc'] = 'lua',
   ['lynx.cfg'] = 'lynx',
   ['m3overrides'] = 'm3build',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 23fa1c5068..51ea1971cb 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -634,6 +634,19 @@ function M.lpc(bufnr)
   return 'c'
 end
 
+function M.lsl(bufnr)
+  if vim.g.filetype_lsl then
+    return vim.g.filetype_lsl
+  end
+
+  local line = nextnonblank(bufnr, 1)
+  if findany(line, { '^%s*%%', ':%s*trait%s*$' }) then
+    return 'larch'
+  else
+    return 'lsl'
+  end
+end
+
 function M.m(bufnr)
   if vim.g.filetype_m then
     return vim.g.filetype_m
-- 
cgit 


From fad558b6affd54075654dd55922348f76a95e338 Mon Sep 17 00:00:00 2001
From: ObserverOfTime 
Date: Wed, 19 Oct 2022 20:08:01 +0300
Subject: vim-patch:9.0.0798: clang format configuration files are not
 recognized (#20741)

Problem:    Clang format configuration files are not recognized.
Solution:   Use yaml for Clang format configuration files. (Marwin Glaser,
            closes vim/vim#11398)
https://github.com/vim/vim/commit/3c708c43908ba44f075bbaa7daf584c6b46d9723
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 7dd1d7cd25..af2617677e 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1676,6 +1676,7 @@ local filename = {
   fglrxrc = 'xml',
   ['/etc/blkid.tab'] = 'xml',
   ['/etc/blkid.tab.old'] = 'xml',
+  ['.clang-format'] = 'yaml',
   ['.clang-tidy'] = 'yaml',
   ['/etc/zprofile'] = 'zsh',
   ['.zlogin'] = 'zsh',
-- 
cgit 


From 45ae5c6dc5af63957eb2009e0425d91a3cc79d66 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 20 Oct 2022 22:49:22 +0200
Subject: vim-patch:9.0.0808: jsonnet filetype detection has a typo (#20753)

Problem:    jsonnet filetype detection has a typo.
Solution:   Change "libjsonnet" to "libsonnet". (Maxime Brunet, closes vim/vim#11412)
https://github.com/vim/vim/commit/6c8bc37a1083d17447156592f6f52da2d40b4855
---
 runtime/lua/vim/filetype.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index af2617677e..a574ab1942 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -564,7 +564,7 @@ local extension = {
   json5 = 'json5',
   jsonc = 'jsonc',
   jsonnet = 'jsonnet',
-  libjsonnet = 'jsonnet',
+  libsonnet = 'jsonnet',
   jsp = 'jsp',
   jl = 'julia',
   kv = 'kivy',
-- 
cgit 


From e554f5c545b9ee1195d617b3ac21f24829330a31 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 21 Oct 2022 14:04:53 +0200
Subject: fix(filetype): don't pass empty string to detect (#20766)

Problem: `*.db` files use empty string as default filetype, which is
inconsistent with the rest of the code which uses `nil` instead.
Solution: don't pass a default empty string
---
 runtime/lua/vim/filetype.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index a574ab1942..2c32f9bf52 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -170,7 +170,7 @@ local extension = {
     return require('vim.filetype.detect').bindzone(bufnr, 'dcl')
   end,
   db = function(path, bufnr)
-    return require('vim.filetype.detect').bindzone(bufnr, '')
+    return require('vim.filetype.detect').bindzone(bufnr)
   end,
   bicep = 'bicep',
   bb = 'bitbake',
-- 
cgit 


From 837190720310deca0231fc42aa3023957ff79a3a Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 21 Oct 2022 16:36:43 +0200
Subject: vim-patch:9.0.0814: aws config files are not recognized (#20769)

vim-patch:436e5d395fd6 (since upstream tagged the wrong commit)

Problem:    Aws config files are not recognized.
Solution:   Use "confini" for aws config files. (Justin M. Keyes,
            closes vim/vim#11416)
https://github.com/vim/vim/commit/436e5d395fd629c8d33b5cf7b373aad007f16851
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 2c32f9bf52..56659eff04 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1791,6 +1791,8 @@ local pattern = {
   ['.*/etc/DIR_COLORS'] = 'dircolors',
   ['.*/etc/dnsmasq%.conf'] = 'dnsmasq',
   ['php%.ini%-.*'] = 'dosini',
+  ['.*/%.aws/config'] = 'confini',
+  ['.*/%.aws/credentials'] = 'confini',
   ['.*/etc/pacman%.conf'] = 'confini',
   ['.*/etc/yum%.conf'] = 'dosini',
   ['.*lvs'] = 'dracula',
-- 
cgit 


From 4573cfa3adac3a7dbf1b6b032471a1c14adc7427 Mon Sep 17 00:00:00 2001
From: NAKAI Tsuyoshi <82267684+uga-rosa@users.noreply.github.com>
Date: Mon, 24 Oct 2022 21:53:53 +0900
Subject: fix(lua): pesc, tbl_islist result types #20751

Problem:
- pesc() returns multiple results, it should return a single result.
- tbl_islist() returns non-boolean in some branches.
- Docstring: @generic must be declared first

Solution:
Constrain docstring annotations.
Fix return types.

Co-authored-by: Justin M. Keyes 
---
 runtime/lua/vim/shared.lua | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f03d608e56..f980547ae4 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -62,7 +62,7 @@ end)()
 ---
 ---@param s string String to split
 ---@param sep string Separator or pattern
----@param plain boolean If `true` use `sep` literally (passed to string.find)
+---@param plain (boolean|nil) If `true` use `sep` literally (passed to string.find)
 ---@return fun():string (function) Iterator over the split components
 function vim.gsplit(s, sep, plain)
   vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
@@ -108,11 +108,9 @@ end
 ---
 ---@see |vim.gsplit()|
 ---
----@alias split_kwargs {plain: boolean, trimempty: boolean} | boolean | nil
----
 ---@param s string String to split
 ---@param sep string Separator or pattern
----@param kwargs? {plain: boolean, trimempty: boolean} (table|nil) Keyword arguments:
+---@param kwargs ({plain: boolean, trimempty: boolean}|nil) Keyword arguments:
 ---       - plain: (boolean) If `true` use `sep` literally (passed to string.find)
 ---       - trimempty: (boolean) If `true` remove empty items from the front
 ---         and back of the list
@@ -159,8 +157,8 @@ end
 ---
 ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
 ---
----@param t table (table) Table
 ---@generic T: table
+---@param t table (table) Table
 ---@return T[] (list) List of keys
 function vim.tbl_keys(t)
   assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
@@ -417,8 +415,8 @@ end
 ---@generic T: table
 ---@param dst T List which will be modified and appended to
 ---@param src table List from which values will be inserted
----@param start? number Start index on src. Defaults to 1
----@param finish? number Final index on src. Defaults to `#src`
+---@param start (number|nil) Start index on src. Defaults to 1
+---@param finish (number|nil) Final index on src. Defaults to `#src`
 ---@return T dst
 function vim.list_extend(dst, src, start, finish)
   vim.validate({
@@ -486,7 +484,7 @@ function vim.tbl_islist(t)
     -- TODO(bfredl): in the future, we will always be inside nvim
     -- then this check can be deleted.
     if vim._empty_dict_mt == nil then
-      return nil
+      return false
     end
     return getmetatable(t) ~= vim._empty_dict_mt
   end
@@ -544,7 +542,7 @@ end
 ---@return string %-escaped pattern string
 function vim.pesc(s)
   vim.validate({ s = { s, 's' } })
-  return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
+  return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
 end
 
 --- Tests if `s` starts with `prefix`.
-- 
cgit 


From 356244d50ba01c63b3ac33057a69462c4029612c Mon Sep 17 00:00:00 2001
From: Martin Kunz 
Date: Sat, 29 Oct 2022 01:13:27 +0200
Subject: fix(docs): nil as viable argument for goto_prev (#20852)

Added `nil` as a possible option to `vim.diagnostics.goto_prev` in the
docs
---
 runtime/lua/vim/diagnostic.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 84091fbf0e..af2d41bf7e 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -797,7 +797,7 @@ function M.get_prev_pos(opts)
 end
 
 --- Move to the previous diagnostic in the current buffer.
----@param opts table See |vim.diagnostic.goto_next()|
+---@param opts table|nil See |vim.diagnostic.goto_next()|
 function M.goto_prev(opts)
   return diagnostic_move_pos(opts, M.get_prev_pos(opts))
 end
-- 
cgit 


From e0dff29adc7690bb693ff05ff8776d07e756d4f9 Mon Sep 17 00:00:00 2001
From: lvimuser <109605931+lvimuser@users.noreply.github.com>
Date: Sun, 30 Oct 2022 06:35:22 -0300
Subject: fix(lsp/window_showDocument): correctly handle external resources
 #20867

- since cmd is a list, it runs directly ('no shell') and we shouldn't
  escape.
- typo: shellerror -> shell_error
---
 runtime/lua/vim/lsp/handlers.lua | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 93fd621161..c648a53555 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -519,15 +519,15 @@ M['window/showDocument'] = function(_, result, ctx, _)
     -- TODO(lvimuser): ask the user for confirmation
     local cmd
     if vim.fn.has('win32') == 1 then
-      cmd = { 'cmd.exe', '/c', 'start', '""', vim.fn.shellescape(uri) }
+      cmd = { 'cmd.exe', '/c', 'start', '""', uri }
     elseif vim.fn.has('macunix') == 1 then
-      cmd = { 'open', vim.fn.shellescape(uri) }
+      cmd = { 'open', uri }
     else
-      cmd = { 'xdg-open', vim.fn.shellescape(uri) }
+      cmd = { 'xdg-open', uri }
     end
 
     local ret = vim.fn.system(cmd)
-    if vim.v.shellerror ~= 0 then
+    if vim.v.shell_error ~= 0 then
       return {
         success = false,
         error = {
-- 
cgit 


From c7a88a470fa78b9e8a925b874752df473e90f6c3 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Sun, 30 Oct 2022 04:44:14 +0100
Subject: feat(checkhealth): improve treesitter report

---
 runtime/lua/vim/treesitter/health.lua | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 4995c80a02..1c9b58c1a8 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -1,5 +1,6 @@
 local M = {}
 local ts = vim.treesitter
+local health = require('vim.health')
 
 --- Lists the parsers currently installed
 ---
@@ -10,27 +11,21 @@ end
 
 --- Performs a healthcheck for treesitter integration
 function M.check()
-  local report_info = vim.fn['health#report_info']
-  local report_ok = vim.fn['health#report_ok']
-  local report_error = vim.fn['health#report_error']
   local parsers = M.list_parsers()
 
-  report_info(string.format('Runtime ABI version : %d', ts.language_version))
+  health.report_info(string.format('Nvim runtime ABI version: %d', ts.language_version))
 
   for _, parser in pairs(parsers) do
     local parsername = vim.fn.fnamemodify(parser, ':t:r')
-
     local is_loadable, ret = pcall(ts.language.require_language, parsername)
 
-    if not is_loadable then
-      report_error(string.format('Impossible to load parser for %s: %s', parsername, ret))
+    if not is_loadable or not ret then
+      health.report_error(string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?'))
     elseif ret then
       local lang = ts.language.inspect_language(parsername)
-      report_ok(
-        string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version)
+      health.report_ok(
+        string.format('Parser: %-10s ABI: %d, path: %s', parsername, lang._abi_version, parser)
       )
-    else
-      report_error(string.format('Unable to load parser for %s', parsername))
     end
   end
 end
-- 
cgit 


From cc7c378bf319d62491ca121ab598397428e4ced4 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Sun, 30 Oct 2022 06:41:28 +0100
Subject: feat(checkhealth): check runtime ($VIMRUNTIME)

Move man/health.lua into the "runtime" check.

fix #20696
---
 runtime/lua/vim/treesitter/health.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 1c9b58c1a8..c0a1eca0ce 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -20,7 +20,9 @@ function M.check()
     local is_loadable, ret = pcall(ts.language.require_language, parsername)
 
     if not is_loadable or not ret then
-      health.report_error(string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?'))
+      health.report_error(
+        string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?')
+      )
     elseif ret then
       local lang = ts.language.inspect_language(parsername)
       health.report_ok(
-- 
cgit 


From 850d7146fc7fb57691641c729d5580b834cd7dd2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 12:43:05 +0800
Subject: fix(paste): feed keys as typed in cmdline mode (#20959)

---
 runtime/lua/vim/_editor.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 0f312f19f5..0013f38d89 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -167,7 +167,8 @@ do
         local line1 = lines[1]:gsub('(%c)', '\022%1')
         -- nvim_input() is affected by mappings,
         -- so use nvim_feedkeys() with "n" flag to ignore mappings.
-        vim.api.nvim_feedkeys(line1, 'n', true)
+        -- "t" flag is also needed so the pasted text is saved in cmdline history.
+        vim.api.nvim_feedkeys(line1, 'nt', true)
       end
       return true
     end
-- 
cgit 


From 050b0e30b9d8a073a3b421a6cebd878226249ab6 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 7 Nov 2022 22:28:28 +0100
Subject: vim-patch:9.0.0843: VHS tape files are not recognized (#20995)

Problem:    VHS tape files are not recognized.
Solution:   Add a filetype pattern. (Carlos Alexandro Becker, closes vim/vim#11452)

https://github.com/vim/vim/commit/1756f4b21837e8596241ecd668f4abbbab4bc7e5

Co-authored-by: Carlos A Becker 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 56659eff04..5aa968e211 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1057,6 +1057,7 @@ local extension = {
   hdl = 'vhdl',
   vho = 'vhdl',
   vbe = 'vhdl',
+  tape = 'vhs',
   vim = 'vim',
   vba = 'vim',
   mar = 'vmasm',
-- 
cgit 


From 59ff4691f67fc1ddd3d1b7240a2f2eb095e58281 Mon Sep 17 00:00:00 2001
From: Jongwook Choi 
Date: Mon, 7 Nov 2022 19:15:15 -0500
Subject: fix(vim.ui.input): return empty string when inputs nothing (#20883)

fix(vim.ui.input): return empty string when inputs nothing

The previous behavior of `vim.ui.input()` when typing  with
no text input (with an intention of having the empty string as input)
was to execute `on_confirm(nil)`, conflicting with its documentation.

Inputting an empty string should now correctly execute `on_confirm('')`.
This should be clearly distinguished from cancelling or aborting the
input UI, in which case `on_confirm(nil)` is executed as before.
---
 runtime/lua/vim/ui.lua | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 6f1ce3089d..d9a3963afc 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -73,7 +73,8 @@ end
 ---               user inputs.
 ---@param on_confirm function ((input|nil) -> ())
 ---               Called once the user confirms or abort the input.
----               `input` is what the user typed.
+---               `input` is what the user typed (it might be
+---               an empty string if nothing was entered), or
 ---               `nil` if the user aborted the dialog.
 ---
 --- Example:
@@ -88,11 +89,17 @@ function M.input(opts, on_confirm)
   })
 
   opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict()
+
+  -- Note that vim.fn.input({}) returns an empty string when cancelled.
+  -- vim.ui.input() should distinguish aborting from entering an empty string.
+  local _canceled = vim.NIL
+  opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
+
   local input = vim.fn.input(opts)
-  if #input > 0 then
-    on_confirm(input)
-  else
+  if input == _canceled then
     on_confirm(nil)
+  else
+    on_confirm(input)
   end
 end
 
-- 
cgit 


From 07eb4263caa671de63186e9bbd650ec4b1fbce1a Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 28 Oct 2022 13:09:22 +0100
Subject: feat(spell): support nospell in treesitter queries

---
 runtime/lua/vim/treesitter/highlighter.lua | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 83a26aff13..f5e5ca1988 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -164,7 +164,7 @@ function TSHighlighter:get_query(lang)
 end
 
 ---@private
-local function on_line_impl(self, buf, line, spell)
+local function on_line_impl(self, buf, line, is_spell_nav)
   self.tree:for_each_tree(function(tstree, tree)
     if not tstree then
       return
@@ -201,17 +201,26 @@ local function on_line_impl(self, buf, line, spell)
       local start_row, start_col, end_row, end_col = node:range()
       local hl = highlighter_query.hl_cache[capture]
 
-      local is_spell = highlighter_query:query().captures[capture] == 'spell'
+      local capture_name = highlighter_query:query().captures[capture]
+      local spell = nil
+      if capture_name == 'spell' then
+        spell = true
+      elseif capture_name == 'nospell' then
+        spell = false
+      end
+
+      -- Give nospell a higher priority so it always overrides spell captures.
+      local spell_pri_offset = capture_name == 'nospell' and 1 or 0
 
-      if hl and end_row >= line and (not spell or is_spell) then
+      if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
         a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
           end_line = end_row,
           end_col = end_col,
           hl_group = hl,
           ephemeral = true,
-          priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
+          priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below
           conceal = metadata.conceal,
-          spell = is_spell,
+          spell = spell,
         })
       end
       if start_row > line then
-- 
cgit 


From b3f781ba912b0d24896d85fc8434faaedfddfeb2 Mon Sep 17 00:00:00 2001
From: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
Date: Sat, 12 Nov 2022 06:57:35 -0800
Subject: fix: vim.ui.input always calls callback #21006

Followup to #20883
Related: #18144

This patch changes the behavior of the default `vim.ui.input` when the user
aborts with ``. Currently, it produces an error message + stack and causes
`on_confirm` to not be called. With this patch, `` will cause `on_confirm`
to be called with `nil`, the same behavior as when the user aborts with ``.
I can think of three good reasons why the behavior should be this way:

1. Easier for the user to understand** It's not intuitive for there to be two
   ways to abort an input dialog that have _different_ outcomes. As a user,
   I would expect any action that cancels the input to leave me in the same
   state. As a plugin author, I see no value in having two possible outcomes for
   aborting the input. I have to handle both cases, but I can't think of
   a situation where I would want to treat one differently than the other.

2. Provides an API that can be overridden by other implementations** The current
   contract of "throw an error upon ``" cannot be replicated by async
   implementations of `vim.ui.input`. If the callsite wants to handle the case
   of the user hitting `` they need to use `pcall(vim.ui.input, ...)`,
   however an async implementation will instantly return and so there will be no
   way for it to produce the same error-throwing behavior when the user inputs
   ``. This makes it impossible to be fully API-compatible with the
   built-in `vim.ui.input`.

3. Provides a useful guarantee to the callsite** As a plugin author, I want the
   guarantee that `on_confirm` will _always_ be called (only catastrophic errors
   should prevent this). If I am in the middle of some async thread of logic,
   I need some way to resume that logic after handing off control to
   `vim.ui.input`. The only way to handle the `` case is with `pcall`,
   which as already mentioned, breaks down if you're using an alternative
   implementation.
---
 runtime/lua/vim/ui.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index d9a3963afc..97dccd83ab 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -95,8 +95,8 @@ function M.input(opts, on_confirm)
   local _canceled = vim.NIL
   opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
 
-  local input = vim.fn.input(opts)
-  if input == _canceled then
+  local ok, input = pcall(vim.fn.input, opts)
+  if not ok or input == _canceled then
     on_confirm(nil)
   else
     on_confirm(input)
-- 
cgit 


From e15f61b1bd60f6a198a0d9969cea407784ff71d0 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 14 Nov 2022 20:26:27 +0100
Subject: fix(lua): make `vim.deepcopy` work with `vim.NIL`

style: changed double quotes to single quotes

feat: add tests

fix tests
---
 runtime/lua/vim/shared.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f980547ae4..f4a57c13c8 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -49,6 +49,9 @@ vim.deepcopy = (function()
     if f then
       return f(orig, cache or {})
     else
+      if type(orig) == 'userdata' and orig == vim.NIL then
+        return vim.NIL
+      end
       error('Cannot deepcopy object of type ' .. type(orig))
     end
   end
-- 
cgit 


From f1922e78a1df1b1d32779769432fb5586edf5fbb Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sat, 5 Nov 2022 13:37:05 -0600
Subject: feat: add vim.secure.read()

This function accepts a path to a file and prompts the user if the file
is trusted. If the user confirms that the file is trusted, the contents
of the file are returned. The user's decision is stored in a trust
database at $XDG_STATE_HOME/nvim/trust. When this function is invoked
with a path that is already marked as trusted in the trust database, the
user is not prompted for a response.
---
 runtime/lua/vim/_editor.lua |   1 +
 runtime/lua/vim/secure.lua  | 106 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
 create mode 100644 runtime/lua/vim/secure.lua

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 0013f38d89..ad4dc20efb 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -36,6 +36,7 @@ for k, v in pairs({
   ui = true,
   health = true,
   fs = true,
+  secure = true,
 }) do
   vim._submodules[k] = v
 end
diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua
new file mode 100644
index 0000000000..341ff8df05
--- /dev/null
+++ b/runtime/lua/vim/secure.lua
@@ -0,0 +1,106 @@
+local M = {}
+
+--- Attempt to read the file at {path} prompting the user if the file should be
+--- trusted. The user's choice is persisted in a trust database at
+--- $XDG_STATE_HOME/nvim/trust.
+---
+---@param path (string) Path to a file to read.
+---
+---@return (string|nil) The contents of the given file if it exists and is
+---        trusted, or nil otherwise.
+function M.read(path)
+  vim.validate({ path = { path, 's' } })
+  local fullpath = vim.loop.fs_realpath(vim.fs.normalize(path))
+  if not fullpath then
+    return nil
+  end
+
+  local trust = {}
+  do
+    local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r')
+    if f then
+      local contents = f:read('*a')
+      if contents then
+        for line in vim.gsplit(contents, '\n') do
+          local hash, file = string.match(line, '^(%S+) (.+)$')
+          if hash and file then
+            trust[file] = hash
+          end
+        end
+      end
+      f:close()
+    end
+  end
+
+  if trust[fullpath] == '!' then
+    -- File is denied
+    return nil
+  end
+
+  local contents
+  do
+    local f = io.open(fullpath, 'r')
+    if not f then
+      return nil
+    end
+    contents = f:read('*a')
+    f:close()
+  end
+
+  local hash = vim.fn.sha256(contents)
+  if trust[fullpath] == hash then
+    -- File already exists in trust database
+    return contents
+  end
+
+  -- File either does not exist in trust database or the hash does not match
+  local choice = vim.fn.confirm(
+    string.format('%s is not trusted.', fullpath),
+    '&ignore\n&view\n&deny\n&allow',
+    1
+  )
+
+  if choice == 0 or choice == 1 then
+    -- Cancelled or ignored
+    return nil
+  elseif choice == 2 then
+    -- View
+    vim.cmd('new')
+    local buf = vim.api.nvim_get_current_buf()
+    local lines = vim.split(string.gsub(contents, '\n$', ''), '\n')
+    vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
+    vim.bo[buf].bufhidden = 'hide'
+    vim.bo[buf].buftype = 'nofile'
+    vim.bo[buf].swapfile = false
+    vim.bo[buf].modeline = false
+    vim.bo[buf].buflisted = false
+    vim.bo[buf].readonly = true
+    vim.bo[buf].modifiable = false
+    return nil
+  elseif choice == 3 then
+    -- Deny
+    trust[fullpath] = '!'
+    contents = nil
+  elseif choice == 4 then
+    -- Allow
+    trust[fullpath] = hash
+  end
+
+  do
+    local f, err = io.open(vim.fn.stdpath('state') .. '/trust', 'w')
+    if not f then
+      error(err)
+    end
+
+    local t = {}
+    for p, h in pairs(trust) do
+      t[#t + 1] = string.format('%s %s\n', h, p)
+    end
+    f:write(table.concat(t))
+    f:close()
+  end
+
+  return contents
+end
+
+return M
-- 
cgit 


From 523b1943c359cf79f29229a3d882c55ea407a237 Mon Sep 17 00:00:00 2001
From: Matthew Gramigna 
Date: Thu, 17 Nov 2022 18:39:31 -0500
Subject: vim-patch:9.0.0897: Clinical Quality Language files are not
 recognized (#21094)

Problem:    Clinical Quality Language files are not recognized.
Solution:   Add the "*.cql" pattern. (Matthew Gramigna, closes vim/vim#11452)

https://github.com/vim/vim/commit/12babe45a389cd1ea8befd5b06239e877b4abbba

Co-authored-by: mgramigna 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 5aa968e211..f06a5690a0 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -257,6 +257,7 @@ local extension = {
   cc = function(path, bufnr)
     return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp'
   end,
+  cql = 'cqlang',
   crm = 'crm',
   csx = 'cs',
   cs = 'cs',
-- 
cgit 


From af204dd0f193c3cd3154156c9f9fd40199b840c6 Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Sat, 19 Nov 2022 10:48:49 +0100
Subject: feat(lsp): run handler in coroutine to support async response
 (#21026)

To illustrate a use-case this also changes `window/showMessageRequest`
to use `vim.ui.select`
---
 runtime/lua/vim/lsp/handlers.lua | 44 ++++++++++++++++--------
 runtime/lua/vim/lsp/protocol.lua |  9 +++++
 runtime/lua/vim/lsp/rpc.lua      | 72 +++++++++++++++++++++-------------------
 3 files changed, 76 insertions(+), 49 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index c648a53555..c80adc6a87 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -81,22 +81,38 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
 end
 
 --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
+---@param result lsp.ShowMessageRequestParams
 M['window/showMessageRequest'] = function(_, result)
-  local actions = result.actions
-  print(result.message)
-  local option_strings = { result.message, '\nRequest Actions:' }
-  for i, action in ipairs(actions) do
-    local title = action.title:gsub('\r\n', '\\r\\n')
-    title = title:gsub('\n', '\\n')
-    table.insert(option_strings, string.format('%d. %s', i, title))
-  end
-
-  -- window/showMessageRequest can return either MessageActionItem[] or null.
-  local choice = vim.fn.inputlist(option_strings)
-  if choice < 1 or choice > #actions then
-    return vim.NIL
+  local actions = result.actions or {}
+  local co, is_main = coroutine.running()
+  if co and not is_main then
+    local opts = {
+      prompt = result.message .. ': ',
+      format_item = function(action)
+        return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n')
+      end,
+    }
+    vim.ui.select(actions, opts, function(choice)
+      -- schedule to ensure resume doesn't happen _before_ yield with
+      -- default synchronous vim.ui.select
+      vim.schedule(function()
+        coroutine.resume(co, choice or vim.NIL)
+      end)
+    end)
+    return coroutine.yield()
   else
-    return actions[choice]
+    local option_strings = { result.message, '\nRequest Actions:' }
+    for i, action in ipairs(actions) do
+      local title = action.title:gsub('\r\n', '\\r\\n')
+      title = title:gsub('\n', '\\n')
+      table.insert(option_strings, string.format('%d. %s', i, title))
+    end
+    local choice = vim.fn.inputlist(option_strings)
+    if choice < 1 or choice > #actions then
+      return vim.NIL
+    else
+      return actions[choice]
+    end
   end
 end
 
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 7442c8f005..8dc93b3b67 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -20,6 +20,14 @@ function transform_schema_to_table()
 end
 --]=]
 
+---@class lsp.ShowMessageRequestParams
+---@field type lsp.MessageType
+---@field message string
+---@field actions nil|lsp.MessageActionItem[]
+
+---@class lsp.MessageActionItem
+---@field title string
+
 local constants = {
   DiagnosticSeverity = {
     -- Reports an error.
@@ -39,6 +47,7 @@ local constants = {
     Deprecated = 2,
   },
 
+  ---@enum lsp.MessageType
   MessageType = {
     -- An error message.
     Error = 1,
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index ff62623544..b93b227150 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -391,44 +391,46 @@ function Client:handle_body(body)
     -- Schedule here so that the users functions don't trigger an error and
     -- we can still use the result.
     schedule(function()
-      local status, result
-      status, result, err = self:try_call(
-        client_errors.SERVER_REQUEST_HANDLER_ERROR,
-        self.dispatchers.server_request,
-        decoded.method,
-        decoded.params
-      )
-      local _ = log.debug()
-        and log.debug(
-          'server_request: callback result',
-          { status = status, result = result, err = err }
+      coroutine.wrap(function()
+        local status, result
+        status, result, err = self:try_call(
+          client_errors.SERVER_REQUEST_HANDLER_ERROR,
+          self.dispatchers.server_request,
+          decoded.method,
+          decoded.params
         )
-      if status then
-        if result == nil and err == nil then
-          error(
-            string.format(
-              'method %q: either a result or an error must be sent to the server in response',
-              decoded.method
-            )
-          )
-        end
-        if err then
-          assert(
-            type(err) == 'table',
-            'err must be a table. Use rpc_response_error to help format errors.'
-          )
-          local code_name = assert(
-            protocol.ErrorCodes[err.code],
-            'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+        local _ = log.debug()
+          and log.debug(
+            'server_request: callback result',
+            { status = status, result = result, err = err }
           )
-          err.message = err.message or code_name
+        if status then
+          if result == nil and err == nil then
+            error(
+              string.format(
+                'method %q: either a result or an error must be sent to the server in response',
+                decoded.method
+              )
+            )
+          end
+          if err then
+            assert(
+              type(err) == 'table',
+              'err must be a table. Use rpc_response_error to help format errors.'
+            )
+            local code_name = assert(
+              protocol.ErrorCodes[err.code],
+              'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+            )
+            err.message = err.message or code_name
+          end
+        else
+          -- On an exception, result will contain the error message.
+          err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
+          result = nil
         end
-      else
-        -- On an exception, result will contain the error message.
-        err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
-        result = nil
-      end
-      self:send_response(decoded.id, err, result)
+        self:send_response(decoded.id, err, result)
+      end)()
     end)
     -- This works because we are expecting vim.NIL here
   elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
-- 
cgit 


From cfdf5e6f372928c11a2b1459b14c4c2de5f69c51 Mon Sep 17 00:00:00 2001
From: Grzegorz Rozdzialik 
Date: Sat, 19 Nov 2022 12:27:00 +0100
Subject: fix(lsp): ignore hover and signatureHelp responses on buffer change
 (#21121)

Language servers can take some time to respond to the
`textDocument/hover` and `textDocument/signatureHelp` messages. During
that time, the user could have already moved to another buffer. The
popup was always shown in the current buffer, which could be a different
one than the buffer for which the request was sent.

This was particularly annoying when moving to a buffer with a `BufLeave`
autocmd, as that autocmd was triggered when the hover popup was shown
for the original buffer.

Ignoring the response from these 2 messages if they are for a buffer
that is not the current one leads to less noise. The popup will only be
shown for the buffer for which it was requested.

A more robust solution could involve cancelling the hover/signatureHelp
request if the buffer changes so the language server can free its
resources. It could be implemented in the future.
---
 runtime/lua/vim/lsp/handlers.lua | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index c80adc6a87..8e5e75232f 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -328,6 +328,10 @@ end
 function M.hover(_, result, ctx, config)
   config = config or {}
   config.focus_id = ctx.method
+  if api.nvim_get_current_buf() ~= ctx.bufnr then
+    -- Ignore result since buffer changed. This happens for slow language servers.
+    return
+  end
   if not (result and result.contents) then
     vim.notify('No information available')
     return
@@ -408,6 +412,10 @@ M['textDocument/implementation'] = location_handler
 function M.signature_help(_, result, ctx, config)
   config = config or {}
   config.focus_id = ctx.method
+  if api.nvim_get_current_buf() ~= ctx.bufnr then
+    -- Ignore result since buffer changed. This happens for slow language servers.
+    return
+  end
   -- When use `autocmd CompleteDone  lua vim.lsp.buf.signature_help()` to call signatureHelp handler
   -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `` to ignore
   if not (result and result.signatures and result.signatures[1]) then
-- 
cgit 


From 6e8ed5abaa9c33d1d78ab7ff5b07dd5bac623a1d Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Sat, 19 Nov 2022 21:41:47 +0800
Subject: perf(diagnostic): use api variable and improve validate (#21111)

* fix(diagnostic): use api variable and improve validate

* fix: fix test case
---
 runtime/lua/vim/diagnostic.lua | 96 ++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 50 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index af2d41bf7e..09eeb617f5 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1,4 +1,4 @@
-local if_nil = vim.F.if_nil
+local api, if_nil = vim.api, vim.F.if_nil
 
 local M = {}
 
@@ -47,11 +47,11 @@ local bufnr_and_namespace_cacher_mt = {
 
 local diagnostic_cache
 do
-  local group = vim.api.nvim_create_augroup('DiagnosticBufWipeout', {})
+  local group = api.nvim_create_augroup('DiagnosticBufWipeout', {})
   diagnostic_cache = setmetatable({}, {
     __index = function(t, bufnr)
       assert(bufnr > 0, 'Invalid buffer number')
-      vim.api.nvim_create_autocmd('BufWipeout', {
+      api.nvim_create_autocmd('BufWipeout', {
         group = group,
         buffer = bufnr,
         callback = function()
@@ -245,7 +245,7 @@ end)()
 ---@private
 local function get_bufnr(bufnr)
   if not bufnr or bufnr == 0 then
-    return vim.api.nvim_get_current_buf()
+    return api.nvim_get_current_buf()
   end
   return bufnr
 end
@@ -299,7 +299,7 @@ end
 ---@private
 local function restore_extmarks(bufnr, last)
   for ns, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do
-    local extmarks_current = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, { details = true })
+    local extmarks_current = api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, { details = true })
     local found = {}
     for _, extmark in ipairs(extmarks_current) do
       -- nvim_buf_set_lines will move any extmark to the line after the last
@@ -312,7 +312,7 @@ local function restore_extmarks(bufnr, last)
       if not found[extmark[1]] then
         local opts = extmark[4]
         opts.id = extmark[1]
-        pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
+        pcall(api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
       end
     end
   end
@@ -322,7 +322,7 @@ end
 local function save_extmarks(namespace, bufnr)
   bufnr = get_bufnr(bufnr)
   if not diagnostic_attached_buffers[bufnr] then
-    vim.api.nvim_buf_attach(bufnr, false, {
+    api.nvim_buf_attach(bufnr, false, {
       on_lines = function(_, _, _, _, _, last)
         restore_extmarks(bufnr, last - 1)
       end,
@@ -333,7 +333,7 @@ local function save_extmarks(namespace, bufnr)
     diagnostic_attached_buffers[bufnr] = true
   end
   diagnostic_cache_extmarks[bufnr][namespace] =
-    vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
+    api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
 end
 
 local registered_autocmds = {}
@@ -366,8 +366,8 @@ local function schedule_display(namespace, bufnr, args)
 
   local key = make_augroup_key(namespace, bufnr)
   if not registered_autocmds[key] then
-    local group = vim.api.nvim_create_augroup(key, { clear = true })
-    vim.api.nvim_create_autocmd(insert_leave_auto_cmds, {
+    local group = api.nvim_create_augroup(key, { clear = true })
+    api.nvim_create_autocmd(insert_leave_auto_cmds, {
       group = group,
       buffer = bufnr,
       callback = function()
@@ -384,7 +384,7 @@ local function clear_scheduled_display(namespace, bufnr)
   local key = make_augroup_key(namespace, bufnr)
 
   if registered_autocmds[key] then
-    vim.api.nvim_del_augroup_by_name(key)
+    api.nvim_del_augroup_by_name(key)
     registered_autocmds[key] = nil
   end
 end
@@ -399,7 +399,7 @@ local function get_diagnostics(bufnr, opts, clamp)
   -- Memoized results of buf_line_count per bufnr
   local buf_line_count = setmetatable({}, {
     __index = function(t, k)
-      t[k] = vim.api.nvim_buf_line_count(k)
+      t[k] = api.nvim_buf_line_count(k)
       return rawget(t, k)
     end,
   })
@@ -407,7 +407,7 @@ local function get_diagnostics(bufnr, opts, clamp)
   ---@private
   local function add(b, d)
     if not opts.lnum or d.lnum == opts.lnum then
-      if clamp and vim.api.nvim_buf_is_loaded(b) then
+      if clamp and api.nvim_buf_is_loaded(b) then
         local line_count = buf_line_count[b] - 1
         if
           d.lnum > line_count
@@ -471,7 +471,7 @@ local function set_list(loclist, opts)
   local winnr = opts.winnr or 0
   local bufnr
   if loclist then
-    bufnr = vim.api.nvim_win_get_buf(winnr)
+    bufnr = api.nvim_win_get_buf(winnr)
   end
   -- Don't clamp line numbers since the quickfix list can already handle line
   -- numbers beyond the end of the buffer
@@ -483,7 +483,7 @@ local function set_list(loclist, opts)
     vim.fn.setqflist({}, ' ', { title = title, items = items })
   end
   if open then
-    vim.api.nvim_command(loclist and 'lopen' or 'botright copen')
+    api.nvim_command(loclist and 'lopen' or 'botright copen')
   end
 end
 
@@ -492,7 +492,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
   position[1] = position[1] - 1
   bufnr = get_bufnr(bufnr)
   local wrap = vim.F.if_nil(opts.wrap, true)
-  local line_count = vim.api.nvim_buf_line_count(bufnr)
+  local line_count = api.nvim_buf_line_count(bufnr)
   local diagnostics =
     get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
   local line_diagnostics = diagnostic_lines(diagnostics)
@@ -506,7 +506,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
       lnum = (lnum + line_count) % line_count
     end
     if line_diagnostics[lnum] and not vim.tbl_isempty(line_diagnostics[lnum]) then
-      local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
+      local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
       local sort_diagnostics, is_next
       if search_forward then
         sort_diagnostics = function(a, b)
@@ -542,17 +542,17 @@ local function diagnostic_move_pos(opts, pos)
   opts = opts or {}
 
   local float = vim.F.if_nil(opts.float, true)
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
+  local win_id = opts.win_id or api.nvim_get_current_win()
 
   if not pos then
-    vim.api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
+    api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
     return
   end
 
-  vim.api.nvim_win_call(win_id, function()
+  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] })
+    api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
     -- Open folds under the cursor
     vim.cmd('normal! zv')
   end)
@@ -561,7 +561,7 @@ local function diagnostic_move_pos(opts, pos)
     local float_opts = type(float) == 'table' and float or {}
     vim.schedule(function()
       M.open_float(vim.tbl_extend('keep', float_opts, {
-        bufnr = vim.api.nvim_win_get_buf(win_id),
+        bufnr = api.nvim_win_get_buf(win_id),
         scope = 'cursor',
         focus = false,
       }))
@@ -666,13 +666,13 @@ function M.config(opts, namespace)
 
   if namespace then
     for bufnr, v in pairs(diagnostic_cache) do
-      if vim.api.nvim_buf_is_loaded(bufnr) and v[namespace] then
+      if api.nvim_buf_is_loaded(bufnr) and v[namespace] then
         M.show(namespace, bufnr)
       end
     end
   else
     for bufnr, v in pairs(diagnostic_cache) do
-      if vim.api.nvim_buf_is_loaded(bufnr) then
+      if api.nvim_buf_is_loaded(bufnr) then
         for ns in pairs(v) do
           M.show(ns, bufnr)
         end
@@ -707,11 +707,11 @@ function M.set(namespace, bufnr, diagnostics, opts)
     set_diagnostic_cache(namespace, bufnr, diagnostics)
   end
 
-  if vim.api.nvim_buf_is_loaded(bufnr) then
+  if api.nvim_buf_is_loaded(bufnr) then
     M.show(namespace, bufnr, nil, opts)
   end
 
-  vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+  api.nvim_exec_autocmds('DiagnosticChanged', {
     modeline = false,
     buffer = bufnr,
     data = { diagnostics = diagnostics },
@@ -726,7 +726,7 @@ function M.get_namespace(namespace)
   vim.validate({ namespace = { namespace, 'n' } })
   if not all_namespaces[namespace] then
     local name
-    for k, v in pairs(vim.api.nvim_get_namespaces()) do
+    for k, v in pairs(api.nvim_get_namespaces()) do
       if namespace == v then
         name = k
         break
@@ -776,9 +776,9 @@ end
 function M.get_prev(opts)
   opts = opts or {}
 
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
-  local bufnr = vim.api.nvim_win_get_buf(win_id)
-  local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+  local win_id = opts.win_id or api.nvim_get_current_win()
+  local bufnr = api.nvim_win_get_buf(win_id)
+  local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
 
   return next_diagnostic(cursor_position, false, bufnr, opts, opts.namespace)
 end
@@ -809,9 +809,9 @@ end
 function M.get_next(opts)
   opts = opts or {}
 
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
-  local bufnr = vim.api.nvim_win_get_buf(win_id)
-  local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+  local win_id = opts.win_id or api.nvim_get_current_win()
+  local bufnr = api.nvim_win_get_buf(win_id)
+  local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
 
   return next_diagnostic(cursor_position, true, bufnr, opts, opts.namespace)
 end
@@ -931,7 +931,7 @@ M.handlers.underline = {
 
     local ns = M.get_namespace(namespace)
     if not ns.user_data.underline_ns then
-      ns.user_data.underline_ns = vim.api.nvim_create_namespace('')
+      ns.user_data.underline_ns = api.nvim_create_namespace('')
     end
 
     local underline_ns = ns.user_data.underline_ns
@@ -958,7 +958,7 @@ M.handlers.underline = {
     local ns = M.get_namespace(namespace)
     if ns.user_data.underline_ns then
       diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
-      vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
+      api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
     end
   end,
 }
@@ -997,7 +997,7 @@ M.handlers.virtual_text = {
 
     local ns = M.get_namespace(namespace)
     if not ns.user_data.virt_text_ns then
-      ns.user_data.virt_text_ns = vim.api.nvim_create_namespace('')
+      ns.user_data.virt_text_ns = api.nvim_create_namespace('')
     end
 
     local virt_text_ns = ns.user_data.virt_text_ns
@@ -1009,7 +1009,7 @@ M.handlers.virtual_text = {
       local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text)
 
       if virt_texts then
-        vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
+        api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
           hl_mode = 'combine',
           virt_text = virt_texts,
         })
@@ -1021,7 +1021,7 @@ M.handlers.virtual_text = {
     local ns = M.get_namespace(namespace)
     if ns.user_data.virt_text_ns then
       diagnostic_cache_extmarks[bufnr][ns.user_data.virt_text_ns] = {}
-      vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
+      api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
     end
   end,
 }
@@ -1153,7 +1153,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
   if opts.update_in_insert then
     clear_scheduled_display(namespace, bufnr)
   else
-    local mode = vim.api.nvim_get_mode()
+    local mode = api.nvim_get_mode()
     if string.sub(mode.mode, 1, 1) == 'i' then
       schedule_display(namespace, bufnr, opts)
       return
@@ -1251,7 +1251,7 @@ function M.open_float(opts, ...)
   local lnum, col
   if scope == 'line' or scope == 'cursor' then
     if not opts.pos then
-      local pos = vim.api.nvim_win_get_cursor(0)
+      local pos = api.nvim_win_get_cursor(0)
       lnum = pos[1] - 1
       col = pos[2]
     elseif type(opts.pos) == 'number' then
@@ -1273,7 +1273,7 @@ function M.open_float(opts, ...)
     end, diagnostics)
   elseif scope == 'cursor' then
     -- LSP servers can send diagnostics with `end_col` past the length of the line
-    local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
+    local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
     diagnostics = vim.tbl_filter(function(d)
       return d.lnum == lnum
         and math.min(d.col, line_length - 1) <= col
@@ -1305,9 +1305,7 @@ function M.open_float(opts, ...)
     vim.validate({
       header = {
         header,
-        function(v)
-          return type(v) == 'string' or type(v) == 'table'
-        end,
+        { 'string', 'table' },
         "'string' or 'table'",
       },
     })
@@ -1341,9 +1339,7 @@ function M.open_float(opts, ...)
     vim.validate({
       prefix = {
         prefix_opt,
-        function(v)
-          return type(v) == 'string' or type(v) == 'table' or type(v) == 'function'
-        end,
+        { 'string', 'table', 'function' },
         "'string' or 'table' or 'function'",
       },
     })
@@ -1377,9 +1373,9 @@ function M.open_float(opts, ...)
   for i, hi in ipairs(highlights) do
     local prefixlen, hiname, prefix_hiname = unpack(hi)
     if prefix_hiname then
-      vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
+      api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
     end
-    vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
+    api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
   end
 
   return float_bufnr, winnr
@@ -1410,7 +1406,7 @@ function M.reset(namespace, bufnr)
       M.hide(iter_namespace, iter_bufnr)
     end
 
-    vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+    api.nvim_exec_autocmds('DiagnosticChanged', {
       modeline = false,
       buffer = iter_bufnr,
       data = { diagnostics = {} },
-- 
cgit 


From fbce9f421ad1ad466126a24524ade9df978486d5 Mon Sep 17 00:00:00 2001
From: beardedsakimonkey <54521218+beardedsakimonkey@users.noreply.github.com>
Date: Sun, 20 Nov 2022 20:09:35 +0000
Subject: feat(diagnostic): add `suffix` option to `open_float()` (#21130)

Closes #18687

This introduces a `suffix` option to `vim.diagnostic.open_float()` (and
consequently `vim.diagnostic.config()`) that appends some text to each
diagnostic in the float.

It accepts the same types as `prefix`. For multiline diagnostics, the suffix is
only appended to the last line. By default, the suffix will render the
diagnostic error code, if any.
---
 runtime/lua/vim/diagnostic.lua | 65 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 53 insertions(+), 12 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 09eeb617f5..008b00a9a0 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1220,6 +1220,8 @@ end
 ---                      string, it is prepended to each diagnostic in the window with no
 ---                      highlight.
 ---                      Overrides the setting from |vim.diagnostic.config()|.
+---            - suffix: Same as {prefix}, but appends the text to the diagnostic instead of
+---                      prepending it. Overrides the setting from |vim.diagnostic.config()|.
 ---@return tuple ({float_bufnr}, {win_id})
 function M.open_float(opts, ...)
   -- Support old (bufnr, opts) signature
@@ -1313,11 +1315,11 @@ function M.open_float(opts, ...)
       -- Don't insert any lines for an empty string
       if string.len(if_nil(header[1], '')) > 0 then
         table.insert(lines, header[1])
-        table.insert(highlights, { 0, header[2] or 'Bold' })
+        table.insert(highlights, { hlname = header[2] or 'Bold' })
       end
     elseif #header > 0 then
       table.insert(lines, header)
-      table.insert(highlights, { 0, 'Bold' })
+      table.insert(highlights, { hlname = 'Bold' })
     end
   end
 
@@ -1350,18 +1352,52 @@ function M.open_float(opts, ...)
     end
   end
 
+  local suffix_opt = if_nil(opts.suffix, function(diagnostic)
+    return diagnostic.code and string.format(' [%s]', diagnostic.code) or ''
+  end)
+
+  local suffix, suffix_hl_group
+  if suffix_opt then
+    vim.validate({
+      suffix = {
+        suffix_opt,
+        { 'string', 'table', 'function' },
+        "'string' or 'table' or 'function'",
+      },
+    })
+    if type(suffix_opt) == 'string' then
+      suffix, suffix_hl_group = suffix_opt, 'NormalFloat'
+    elseif type(suffix_opt) == 'table' then
+      suffix, suffix_hl_group = suffix_opt[1] or '', suffix_opt[2] or 'NormalFloat'
+    end
+  end
+
   for i, diagnostic in ipairs(diagnostics) do
     if prefix_opt and type(prefix_opt) == 'function' then
       prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
       prefix, prefix_hl_group = prefix or '', prefix_hl_group or 'NormalFloat'
     end
+    if suffix_opt and type(suffix_opt) == 'function' then
+      suffix, suffix_hl_group = suffix_opt(diagnostic, i, #diagnostics)
+      suffix, suffix_hl_group = suffix or '', suffix_hl_group or 'NormalFloat'
+    end
     local hiname = floating_highlight_map[diagnostic.severity]
     local message_lines = vim.split(diagnostic.message, '\n')
-    table.insert(lines, prefix .. message_lines[1])
-    table.insert(highlights, { #prefix, hiname, prefix_hl_group })
-    for j = 2, #message_lines do
-      table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
-      table.insert(highlights, { 0, hiname })
+    for j = 1, #message_lines do
+      local pre = j == 1 and prefix or string.rep(' ', #prefix)
+      local suf = j == #message_lines and suffix or ''
+      table.insert(lines, pre .. message_lines[j] .. suf)
+      table.insert(highlights, {
+        hlname = hiname,
+        prefix = {
+          length = j == 1 and #prefix or 0,
+          hlname = prefix_hl_group,
+        },
+        suffix = {
+          length = j == #message_lines and #suffix or 0,
+          hlname = suffix_hl_group,
+        },
+      })
     end
   end
 
@@ -1370,12 +1406,17 @@ function M.open_float(opts, ...)
     opts.focus_id = scope
   end
   local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts)
-  for i, hi in ipairs(highlights) do
-    local prefixlen, hiname, prefix_hiname = unpack(hi)
-    if prefix_hiname then
-      api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
+  for i, hl in ipairs(highlights) do
+    local line = lines[i]
+    local prefix_len = hl.prefix and hl.prefix.length or 0
+    local suffix_len = hl.suffix and hl.suffix.length or 0
+    if prefix_len > 0 then
+      api.nvim_buf_add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len)
+    end
+    api.nvim_buf_add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len)
+    if suffix_len > 0 then
+      api.nvim_buf_add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1)
     end
-    api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
   end
 
   return float_bufnr, winnr
-- 
cgit 


From 126ef65e5b3ff0da68bfe166be0bb0a33664142b Mon Sep 17 00:00:00 2001
From: beardedsakimonkey <54521218+beardedsakimonkey@users.noreply.github.com>
Date: Sun, 20 Nov 2022 23:57:36 +0000
Subject: feat(diagnostic): add `suffix` option to `virt_text` config (#21140)

This introduces a `suffix` option to the `virt_text` config in
`vim.diagnostic.config()`. The suffix can either be a string which is appended
to the diagnostic message or a function returning such. The function receives a
`diagnostic` argument, which is the diagnostic table of the last diagnostic (the
one whose message is rendered as virt text).
---
 runtime/lua/vim/diagnostic.lua | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 008b00a9a0..a7fc47a5a8 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -613,6 +613,10 @@ end
 ---                       * spacing: (number) Amount of empty spaces inserted at the beginning
 ---                                  of the virtual text.
 ---                       * prefix: (string) Prepend diagnostic message with prefix.
+---                       * suffix: (string or function) Append diagnostic message with suffix.
+---                                 If a function, it must have the signature (diagnostic) ->
+---                                 string, where {diagnostic} is of type |diagnostic-structure|.
+---                                 This can be used to render an LSP diagnostic error code.
 ---                       * 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:
@@ -1039,6 +1043,7 @@ function M._get_virt_text_chunks(line_diags, opts)
 
   opts = opts or {}
   local prefix = opts.prefix or '■'
+  local suffix = opts.suffix or ''
   local spacing = opts.spacing or 4
 
   -- Create a little more space between virtual text and contents
@@ -1052,8 +1057,11 @@ function M._get_virt_text_chunks(line_diags, opts)
   -- TODO(tjdevries): Allow different servers to be shown first somehow?
   -- TODO(tjdevries): Display server name associated with these?
   if last.message then
+    if type(suffix) == 'function' then
+      suffix = suffix(last) or ''
+    end
     table.insert(virt_texts, {
-      string.format('%s %s', prefix, last.message:gsub('\r', ''):gsub('\n', '  ')),
+      string.format('%s %s%s', prefix, last.message:gsub('\r', ''):gsub('\n', '  '), suffix),
       virtual_text_highlight_map[last.severity],
     })
 
-- 
cgit 


From 2bb244af314e80afbab30b4db4490c8dae894b85 Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Mon, 21 Nov 2022 18:06:14 +0800
Subject: feat(lsp): support set title in lsp relate floatwindow (#21110)

---
 runtime/lua/vim/lsp/handlers.lua | 4 +++-
 runtime/lua/vim/lsp/util.lua     | 9 +++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 8e5e75232f..39e2577294 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -317,7 +317,9 @@ end
 --- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
 ---   vim.lsp.handlers.hover, {
 ---     -- Use a sharp border with `FloatBorder` highlights
----     border = "single"
+---     border = "single",
+---     -- add the title in hover float window
+---     title = "hover"
 ---   }
 --- )
 --- 
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b0f9c1660e..d89757ef0c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1050,6 +1050,13 @@ function M.make_floating_popup_options(width, height, opts) col = 1 end + local title = (opts.border and opts.title) and opts.title or nil + local title_pos + + if title then + title_pos = opts.title_pos or 'center' + end + return { anchor = anchor, col = col + (opts.offset_x or 0), @@ -1061,6 +1068,8 @@ function M.make_floating_popup_options(width, height, opts) width = width, border = opts.border or default_border, zindex = opts.zindex or 50, + title = title, + title_pos = title_pos, } end -- cgit From 904d0056d520ef01e2873bbaa91a4693e8dfd226 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 21 Nov 2022 22:02:18 +0100 Subject: fix(diagnostic): correct type annotations; add Diagnostic type (#21120) Some functions didn't include the `nil` case in the return type annotation. This corrects those and also adds a Diagnostic class definition for the diagnostic.get return type --- runtime/lua/vim/diagnostic.lua | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index a7fc47a5a8..18df1f1586 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -2,6 +2,7 @@ local api, if_nil = vim.api, vim.F.if_nil local M = {} +---@enum DiagnosticSeverity M.severity = { ERROR = 1, WARN = 2, @@ -755,6 +756,18 @@ function M.get_namespaces() return vim.deepcopy(all_namespaces) end +---@class Diagnostic +---@field buffer number +---@field lnum number 0-indexed +---@field end_lnum nil|number 0-indexed +---@field col number 0-indexed +---@field end_col nil|number 0-indexed +---@field severity DiagnosticSeverity +---@field message string +---@field source nil|string +---@field code nil|string +---@field user_data nil|any arbitrary data plugins can add + --- Get current diagnostics. --- ---@param bufnr number|nil Buffer number to get diagnostics from. Use 0 for @@ -763,7 +776,7 @@ end --- - namespace: (number) Limit diagnostics to the given namespace. --- - lnum: (number) Limit diagnostics to the given line number. --- - severity: See |diagnostic-severity|. ----@return table A list of diagnostic items |diagnostic-structure|. +---@return Diagnostic[] table A list of diagnostic items |diagnostic-structure|. function M.get(bufnr, opts) vim.validate({ bufnr = { bufnr, 'n', true }, @@ -775,8 +788,8 @@ end --- Get the previous diagnostic closest to the cursor position. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Previous diagnostic +---@param opts nil|table See |vim.diagnostic.goto_next()| +---@return Diagnostic|nil Previous diagnostic function M.get_prev(opts) opts = opts or {} @@ -789,8 +802,9 @@ end --- Return the position of the previous diagnostic in the current buffer. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Previous diagnostic position as a (row, col) tuple. +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return table|false Previous diagnostic position as a (row, col) tuple or false if there is no +--- prior diagnostic function M.get_prev_pos(opts) local prev = M.get_prev(opts) if not prev then @@ -808,8 +822,8 @@ end --- Get the next diagnostic closest to the cursor position. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Next diagnostic +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return Diagnostic|nil Next diagnostic function M.get_next(opts) opts = opts or {} @@ -822,8 +836,9 @@ end --- Return the position of the next diagnostic in the current buffer. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Next diagnostic position as a (row, col) tuple. +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return table|false Next diagnostic position as a (row, col) tuple or false if no next +--- diagnostic. function M.get_next_pos(opts) local next = M.get_next(opts) if not next then @@ -1230,7 +1245,7 @@ end --- Overrides the setting from |vim.diagnostic.config()|. --- - suffix: Same as {prefix}, but appends the text to the diagnostic instead of --- prepending it. Overrides the setting from |vim.diagnostic.config()|. ----@return tuple ({float_bufnr}, {win_id}) +---@return number|nil, number|nil: ({float_bufnr}, {win_id}) function M.open_float(opts, ...) -- Support old (bufnr, opts) signature local bufnr @@ -1578,7 +1593,7 @@ end ---@param defaults table|nil Table of default values for any fields not listed in {groups}. --- When omitted, numeric values default to 0 and "severity" defaults to --- ERROR. ----@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. +---@return Diagnostic|nil: |diagnostic-structure| or `nil` if {pat} fails to match {str}. function M.match(str, pat, groups, severity_map, defaults) vim.validate({ str = { str, 's' }, @@ -1625,7 +1640,7 @@ local errlist_type_map = { --- passed to |setqflist()| or |setloclist()|. --- ---@param diagnostics table List of diagnostics |diagnostic-structure|. ----@return array of quickfix list items |setqflist-what| +---@return table[] of quickfix list items |setqflist-what| function M.toqflist(diagnostics) vim.validate({ diagnostics = { @@ -1662,7 +1677,7 @@ end --- ---@param list table A list of quickfix items from |getqflist()| or --- |getloclist()|. ----@return array of diagnostics |diagnostic-structure| +---@return Diagnostic[] array of |diagnostic-structure| function M.fromqflist(list) vim.validate({ list = { -- cgit From d41e93d5a83642f90898cae211e017d99ff97fd9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 22 Nov 2022 23:28:49 +0100 Subject: vim-patch:9.0.0922: Mermaid files are not recognized (#21160) Problem: Mermaid files are not recognized. Solution: Add patterns for Mermaid. (Crag MacEachern) https://github.com/vim/vim/commit/364438d1e817d1d76003695f9ab533df35f8948a Co-authored-by: Bram Moolenaar --- runtime/lua/vim/filetype.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index f06a5690a0..1cc9beecc6 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -659,6 +659,9 @@ local extension = { dmt = 'maxima', wxm = 'maxima', mel = 'mel', + mmd = 'mermaid', + mmdc = 'mermaid', + mermaid = 'mermaid', mf = 'mf', mgl = 'mgl', mgp = 'mgp', -- cgit From ddea80ebd66617bfc3a1af0b08d55dd7ed51f2ca Mon Sep 17 00:00:00 2001 From: AzerAfram <97570339+AzerAfram@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:40:07 -0800 Subject: docs(lua): add clarifications for fs.find() and fs.normalize() (#21132) Co-Authored-By: Gregory Anders <8965202+gpanders@users.noreply.github.com> Co-Authored-By: zeertzjq --- runtime/lua/vim/fs.lua | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 7bd635d8b6..c7c053852d 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -79,11 +79,12 @@ end ---@param names (string|table|fun(name: string): boolean) Names of the files --- and directories to find. --- Must be base names, paths and globs are not supported. ---- If a function it is called per file and dir within the ---- traversed directories to test if they match. +--- The function is called per file and directory within the +--- traversed directories to test if they match {names}. +--- ---@param opts (table) Optional keyword arguments: --- - path (string): Path to begin searching from. If ---- omitted, the current working directory is used. +--- omitted, the |current-directory| is used. --- - upward (boolean, default false): If true, search --- upward through parent directories. Otherwise, --- search through child directories @@ -92,12 +93,13 @@ end --- reached. The directory itself is not searched. --- - type (string): Find only files ("file") or --- directories ("directory"). If omitted, both ---- files and directories that match {name} are +--- files and directories that match {names} are --- included. --- - limit (number, default 1): Stop the search after --- finding this many matches. Use `math.huge` to --- place no limit on the number of matches. ----@return (table) The paths of all matching files or directories +--- +---@return (table) The normalized paths |vim.fs.normalize()| of all matching files or directories function M.find(names, opts) opts = opts or {} vim.validate({ @@ -211,16 +213,16 @@ end --- backslash (\\) characters are converted to forward slashes (/). Environment --- variables are also expanded. --- ---- Example: +--- Examples: ---
---- vim.fs.normalize('C:\\Users\\jdoe')
---- => 'C:/Users/jdoe'
+---   vim.fs.normalize('C:\\Users\\jdoe')
+---   => 'C:/Users/jdoe'
 ---
---- vim.fs.normalize('~/src/neovim')
---- => '/home/jdoe/src/neovim'
+---   vim.fs.normalize('~/src/neovim')
+---   => '/home/jdoe/src/neovim'
 ---
---- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
---- => '/Users/jdoe/.config/nvim/init.vim'
+---   vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+---   => '/Users/jdoe/.config/nvim/init.vim'
 --- 
--- ---@param path (string) Path to normalize -- cgit From 5ca1f48b4021e25d97bc0a29644310be4db5f70d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 25 Nov 2022 09:17:08 +0100 Subject: vim-patch:9.0.0942: Workflow Description Language files are not recognized (#21183) Problem: Workflow Description Language files are not recognized. Solution: Add a pattern for the "wdl" filetype. (Matt Dunford, closes vim/vim#11611) https://github.com/vim/vim/commit/f60bdc3417a56a1f69e001a7ec210b92d5b0f2e1 Co-authored-by: Matt Dunford --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 1cc9beecc6..ced5bb97a9 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1071,6 +1071,7 @@ local extension = { vue = 'vue', wat = 'wast', wast = 'wast', + wdl = 'wdl', wm = 'webmacro', wbt = 'winbatch', wml = 'wml', -- cgit From 7875e1377c24699a3773609996c4e21320a09f31 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 25 Nov 2022 16:17:30 +0800 Subject: vim-patch:9.0.0932: Oblivion files are not recognized (#21179) Problem: Oblivion files are not recognized. Solution: Recognize Oblivion files and alike as "obse". (closes vim/vim#11540) https://github.com/vim/vim/commit/ecfd511e8d802068434735dda00db6b783df6922 Co-authored-by: Bram Moolenaar --- runtime/lua/vim/filetype.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index ced5bb97a9..5e086c8abf 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -729,6 +729,10 @@ local extension = { nsi = 'nsis', nsh = 'nsis', obj = 'obj', + obl = 'obse', + obse = 'obse', + oblivion = 'obse', + obscript = 'obse', mlt = 'ocaml', mly = 'ocaml', mll = 'ocaml', -- cgit From 29b80f6f2e9391b5d78390f65d09f00f73829310 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 25 Nov 2022 10:08:15 +0100 Subject: vim-patch:9.0.0935: when using dash it may not be recognize as filetype "sh" (#21174) * vim-patch:9.0.0935: when using dash it may not be recognize as filetype "sh" Problem: When using dash it may not be recognize as filetype "sh". Solution: Add checks for "dash". (Eisuke Kawashima,closes vim/vim#11600) https://github.com/vim/vim/commit/24482fbfd599d2273c48951df7d00d62f3e66c85 Co-authored-by: Eisuke Kawashima Co-authored-by: zeertzjq --- runtime/lua/vim/filetype/detect.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 51ea1971cb..a5f20b61a6 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1166,13 +1166,14 @@ function M.sh(path, contents, name) vim.b[b].is_bash = nil vim.b[b].is_sh = nil end - elseif vim.g.bash_is_sh or matchregex(name, [[\]]) or matchregex(name, [[\]]) then + elseif vim.g.bash_is_sh or matchregex(name, [[\<\(bash\|bash2\)\>]]) then on_detect = function(b) vim.b[b].is_bash = 1 vim.b[b].is_kornshell = nil vim.b[b].is_sh = nil end - elseif matchregex(name, [[\]]) then + -- Ubuntu links sh to dash + elseif matchregex(name, [[\<\(sh\|dash\)\>]]) then on_detect = function(b) vim.b[b].is_sh = 1 vim.b[b].is_kornshell = nil @@ -1460,8 +1461,8 @@ local function match_from_hashbang(contents, path) name = 'wish' end - if matchregex(name, [[^\(bash\d*\|\|ksh\d*\|sh\)\>]]) then - -- Bourne-like shell scripts: bash bash2 ksh ksh93 sh + if matchregex(name, [[^\(bash\d*\|dash\|ksh\d*\|sh\)\>]]) then + -- Bourne-like shell scripts: bash bash2 dash ksh ksh93 sh return require('vim.filetype.detect').sh(path, contents, first_line) elseif matchregex(name, [[^csh\>]]) then return require('vim.filetype.detect').shell(path, contents, vim.g.filetype_csh or 'csh') -- cgit From 9dfbbde240fc095b856d8e0e1c670b1912ec6640 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 26 Nov 2022 00:52:30 +0100 Subject: docs: fix typos (#21168) --- runtime/lua/vim/fs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index c7c053852d..d128c15233 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -73,7 +73,7 @@ end --- searches are recursive and may search through many directories! If {stop} --- is non-nil, then the search stops when the directory given in {stop} is --- reached. The search terminates when {limit} (default 1) matches are found. ---- The search can be narrowed to find only files or or only directories by +--- The search can be narrowed to find only files or only directories by --- specifying {type} to be "file" or "directory", respectively. --- ---@param names (string|table|fun(name: string): boolean) Names of the files -- cgit From 319fbffc94896dd9cf0a77891d69b7fcada1fad4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 26 Nov 2022 14:35:40 +0100 Subject: vim-patch:9.0.0952: Eclipse preference files are not recognized (#21199) Problem: Eclipse preference files are not recognized. Solution: Add a pattern to use "jproperties" for Eclipse preference files. (closes vim/vim#11618) https://github.com/vim/vim/commit/f3f198b6349fe252b72975701e2f17d932b19c70 Co-authored-by: ObserverOfTime --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 5e086c8abf..dbad747f8f 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1909,6 +1909,7 @@ local pattern = { ['Prl.*%..*'] = starsetf('jam'), ['.*%.properties_..'] = 'jproperties', ['.*%.properties_.._..'] = 'jproperties', + ['org%.eclipse%..*%.prefs'] = 'jproperties', ['.*%.properties_.._.._.*'] = starsetf('jproperties'), ['Kconfig%..*'] = starsetf('kconfig'), ['.*%.[Ss][Uu][Bb]'] = 'krl', -- cgit From f004812b338340e5f5157aa68d09d3f0e5605c6c Mon Sep 17 00:00:00 2001 From: Jlll1 Date: Mon, 28 Nov 2022 20:23:04 +0100 Subject: feat(secure): add `:trust` command and vim.secure.trust() (#21107) Introduce vim.secure.trust() to programmatically manage the trust database. Use this function in a new :trust ex command which can be used as a simple frontend. Resolves: https://github.com/neovim/neovim/issues/21092 Co-authored-by: Gregory Anders Co-authored-by: ii14 --- runtime/lua/vim/secure.lua | 139 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 341ff8df05..0b4d7d53a2 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -1,9 +1,50 @@ local M = {} +---@private +--- Reads trust database from $XDG_STATE_HOME/nvim/trust. +--- +---@return (table) Contents of trust database, if it exists. Empty table otherwise. +local function read_trust() + local trust = {} + local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r') + if f then + local contents = f:read('*a') + if contents then + for line in vim.gsplit(contents, '\n') do + local hash, file = string.match(line, '^(%S+) (.+)$') + if hash and file then + trust[file] = hash + end + end + end + f:close() + end + return trust +end + +---@private +--- Writes provided {trust} table to trust database at +--- $XDG_STATE_HOME/nvim/trust. +--- +---@param trust (table) Trust table to write +local function write_trust(trust) + vim.validate({ trust = { trust, 't' } }) + local f = assert(io.open(vim.fn.stdpath('state') .. '/trust', 'w')) + + local t = {} + for p, h in pairs(trust) do + t[#t + 1] = string.format('%s %s\n', h, p) + end + f:write(table.concat(t)) + f:close() +end + --- Attempt to read the file at {path} prompting the user if the file should be --- trusted. The user's choice is persisted in a trust database at --- $XDG_STATE_HOME/nvim/trust. --- +---@see |:trust| +--- ---@param path (string) Path to a file to read. --- ---@return (string|nil) The contents of the given file if it exists and is @@ -15,22 +56,7 @@ function M.read(path) return nil end - local trust = {} - do - local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r') - if f then - local contents = f:read('*a') - if contents then - for line in vim.gsplit(contents, '\n') do - local hash, file = string.match(line, '^(%S+) (.+)$') - if hash and file then - trust[file] = hash - end - end - end - f:close() - end - end + local trust = read_trust() if trust[fullpath] == '!' then -- File is denied @@ -86,21 +112,82 @@ function M.read(path) trust[fullpath] = hash end - do - local f, err = io.open(vim.fn.stdpath('state') .. '/trust', 'w') - if not f then - error(err) + write_trust(trust) + + return contents +end + +--- Manage the trust database. +--- +--- The trust database is located at |$XDG_STATE_HOME|/nvim/trust. +--- +---@param opts (table): +--- - action (string): "allow" to add a file to the trust database and trust it, +--- "deny" to add a file to the trust database and deny it, +--- "remove" to remove file from the trust database +--- - path (string|nil): Path to a file to update. Mutually exclusive with {bufnr}. +--- Cannot be used when {action} is "allow". +--- - bufnr (number|nil): Buffer number to update. Mutually exclusive with {path}. +---@return (boolean, string) success, msg: +--- - true and full path of target file if operation was successful +--- - false and error message on failure +function M.trust(opts) + vim.validate({ + path = { opts.path, 's', true }, + bufnr = { opts.bufnr, 'n', true }, + action = { + opts.action, + function(m) + return m == 'allow' or m == 'deny' or m == 'remove' + end, + [["allow" or "deny" or "remove"]], + }, + }) + + local path = opts.path + local bufnr = opts.bufnr + local action = opts.action + + if path and bufnr then + error('path and bufnr are mutually exclusive', 2) + end + + local fullpath + if path then + fullpath = vim.loop.fs_realpath(vim.fs.normalize(path)) + else + local bufname = vim.api.nvim_buf_get_name(bufnr) + if bufname == '' then + return false, 'buffer is not associated with a file' end + fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname)) + end + + if not fullpath then + return false, string.format('invalid path: %s', path) + end + + local trust = read_trust() + + if action == 'allow' then + assert(bufnr, 'bufnr is required when action is "allow"') - local t = {} - for p, h in pairs(trust) do - t[#t + 1] = string.format('%s %s\n', h, p) + local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' + local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline) + if vim.bo[bufnr].endofline then + contents = contents .. newline end - f:write(table.concat(t)) - f:close() + local hash = vim.fn.sha256(contents) + + trust[fullpath] = hash + elseif action == 'deny' then + trust[fullpath] = '!' + elseif action == 'remove' then + trust[fullpath] = nil end - return contents + write_trust(trust) + return true, fullpath end return M -- cgit From 80b6edabe3e4203ee4bf50261af07a6a0495ef36 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:40:50 -0700 Subject: refactor: rework parameter validation in vim.secure.trust() (#21223) --- runtime/lua/vim/secure.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 0b4d7d53a2..08b1ff871c 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -148,19 +148,23 @@ function M.trust(opts) local bufnr = opts.bufnr local action = opts.action - if path and bufnr then - error('path and bufnr are mutually exclusive', 2) + assert(not path or not bufnr, '"path" and "bufnr" are mutually exclusive') + + if action == 'allow' then + assert(not path, '"path" is not valid when action is "allow"') end local fullpath if path then fullpath = vim.loop.fs_realpath(vim.fs.normalize(path)) - else + elseif bufnr then local bufname = vim.api.nvim_buf_get_name(bufnr) if bufname == '' then return false, 'buffer is not associated with a file' end fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname)) + else + error('one of "path" or "bufnr" is required') end if not fullpath then @@ -170,8 +174,6 @@ function M.trust(opts) local trust = read_trust() if action == 'allow' then - assert(bufnr, 'bufnr is required when action is "allow"') - local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline) if vim.bo[bufnr].endofline then -- cgit From 615f124003376c007442319b31a172360796974c Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Nov 2022 02:45:48 +0100 Subject: docs: fix typos (#21196) Co-authored-by: zeertzjq Co-authored-by: Raphael Co-authored-by: Gregory Anders --- runtime/lua/vim/lsp/sync.lua | 2 +- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 0d65e86b55..826352f036 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -392,7 +392,7 @@ end ---@param lastline number line to begin search in old_lines for last difference ---@param new_lastline number line to begin search in new_lines for last difference ---@param offset_encoding string encoding requested by language server ----@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent +---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent function M.compute_diff( prev_lines, curr_lines, diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d89757ef0c..ba9f145e01 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1679,7 +1679,7 @@ 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". - ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight + ---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) validate({ bufnr = { bufnr, 'n', true }, -- cgit From 61e99217e68498e757b9f8b0c70978a9635ccbfa Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 1 Dec 2022 09:15:05 -0600 Subject: refactor(fs): replace vim.fn/vim.env in vim.fs (#20379) Avoid using vim.env and vim.fn in vim.fs functions so that they can be used in "fast" contexts. --- runtime/lua/vim/fs.lua | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index d128c15233..8ea7f26575 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,5 +1,7 @@ local M = {} +local iswin = vim.loop.os_uname().sysname == 'Windows_NT' + --- Iterate over all the parents of the given file or directory. --- --- Example: @@ -40,7 +42,19 @@ function M.dirname(file) if file == nil then return nil end - return vim.fn.fnamemodify(file, ':h') + vim.validate({ file = { file, 's' } }) + if iswin and file:match('^%w:[\\/]?$') then + return (file:gsub('\\', '/')) + elseif not file:match('[\\/]') then + return '.' + elseif file == '/' or file:match('^/[^/]+$') then + return '/' + end + local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]') + if iswin and dir:match('^%w:$') then + return dir .. '/' + end + return (dir:gsub('\\', '/')) end --- Return the basename of the given file or directory @@ -48,7 +62,14 @@ end ---@param file (string) File or directory ---@return (string) Basename of {file} function M.basename(file) - return vim.fn.fnamemodify(file, ':t') + if file == nil then + return nil + end + vim.validate({ file = { file, 's' } }) + if iswin and file:match('^%w:[\\/]?$') then + return '' + end + return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end --- Return an iterator over the files and directories located in {path} @@ -229,7 +250,12 @@ end ---@return (string) Normalized path function M.normalize(path) vim.validate({ path = { path, 's' } }) - return (path:gsub('^~/', vim.env.HOME .. '/'):gsub('%$([%w_]+)', vim.env):gsub('\\', '/')) + return ( + path + :gsub('^~/', vim.loop.os_homedir() .. '/') + :gsub('%$([%w_]+)', vim.loop.os_getenv) + :gsub('\\', '/') + ) end return M -- cgit From 0b05bd87c04f9cde5c84a062453619349e370795 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 23 Nov 2022 12:31:49 +0100 Subject: docs(gen): support language annotation in docstrings --- runtime/lua/vim/_editor.lua | 6 +++--- runtime/lua/vim/diagnostic.lua | 26 +++++++++++++------------- runtime/lua/vim/filetype.lua | 6 +++--- runtime/lua/vim/fs.lua | 13 ++++++------- runtime/lua/vim/keymap.lua | 8 ++++---- runtime/lua/vim/lsp.lua | 8 +++----- runtime/lua/vim/lsp/buf.lua | 19 +++++++++---------- runtime/lua/vim/lsp/codelens.lua | 3 ++- runtime/lua/vim/lsp/diagnostic.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 32 ++++++++++++++++---------------- runtime/lua/vim/shared.lua | 36 ++++++++++++++++++------------------ runtime/lua/vim/treesitter.lua | 2 +- runtime/lua/vim/treesitter/query.lua | 4 ++-- runtime/lua/vim/ui.lua | 4 ++-- 14 files changed, 83 insertions(+), 86 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index ad4dc20efb..2913a0ddc6 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -123,7 +123,7 @@ do --- (such as the |TUI|) pastes text into the editor. --- --- Example: To remove ANSI color codes when pasting: - ---
+  --- 
lua
   --- vim.paste = (function(overridden)
   ---   return function(lines, phase)
   ---     for i,line in ipairs(lines) do
@@ -280,7 +280,7 @@ end
 --- command.
 ---
 --- Example:
---- 
+--- 
lua
 ---   vim.cmd('echo 42')
 ---   vim.cmd([[
 ---     augroup My_group
@@ -746,7 +746,7 @@ end
 
 ---Prints given arguments in human-readable format.
 ---Example:
----
+---
lua
 ---  -- Print highlight group Normal and store it's contents in a variable.
 ---  local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
 ---
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 18df1f1586..7557bc79a2 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -579,12 +579,12 @@ end --- followed by namespace configuration, and finally global configuration. --- --- For example, if a user enables virtual text globally with ----
+--- 
lua
 ---   vim.diagnostic.config({ virtual_text = true })
 --- 
--- --- and a diagnostic producer sets diagnostics with ----
+--- 
lua
 ---   vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
 --- 
--- @@ -621,13 +621,13 @@ end --- * 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: ----
----                       function(diagnostic)
----                         if diagnostic.severity == vim.diagnostic.severity.ERROR then
----                           return string.format("E: %s", diagnostic.message)
+---                       
lua
+---                         function(diagnostic)
+---                           if diagnostic.severity == vim.diagnostic.severity.ERROR then
+---                             return string.format("E: %s", diagnostic.message)
+---                           end
+---                           return diagnostic.message
 ---                         end
----                         return diagnostic.message
----                       end
 ---                       
--- - signs: (default true) Use signs for diagnostics. Options: --- * severity: Only show signs for diagnostics matching the given severity @@ -1577,11 +1577,11 @@ end --- --- This can be parsed into a diagnostic |diagnostic-structure| --- with: ----
---- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
---- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
---- local groups = { "severity", "lnum", "col", "message" }
---- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+--- 
lua
+---   local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+---   local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+---   local groups = { "severity", "lnum", "col", "message" }
+---   vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
 --- 
--- ---@param str string String to parse diagnostics from. diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index dbad747f8f..e017843548 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2308,7 +2308,7 @@ end --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- --- Example: ----
+--- 
lua
 ---  vim.filetype.add({
 ---    extension = {
 ---      foo = 'fooscript',
@@ -2344,7 +2344,7 @@ end
 --- 
--- --- To add a fallback match on contents, use ----
+--- 
lua
 --- vim.filetype.add {
 ---   pattern = {
 ---     ['.*'] = {
@@ -2456,7 +2456,7 @@ end
 --- Each of the three options is specified using a key to the single argument of this function.
 --- Example:
 ---
---- 
+--- 
lua
 ---   -- Using a buffer number
 ---   vim.filetype.match({ buf = 42 })
 ---
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index d128c15233..acfe8821ea 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -3,7 +3,7 @@ local M = {}
 --- Iterate over all the parents of the given file or directory.
 ---
 --- Example:
---- 
+--- 
lua
 --- local root_dir
 --- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
 ---   if vim.fn.isdirectory(dir .. "/.git") == 1 then
@@ -98,8 +98,7 @@ end
 ---                       - limit (number, default 1): Stop the search after
 ---                               finding this many matches. Use `math.huge` to
 ---                               place no limit on the number of matches.
----
----@return (table) The normalized paths |vim.fs.normalize()| of all matching files or directories
+---@return (table) Normalized paths |vim.fs.normalize()| of all matching files or directories
 function M.find(names, opts)
   opts = opts or {}
   vim.validate({
@@ -214,15 +213,15 @@ end
 --- variables are also expanded.
 ---
 --- Examples:
---- 
+--- 
lua
 ---   vim.fs.normalize('C:\\Users\\jdoe')
----   => 'C:/Users/jdoe'
+---   --> 'C:/Users/jdoe'
 ---
 ---   vim.fs.normalize('~/src/neovim')
----   => '/home/jdoe/src/neovim'
+---   --> '/home/jdoe/src/neovim'
 ---
 ---   vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
----   => '/Users/jdoe/.config/nvim/init.vim'
+---   --> '/Users/jdoe/.config/nvim/init.vim'
 --- 
--- ---@param path (string) Path to normalize diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index af41794c53..ef1c66ea20 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -2,7 +2,7 @@ local keymap = {} --- Add a new |mapping|. --- Examples: ----
+--- 
lua
 ---   -- Can add mapping to Lua functions
 ---   vim.keymap.set('n', 'lhs', function() print("real lua function") end)
 ---
@@ -21,13 +21,13 @@ local keymap = {}
 --- 
--- --- Note that in a mapping like: ----
+--- 
lua
 ---    vim.keymap.set('n', 'asdf', require('jkl').my_fun)
 --- 
--- --- the ``require('jkl')`` gets evaluated during this call in order to access the function. --- If you want to avoid this cost at startup you can wrap it in a function, for example: ----
+--- 
lua
 ---    vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
 --- 
--- @@ -93,7 +93,7 @@ end --- Remove an existing mapping. --- Examples: ----
+--- 
lua
 ---   vim.keymap.del('n', 'lhs')
 ---
 ---   vim.keymap.del({'n', 'i', 'v'}, 'w', { buffer = 5 })
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index d717275ae4..5bbe4aeba9 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -813,8 +813,7 @@ end
 --- Attaches the current buffer to the client.
 ---
 --- Example:
----
---- 
+--- 
lua
 --- vim.lsp.start({
 ---    name = 'my-server-name',
 ---    cmd = {'name-of-language-server-executable'},
@@ -1754,8 +1753,7 @@ end
 ---
 --- You can also use the `stop()` function on a |vim.lsp.client| object.
 --- To stop all clients:
----
---- 
+--- 
lua
 --- vim.lsp.stop_client(vim.lsp.get_active_clients())
 --- 
--- @@ -2239,7 +2237,7 @@ end ---@param fn function Function to run on each client attached to buffer --- {bufnr}. The function takes the client, client ID, and --- buffer number as arguments. Example: ----
+---             
lua
 ---               vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
 ---                 print(vim.inspect(client))
 ---               end)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index c593e72d62..a5a66fd092 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -162,11 +162,11 @@ end
 ---         Predicate used to filter clients. Receives a client as argument and must return a
 ---         boolean. Clients matching the predicate are included. Example:
 ---
----         
----         -- Never request typescript-language-server for formatting
----         vim.lsp.buf.format {
----           filter = function(client) return client.name ~= "tsserver" end
----         }
+---         
lua
+---           -- Never request typescript-language-server for formatting
+---           vim.lsp.buf.format {
+---             filter = function(client) return client.name ~= "tsserver" end
+---           }
 ---         
--- --- - async boolean|nil @@ -555,11 +555,10 @@ end --- Send request to the server to resolve document highlights for the current --- text document position. This request can be triggered by a key mapping or --- by events such as `CursorHold`, e.g.: ---- ----
---- autocmd CursorHold   lua vim.lsp.buf.document_highlight()
---- autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()
---- autocmd CursorMoved  lua vim.lsp.buf.clear_references()
+--- 
vim
+---   autocmd CursorHold   lua vim.lsp.buf.document_highlight()
+---   autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()
+---   autocmd CursorMoved  lua vim.lsp.buf.clear_references()
 --- 
--- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 4fa02c8db2..71b6bfae30 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -241,7 +241,8 @@ end --- --- It is recommended to trigger this using an autocmd or via keymap. --- ----
+--- Example:
+--- 
vim
 ---   autocmd BufEnter,CursorHold,InsertLeave  lua vim.lsp.codelens.refresh()
 --- 
--- diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 1f9d084e2b..5e2bf75f1b 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -150,7 +150,7 @@ end --- --- See |vim.diagnostic.config()| for configuration options. Handler-specific --- configuration can be set using |vim.lsp.with()|: ----
+--- 
lua
 --- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
 ---   vim.lsp.diagnostic.on_publish_diagnostics, {
 ---     -- Enable underline, use default values
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 39e2577294..e0162218f1 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -313,15 +313,15 @@ M['textDocument/completion'] = function(_, result, _, _)
 end
 
 --- |lsp-handler| for the method "textDocument/hover"
---- 
---- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
----   vim.lsp.handlers.hover, {
----     -- Use a sharp border with `FloatBorder` highlights
----     border = "single",
----     -- add the title in hover float window
----     title = "hover"
----   }
---- )
+--- 
lua
+---   vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+---     vim.lsp.handlers.hover, {
+---       -- Use a sharp border with `FloatBorder` highlights
+---       border = "single",
+---       -- add the title in hover float window
+---       title = "hover"
+---     }
+---   )
 --- 
---@param config table Configuration table. --- - border: (default=nil) @@ -399,13 +399,13 @@ M['textDocument/implementation'] = location_handler --- |lsp-handler| for the method "textDocument/signatureHelp". --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. ----
---- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
----   vim.lsp.handlers.signature_help, {
----     -- Use a sharp border with `FloatBorder` highlights
----     border = "single"
----   }
---- )
+--- 
lua
+---   vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+---     vim.lsp.handlers.signature_help, {
+---       -- Use a sharp border with `FloatBorder` highlights
+---       border = "single"
+---     }
+---   )
 --- 
---@param config table Configuration table. --- - border: (default=nil) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index f4a57c13c8..9129500866 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -102,11 +102,11 @@ end --- Splits a string at each instance of a separator. --- --- Examples: ----
----  split(":aa::b:", ":")     => {'','aa','','b',''}
----  split("axaby", "ab?")     => {'','x','y'}
----  split("x*yz*o", "*", {plain=true})  => {'x','yz','o'}
----  split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
+--- 
lua
+---  split(":aa::b:", ":")                   --> {'','aa','','b',''}
+---  split("axaby", "ab?")                   --> {'','x','y'}
+---  split("x*yz*o", "*", {plain=true})      --> {'x','yz','o'}
+---  split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
 --- 
--- ---@see |vim.gsplit()| @@ -383,7 +383,7 @@ end --- Return `nil` if the key does not exist. --- --- Examples: ----
+--- 
lua
 ---  vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
 ---  vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
 --- 
@@ -495,9 +495,9 @@ end --- Counts the number of non-nil values in table `t`. --- ----
---- vim.tbl_count({ a=1, b=2 }) => 2
---- vim.tbl_count({ 1, 2 }) => 2
+--- 
lua
+--- vim.tbl_count({ a=1, b=2 })  --> 2
+--- vim.tbl_count({ 1, 2 })      --> 2
 --- 
--- ---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua @@ -571,7 +571,7 @@ end --- Validates a parameter specification (types and values). --- --- Usage example: ----
+--- 
lua
 ---  function user.new(name, age, hobbies)
 ---    vim.validate{
 ---      name={name, 'string'},
@@ -583,24 +583,24 @@ end
 --- 
--- --- Examples with explicit argument values (can be run directly): ----
+--- 
lua
 ---  vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
----     => NOP (success)
+---     --> NOP (success)
 ---
 ---  vim.validate{arg1={1, 'table'}}
----     => error('arg1: expected table, got number')
+---     --> error('arg1: expected table, got number')
 ---
 ---  vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
----     => error('arg1: expected even number, got 3')
+---     --> error('arg1: expected even number, got 3')
 --- 
--- --- If multiple types are valid they can be given as a list. ----
+--- 
lua
 ---  vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
----     => NOP (success)
+---     --> NOP (success)
 ---
 ---  vim.validate{arg1={1, {'string', table'}}}
----     => error('arg1: expected string|table, got number')
+---     --> error('arg1: expected string|table, got number')
 ---
 --- 
--- @@ -735,7 +735,7 @@ end --- If {create} is `nil`, this will create a defaulttable whose constructor function is --- this function, effectively allowing to create nested tables on the fly: --- ----
+--- 
lua
 --- local a = vim.defaulttable()
 --- a.b.c = 1
 --- 
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 6cd00516bf..0603ddb421 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -313,7 +313,7 @@ end --- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`. --- --- Example: ----
+--- 
lua
 --- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
 ---     callback = function(args)
 ---         vim.treesitter.start(args.buf, 'latex')
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 7ca7384a88..4bec5db527 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -549,7 +549,7 @@ end
 --- The iterator returns three values: a numeric id identifying the capture,
 --- the captured node, and metadata from any directives processing the match.
 --- The following example shows how to get captures by name:
---- 
+--- 
lua
 --- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
 ---   local name = query.captures[id] -- name of the capture in the query
 ---   -- typically useful info about the node:
@@ -603,7 +603,7 @@ end
 --- If the query has more than one pattern, the capture table might be sparse
 --- and e.g. `pairs()` method should be used over `ipairs`.
 --- Here is an example iterating over all captures in every match:
---- 
+--- 
lua
 --- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
 ---   for id, node in pairs(match) do
 ---     local name = query.captures[id]
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 97dccd83ab..8f5be15221 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -21,7 +21,7 @@ local M = {}
 ---
 ---
 --- Example:
---- 
+--- 
lua
 --- vim.ui.select({ 'tabs', 'spaces' }, {
 ---     prompt = 'Select tabs or spaces:',
 ---     format_item = function(item)
@@ -78,7 +78,7 @@ end
 ---               `nil` if the user aborted the dialog.
 ---
 --- Example:
---- 
+--- 
lua
 --- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
 ---     vim.o.shiftwidth = tonumber(input)
 --- end)
-- 
cgit 


From c768b578faba671beab435954dc4e5a321c94728 Mon Sep 17 00:00:00 2001
From: Folke Lemaitre 
Date: Sat, 3 Dec 2022 13:51:57 +0100
Subject: fix(lsp): render 
{lang} code blocks and set separator default to
 false (#21271)

---
 runtime/lua/vim/lsp/util.lua | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ba9f145e01..e96e98f23c 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1261,7 +1261,7 @@ function M.stylize_markdown(bufnr, contents, opts)
   -- when ft is nil, we get the ft from the regex match
   local matchers = {
     block = { nil, '```+([a-zA-Z0-9_]*)', '```+' },
-    pre = { '', '
', '
' }, + pre = { nil, '
([a-z0-9]*)', '
' }, code = { '', '', '' }, text = { 'text', '', '' }, } @@ -1286,8 +1286,6 @@ function M.stylize_markdown(bufnr, contents, opts) -- Clean up contents = M._trim(contents, opts) - -- Insert blank line separator after code block? - local add_sep = opts.separator == nil and true or opts.separator local stripped = {} local highlights = {} -- keep track of lnums that contain markdown @@ -1315,7 +1313,7 @@ function M.stylize_markdown(bufnr, contents, opts) finish = #stripped, }) -- add a separator, but not on the last line - if add_sep and i < #contents then + if opts.separator and i < #contents then table.insert(stripped, '---') markdown_lines[#stripped] = true end -- cgit From 01a8cd0432a272c41e882af96ee8488a4105bd32 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 4 Dec 2022 21:56:04 +0800 Subject: fix(lsp): remove workspaceFolders field (#21284) --- runtime/lua/vim/lsp.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5bbe4aeba9..9595f0b12c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1285,8 +1285,6 @@ function lsp.start_client(config) client.initialized = true uninitialized_clients[client_id] = nil client.workspace_folders = workspace_folders - -- TODO(mjlbach): Backwards compatibility, to be removed in 0.7 - client.workspaceFolders = client.workspace_folders -- These are the cleaned up capabilities we use for dynamically deciding -- when to send certain events to clients. -- cgit From 67e1390dc8eb584d26ae9c9634c05acb3b7e37ca Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sun, 4 Dec 2022 15:57:46 +0100 Subject: fix(lsp): call show_document with correct args Closes https://github.com/neovim/neovim/issues/21177 --- runtime/lua/vim/lsp/handlers.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index e0162218f1..80df83732e 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -579,7 +579,10 @@ M['window/showDocument'] = function(_, result, ctx, _) range = result.selection, } - local success = util.show_document(location, client.offset_encoding, true, result.takeFocus) + local success = util.show_document(location, client.offset_encoding, { + reuse_win = true, + focus = result.takeFocus, + }) return { success = success or false } end -- cgit From b098e7971fdf8ed3f7d0c52aff0ce126c34ff3c8 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 4 Dec 2022 18:02:24 +0100 Subject: fix(lsp): ensure open_logfile is safe for fast events (#21288) Closes https://github.com/neovim/neovim/issues/21052 --- runtime/lua/vim/lsp/log.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 6c6ba0f206..dd9f7d42a4 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -20,6 +20,17 @@ local format_func = function(arg) end do + ---@private + local function notify(msg, level) + if vim.in_fast_event() then + vim.schedule(function() + vim.notify(msg, level) + end) + else + vim.notify(msg, level) + end + end + local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/' ---@private local function path_join(...) @@ -53,7 +64,7 @@ do logfile, openerr = io.open(logfilename, 'a+') if not logfile then local err_msg = string.format('Failed to open LSP client log file: %s', openerr) - vim.notify(err_msg, vim.log.levels.ERROR) + notify(err_msg, vim.log.levels.ERROR) return false end @@ -64,7 +75,7 @@ do log_info.size / (1000 * 1000), logfilename ) - vim.notify(warn_msg) + notify(warn_msg) end -- Start message for logging -- cgit From f3bf1fbf600050fde155e6a1a766b6f848012208 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Mon, 5 Dec 2022 19:59:04 +0100 Subject: fix(secure): crash when hitting escape in prompt (#21283) - use pcall when calling vim.secure.read from C - catch keyboard interrupts in vim.secure.read, rethrow other errors - selecting "view" in prompt runs :view command - simplify lua stack cleanup with lua_gettop and lua_settop Co-authored-by: ii14 --- runtime/lua/vim/secure.lua | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 08b1ff871c..443b152273 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -80,34 +80,27 @@ function M.read(path) end -- File either does not exist in trust database or the hash does not match - local choice = vim.fn.confirm( + local ok, result = pcall( + vim.fn.confirm, string.format('%s is not trusted.', fullpath), '&ignore\n&view\n&deny\n&allow', 1 ) - if choice == 0 or choice == 1 then + if not ok and result ~= 'Keyboard interrupt' then + error(result) + elseif not ok or result == 0 or result == 1 then -- Cancelled or ignored return nil - elseif choice == 2 then + elseif result == 2 then -- View - vim.cmd('new') - local buf = vim.api.nvim_get_current_buf() - local lines = vim.split(string.gsub(contents, '\n$', ''), '\n') - vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) - vim.bo[buf].bufhidden = 'hide' - vim.bo[buf].buftype = 'nofile' - vim.bo[buf].swapfile = false - vim.bo[buf].modeline = false - vim.bo[buf].buflisted = false - vim.bo[buf].readonly = true - vim.bo[buf].modifiable = false + vim.cmd('sview ' .. fullpath) return nil - elseif choice == 3 then + elseif result == 3 then -- Deny trust[fullpath] = '!' contents = nil - elseif choice == 4 then + elseif result == 4 then -- Allow trust[fullpath] = hash end -- cgit From 0ff9131a925dfc94cf0ce787578ee6e3c5e673d7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 5 Dec 2022 15:54:32 -0700 Subject: vim-patch:9.0.1014: zir files are not recognized (#21301) Problem: Zir files are not recognized. Solution: Add a pattern for Zir files. (closes vim/vim#11664) https://github.com/vim/vim/commit/25201016d5043954689a4c9f7833935294149404 Co-authored-by: Bram Moolenaar --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e017843548..58acca42f7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1122,6 +1122,7 @@ local extension = { yang = 'yang', ['z8a'] = 'z8a', zig = 'zig', + zir = 'zir', zu = 'zimbu', zut = 'zimbutempl', zsh = 'zsh', -- cgit From 54305443b9cd5ac2c2220f12e01a653e8064c3a4 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 8 Dec 2022 10:55:01 +0100 Subject: feat(lsp): support willSave & willSaveWaitUntil capability (#21315) `willSaveWaitUntil` allows servers to respond with text edits before saving a document. That is used by some language servers to format a document or apply quick fixes like removing unused imports. --- runtime/lua/vim/lsp.lua | 32 ++++++++++++++++++++++++++++++-- runtime/lua/vim/lsp/protocol.lua | 12 +++++------- 2 files changed, 35 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9595f0b12c..9c42e9df52 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1611,9 +1611,37 @@ function lsp.buf_attach_client(bufnr, client_id) all_buffer_active_clients[bufnr] = buffer_client_ids local uri = vim.uri_from_bufnr(bufnr) - local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr) + local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr) + local group = api.nvim_create_augroup(augroup, { clear = true }) + api.nvim_create_autocmd('BufWritePre', { + group = group, + buffer = bufnr, + desc = 'vim.lsp: textDocument/willSave', + callback = function(ctx) + for_each_buffer_client(ctx.buf, function(client) + local params = { + textDocument = { + uri = uri, + }, + reason = protocol.TextDocumentSaveReason.Manual, + } + if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then + client.notify('textDocument/willSave', params) + end + if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then + local result, err = + client.request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf) + if result and result.result then + util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) + elseif err then + log.error(vim.inspect(err)) + end + end + end) + end, + }) api.nvim_create_autocmd('BufWritePost', { - group = api.nvim_create_augroup(augroup, { clear = true }), + group = group, buffer = bufnr, desc = 'vim.lsp: textDocument/didSave handler', callback = function(ctx) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 8dc93b3b67..925115d056 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -151,6 +151,7 @@ local constants = { }, -- Represents reasons why a text document is saved. + ---@enum lsp.TextDocumentSaveReason TextDocumentSaveReason = { -- Manually triggered, e.g. by the user pressing save, by starting debugging, -- or by an API call. @@ -631,11 +632,8 @@ function protocol.make_client_capabilities() synchronization = { dynamicRegistration = false, - -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) - willSave = false, - - -- TODO(ashkan) Implement textDocument/willSaveWaitUntil - willSaveWaitUntil = false, + willSave = true, + willSaveWaitUntil = true, -- Send textDocument/didSave after saving (BufWritePost) didSave = true, @@ -870,8 +868,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) text_document_sync_properties = { text_document_open_close = if_nil(textDocumentSync.openClose, false), text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None), - text_document_will_save = if_nil(textDocumentSync.willSave, false), - text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false), + text_document_will_save = if_nil(textDocumentSync.willSave, true), + text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, true), text_document_save = if_nil(textDocumentSync.save, false), text_document_save_include_text = if_nil( type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText, -- cgit From 50ffb8d7f4d9e1b080efbf5eb04595f0399db9e5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 8 Dec 2022 20:28:28 +0800 Subject: refactor(lsp): remove deprecated vim.lsp.buf_get_clients calls (#21337) --- runtime/lua/vim/lsp/buf.lua | 6 +++--- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a5a66fd092..c476f2754f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -464,7 +464,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -489,7 +489,7 @@ function M.add_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, { {} } ) - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then @@ -522,7 +522,7 @@ function M.remove_workspace_folder(workspace_folder) { {} }, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e96e98f23c..ddcdc31c1c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1931,7 +1931,7 @@ function M._get_offset_encoding(bufnr) local offset_encoding - for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = bufnr })) do if client.offset_encoding == nil then vim.notify_once( string.format( -- cgit From d44699800cd0dbf14fb45476c13b6cc3c993b5c7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:22:57 -0700 Subject: feat(treesitter): add vim.treesitter.show_tree() (#21322) Add a "show_tree" function to view a textual representation of the nodes in a language tree in a window. Moving the cursor in the window highlights the corresponding text in the source buffer, and moving the cursor in the source buffer highlights the corresponding nodes in the window. --- runtime/lua/vim/treesitter.lua | 195 +++++++++++++++++++++++++++- runtime/lua/vim/treesitter/languagetree.lua | 4 +- runtime/lua/vim/treesitter/playground.lua | 184 ++++++++++++++++++++++++++ 3 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 runtime/lua/vim/treesitter/playground.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0603ddb421..5c9210cc2d 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -277,7 +277,7 @@ end ---@param opts table Optional keyword arguments: --- - ignore_injections boolean Ignore injected languages (default true) --- ----@return userdata |tsnode| under the cursor +---@return userdata|nil |tsnode| under the cursor function M.get_node_at_pos(bufnr, row, col, opts) if bufnr == 0 then bufnr = a.nvim_get_current_buf() @@ -347,4 +347,197 @@ function M.stop(bufnr) vim.bo[bufnr].syntax = 'on' end +--- Open a window that displays a textual representation of the nodes in the language tree. +--- +--- While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the +--- display of the source language of each node, and press to jump to the node under the +--- cursor in the source buffer. +--- +---@param opts table|nil Optional options table with the following possible keys: +--- - bufnr (number|nil): Buffer to draw the tree into. If omitted, a new +--- buffer is created. +--- - winid (number|nil): Window id to display the tree buffer in. If omitted, +--- a new window is created with {command}. +--- - command (string|nil): Vimscript command to create the window. Default +--- value is "topleft 60vnew". Only used when {winid} is nil. +--- - title (string|fun(bufnr:number):string|nil): Title of the window. If a +--- function, it accepts the buffer number of the source buffer as its only +--- argument and should return a string. +function M.show_tree(opts) + vim.validate({ + opts = { opts, 't', true }, + }) + + local Playground = require('vim.treesitter.playground') + local buf = a.nvim_get_current_buf() + local win = a.nvim_get_current_win() + local pg = assert(Playground:new(buf)) + + opts = opts or {} + + -- Close any existing playground window + if vim.b[buf].playground then + local w = vim.b[buf].playground + if a.nvim_win_is_valid(w) then + a.nvim_win_close(w, true) + end + end + + local w = opts.winid + if not w then + vim.cmd(opts.command or 'topleft 60vnew') + w = a.nvim_get_current_win() + end + + local b = opts.bufnr + if b then + a.nvim_win_set_buf(w, b) + else + b = a.nvim_win_get_buf(w) + end + + vim.b[buf].playground = w + + vim.wo[w].scrolloff = 5 + vim.wo[w].wrap = false + vim.bo[b].buflisted = false + vim.bo[b].buftype = 'nofile' + vim.bo[b].bufhidden = 'wipe' + + local title = opts.title + if not title then + local bufname = a.nvim_buf_get_name(buf) + title = string.format('Syntax tree for %s', vim.fn.fnamemodify(bufname, ':.')) + elseif type(title) == 'function' then + title = title(buf) + end + + assert(type(title) == 'string', 'Window title must be a string') + a.nvim_buf_set_name(b, title) + + pg:draw(b) + + vim.fn.matchadd('Comment', '\\[[0-9:-]\\+\\]') + vim.fn.matchadd('String', '".*"') + + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + a.nvim_buf_set_keymap(b, 'n', '', '', { + desc = 'Jump to the node under the cursor in the source buffer', + callback = function() + local row = a.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + a.nvim_set_current_win(win) + a.nvim_win_set_cursor(win, { pos.lnum + 1, pos.col }) + end, + }) + a.nvim_buf_set_keymap(b, 'n', 'a', '', { + desc = 'Toggle anonymous nodes', + callback = function() + pg.opts.anon = not pg.opts.anon + pg:draw(b) + end, + }) + a.nvim_buf_set_keymap(b, 'n', 'I', '', { + desc = 'Toggle language display', + callback = function() + pg.opts.lang = not pg.opts.lang + pg:draw(b) + end, + }) + + local group = a.nvim_create_augroup('treesitter/playground', {}) + + a.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = b, + callback = function() + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + local row = a.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + a.nvim_buf_set_extmark(buf, pg.ns, pos.lnum, pos.col, { + end_row = pos.end_lnum, + end_col = math.max(0, pos.end_col), + hl_group = 'Visual', + }) + end, + }) + + a.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + + local cursor = a.nvim_win_get_cursor(win) + local cursor_node = + M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { ignore_injections = false }) + if not cursor_node then + return + end + + local cursor_node_id = cursor_node:id() + for i, v in pg:iter() do + if v.id == cursor_node_id then + local start = v.depth + local end_col = start + #v.text + a.nvim_buf_set_extmark(b, pg.ns, i - 1, start, { + end_col = end_col, + hl_group = 'Visual', + }) + a.nvim_win_set_cursor(w, { i, 0 }) + break + end + end + end, + }) + + a.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + pg = assert(Playground:new(buf)) + pg:draw(b) + end, + }) + + a.nvim_create_autocmd('BufLeave', { + group = group, + buffer = b, + callback = function() + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + end, + }) + + a.nvim_create_autocmd('BufLeave', { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + end, + }) + + a.nvim_create_autocmd('BufHidden', { + group = group, + buffer = buf, + once = true, + callback = function() + if a.nvim_win_is_valid(w) then + a.nvim_win_close(w, true) + end + end, + }) +end + return M diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index e9d70c4204..a1e96f8ef2 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -608,7 +608,9 @@ end ---@return userdata|nil Found |tsnode| function LanguageTree:named_node_for_range(range, opts) local tree = self:tree_for_range(range, opts) - return tree:root():named_descendant_for_range(unpack(range)) + if tree then + return tree:root():named_descendant_for_range(unpack(range)) + end end --- Gets the appropriate language that contains {range}. diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua new file mode 100644 index 0000000000..325d303df5 --- /dev/null +++ b/runtime/lua/vim/treesitter/playground.lua @@ -0,0 +1,184 @@ +local api = vim.api + +local M = {} + +---@class Playground +---@field opts table Options table with the following keys: +--- - anon (boolean): If true, display anonymous nodes +--- - lang (boolean): If true, display the language alongside each node +--- +---@class Node +---@field id number Node id +---@field text string Node text +---@field named boolean True if this is a named (non-anonymous) node +---@field depth number Depth of the node within the tree +---@field lnum number Beginning line number of this node in the source buffer +---@field col number Beginning column number of this node in the source buffer +---@field end_lnum number Final line number of this node in the source buffer +---@field end_col number Final column number of this node in the source buffer +---@field lang string Source language of this node + +--- Traverse all child nodes starting at {node}. +--- +--- This is a recursive function. The {depth} parameter indicates the current recursion level. +--- {lang} is a string indicating the language of the tree currently being traversed. Each traversed +--- node is added to {tree}. When recursion completes, {tree} is an array of all nodes in the order +--- they were visited. +--- +--- {injections} is a table mapping node ids from the primary tree to language tree injections. Each +--- injected language has a series of trees nested within the primary language's tree, and the root +--- node of each of these trees is contained within a node in the primary tree. The {injections} +--- table maps nodes in the primary tree to root nodes of injected trees. +--- +---@param node userdata Starting node to begin traversal |tsnode| +---@param depth number Current recursion depth +---@param lang string Language of the tree currently being traversed +---@param injections table Mapping of node ids to root nodes of injected language trees (see +--- explanation above) +---@param tree Node[] Output table containing a list of tables each representing a node in the tree +---@private +local function traverse(node, depth, lang, injections, tree) + local injection = injections[node:id()] + if injection then + traverse(injection.root, depth, injection.lang, injections, tree) + end + + for child, field in node:iter_children() do + local type = child:type() + local lnum, col, end_lnum, end_col = child:range() + local named = child:named() + local text + if named then + if field then + text = string.format('%s: (%s)', field, type) + else + text = string.format('(%s)', type) + end + else + text = string.format('"%s"', type:gsub('\n', '\\n')) + end + + table.insert(tree, { + id = child:id(), + text = text, + named = named, + depth = depth, + lnum = lnum, + col = col, + end_lnum = end_lnum, + end_col = end_col, + lang = lang, + }) + + traverse(child, depth + 1, lang, injections, tree) + end + + return tree +end + +--- Create a new Playground object. +--- +---@param bufnr number Source buffer number +--- +---@return Playground|nil +---@return string|nil Error message, if any +--- +---@private +function M.new(self, bufnr) + local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0) + if not ok then + return nil, 'No parser available for the given buffer' + end + + -- For each child tree (injected language), find the root of the tree and locate the node within + -- the primary tree that contains that root. Add a mapping from the node in the primary tree to + -- the root in the child tree to the {injections} table. + local root = parser:parse()[1]:root() + local injections = {} + parser:for_each_child(function(child, lang) + child:for_each_tree(function(tree) + local r = tree:root() + local node = root:named_descendant_for_range(r:range()) + if node then + injections[node:id()] = { + lang = lang, + root = r, + } + end + end) + end) + + local nodes = traverse(root, 0, parser:lang(), injections, {}) + + local named = {} + for _, v in ipairs(nodes) do + if v.named then + named[#named + 1] = v + end + end + + local t = { + ns = api.nvim_create_namespace(''), + nodes = nodes, + named = named, + opts = { + anon = false, + lang = false, + }, + } + + setmetatable(t, self) + self.__index = self + return t +end + +--- Write the contents of this Playground into {bufnr}. +--- +---@param bufnr number Buffer number to write into. +---@private +function M.draw(self, bufnr) + vim.bo[bufnr].modifiable = true + local lines = {} + for _, item in self:iter() do + lines[#lines + 1] = table.concat({ + string.rep(' ', item.depth), + item.text, + item.lnum == item.end_lnum + and string.format(' [%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col) + or string.format( + ' [%d:%d-%d:%d]', + item.lnum + 1, + item.col + 1, + item.end_lnum + 1, + item.end_col + ), + self.opts.lang and string.format(' %s', item.lang) or '', + }) + end + api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + vim.bo[bufnr].modifiable = false +end + +--- Get node {i} from this Playground object. +--- +--- The node number is dependent on whether or not anonymous nodes are displayed. +--- +---@param i number Node number to get +---@return Node +---@private +function M.get(self, i) + local t = self.opts.anon and self.nodes or self.named + return t[i] +end + +--- Iterate over all of the nodes in this Playground object. +--- +---@return function Iterator over all nodes in this Playground +---@return table +---@return number +---@private +function M.iter(self) + return ipairs(self.opts.anon and self.nodes or self.named) +end + +return M -- cgit From 42009ac7df88bfffeea49a83e642fdc6cf9f9447 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:51:46 -0700 Subject: feat(treesitter): add 'lang' option to show_tree() (#21341) This is necessary for now to support filetypes that use a parser with a different name (e.g. the "terraform" filetype uses the "hcl" parser). --- runtime/lua/vim/treesitter.lua | 19 ++++++++++++------- runtime/lua/vim/treesitter/playground.lua | 10 ++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 5c9210cc2d..7813c2edb2 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -275,6 +275,7 @@ end ---@param row number Position row ---@param col number Position column ---@param opts table Optional keyword arguments: +--- - lang string|nil Parser language --- - ignore_injections boolean Ignore injected languages (default true) --- ---@return userdata|nil |tsnode| under the cursor @@ -284,7 +285,7 @@ function M.get_node_at_pos(bufnr, row, col, opts) end local ts_range = { row, col, row, col } - local root_lang_tree = M.get_parser(bufnr) + local root_lang_tree = M.get_parser(bufnr, opts.lang) if not root_lang_tree then return end @@ -354,6 +355,8 @@ end --- cursor in the source buffer. --- ---@param opts table|nil Optional options table with the following possible keys: +--- - lang (string|nil): The language of the source buffer. If omitted, the +--- filetype of the source buffer is used. --- - bufnr (number|nil): Buffer to draw the tree into. If omitted, a new --- buffer is created. --- - winid (number|nil): Window id to display the tree buffer in. If omitted, @@ -368,12 +371,12 @@ function M.show_tree(opts) opts = { opts, 't', true }, }) + opts = opts or {} + local Playground = require('vim.treesitter.playground') local buf = a.nvim_get_current_buf() local win = a.nvim_get_current_win() - local pg = assert(Playground:new(buf)) - - opts = opts or {} + local pg = assert(Playground:new(buf, opts.lang)) -- Close any existing playground window if vim.b[buf].playground then @@ -473,8 +476,10 @@ function M.show_tree(opts) a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) local cursor = a.nvim_win_get_cursor(win) - local cursor_node = - M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { ignore_injections = false }) + local cursor_node = M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { + lang = opts.lang, + ignore_injections = false, + }) if not cursor_node then return end @@ -503,7 +508,7 @@ function M.show_tree(opts) return true end - pg = assert(Playground:new(buf)) + pg = assert(Playground:new(buf, opts.lang)) pg:draw(b) end, }) diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 325d303df5..bb073290c6 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -3,6 +3,7 @@ local api = vim.api local M = {} ---@class Playground +---@field ns number API namespace ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node @@ -79,13 +80,14 @@ end --- Create a new Playground object. --- ---@param bufnr number Source buffer number +---@param lang string|nil Language of source buffer --- ---@return Playground|nil ---@return string|nil Error message, if any --- ---@private -function M.new(self, bufnr) - local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0) +function M.new(self, bufnr, lang) + local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) if not ok then return nil, 'No parser available for the given buffer' end @@ -95,13 +97,13 @@ function M.new(self, bufnr) -- the root in the child tree to the {injections} table. local root = parser:parse()[1]:root() local injections = {} - parser:for_each_child(function(child, lang) + parser:for_each_child(function(child, lang_) child:for_each_tree(function(tree) local r = tree:root() local node = root:named_descendant_for_range(r:range()) if node then injections[node:id()] = { - lang = lang, + lang = lang_, root = r, } end -- cgit From 9f035559defd9d575f37fd825954610065d9cf96 Mon Sep 17 00:00:00 2001 From: John Drouhard Date: Wed, 23 Nov 2022 10:06:36 -0600 Subject: feat(lsp): initial support for semantic token highlighting * credit to @smolck and @theHamsta for their contributions in laying the groundwork for this feature and for their work on some of the helper utility functions and tests --- runtime/lua/vim/highlight.lua | 1 + runtime/lua/vim/lsp.lua | 9 + runtime/lua/vim/lsp/protocol.lua | 55 +++ runtime/lua/vim/lsp/semantic_tokens.lua | 644 ++++++++++++++++++++++++++++++++ 4 files changed, 709 insertions(+) create mode 100644 runtime/lua/vim/lsp/semantic_tokens.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 0fde515bd9..20ad48dd27 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -5,6 +5,7 @@ local M = {} M.priorities = { syntax = 50, treesitter = 100, + semantic_tokens = 125, diagnostics = 150, user = 200, } diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9c42e9df52..3d3c856fcb 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -4,6 +4,7 @@ local lsp_rpc = require('vim.lsp.rpc') local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') local sync = require('vim.lsp.sync') +local semantic_tokens = require('vim.lsp.semantic_tokens') local api = vim.api local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds = @@ -25,6 +26,7 @@ local lsp = { buf = require('vim.lsp.buf'), diagnostic = require('vim.lsp.diagnostic'), codelens = require('vim.lsp.codelens'), + semantic_tokens = semantic_tokens, util = util, -- Allow raw RPC access. @@ -56,6 +58,8 @@ lsp._request_name_to_capability = { ['textDocument/formatting'] = { 'documentFormattingProvider' }, ['textDocument/completion'] = { 'completionProvider' }, ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, + ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, + ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -1526,6 +1530,11 @@ function lsp.start_client(config) -- TODO(ashkan) handle errors. pcall(config.on_attach, client, bufnr) end + + if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + semantic_tokens.start(bufnr, client.id) + end + client.attached_buffers[bufnr] = true end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 925115d056..dfbd01b8f8 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -629,6 +629,58 @@ export interface WorkspaceClientCapabilities { function protocol.make_client_capabilities() return { textDocument = { + semanticTokens = { + dynamicRegistration = false, + tokenTypes = { + 'namespace', + 'type', + 'class', + 'enum', + 'interface', + 'struct', + 'typeParameter', + 'parameter', + 'variable', + 'property', + 'enumMember', + 'event', + 'function', + 'method', + 'macro', + 'keyword', + 'modifier', + 'comment', + 'string', + 'number', + 'regexp', + 'operator', + 'decorator', + }, + tokenModifiers = { + 'declaration', + 'definition', + 'readonly', + 'static', + 'deprecated', + 'abstract', + 'async', + 'modification', + 'documentation', + 'defaultLibrary', + }, + formats = { 'relative' }, + requests = { + -- TODO(jdrouhard): Add support for this + range = false, + full = { delta = true }, + }, + + overlappingTokenSupport = true, + -- TODO(jdrouhard): Add support for this + multilineTokenSupport = false, + serverCancelSupport = false, + augmentsSyntaxTokens = true, + }, synchronization = { dynamicRegistration = false, @@ -772,6 +824,9 @@ function protocol.make_client_capabilities() workspaceEdit = { resourceOperations = { 'rename', 'create', 'delete' }, }, + semanticTokens = { + refreshSupport = true, + }, }, callHierarchy = { dynamicRegistration = false, diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua new file mode 100644 index 0000000000..99cdc20f54 --- /dev/null +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -0,0 +1,644 @@ +local api = vim.api +local handlers = require('vim.lsp.handlers') +local util = require('vim.lsp.util') + +--- @class STTokenRange +--- @field line number line number 0-based +--- @field start_col number start column 0-based +--- @field end_col number end column 0-based +--- @field type string token type as string +--- @field modifiers string[] token modifiers as strings +--- @field extmark_added boolean whether this extmark has been added to the buffer yet +--- +--- @class STCurrentResult +--- @field version number document version associated with this result +--- @field result_id string resultId from the server; used with delta requests +--- @field highlights STTokenRange[] cache of highlight ranges for this document version +--- @field tokens number[] raw token array as received by the server. used for calculating delta responses +--- @field namespace_cleared boolean whether the namespace was cleared for this result yet +--- +--- @class STActiveRequest +--- @field request_id number the LSP request ID of the most recent request sent to the server +--- @field version number the document version associated with the most recent request +--- +--- @class STClientState +--- @field namespace number +--- @field active_request STActiveRequest +--- @field current_result STCurrentResult + +---@class STHighlighter +---@field active table +---@field bufnr number +---@field augroup number augroup for buffer events +---@field debounce number milliseconds to debounce requests for new tokens +---@field timer table uv_timer for debouncing requests for new tokens +---@field client_state table +local STHighlighter = { active = {} } + +---@private +local function binary_search(tokens, line) + local lo = 1 + local hi = #tokens + while lo < hi do + local mid = math.floor((lo + hi) / 2) + if tokens[mid].line < line then + lo = mid + 1 + else + hi = mid + end + end + return lo +end + +--- Extracts modifier strings from the encoded number in the token array +--- +---@private +---@return string[] +local function modifiers_from_number(x, modifiers_table) + ---@private + local function _get_bit(n, k) + --TODO(jdrouhard): remove once `bit` module is available for non-LuaJIT + if _G.bit then + return _G.bit.band(_G.bit.rshift(n, k), 1) + else + return math.floor((n / math.pow(2, k)) % 2) + end + end + + local modifiers = {} + for i = 0, #modifiers_table - 1 do + local b = _get_bit(x, i) + if b == 1 then + modifiers[#modifiers + 1] = modifiers_table[i + 1] + end + end + + return modifiers +end + +--- Converts a raw token list to a list of highlight ranges used by the on_win callback +--- +---@private +---@return STTokenRange[] +local function tokens_to_ranges(data, bufnr, client) + local legend = client.server_capabilities.semanticTokensProvider.legend + local token_types = legend.tokenTypes + local token_modifiers = legend.tokenModifiers + local ranges = {} + + local line + local start_char = 0 + for i = 1, #data, 5 do + local delta_line = data[i] + line = line and line + delta_line or delta_line + local delta_start = data[i + 1] + start_char = delta_line == 0 and start_char + delta_start or delta_start + + -- data[i+3] +1 because Lua tables are 1-indexed + local token_type = token_types[data[i + 3] + 1] + local modifiers = modifiers_from_number(data[i + 4], token_modifiers) + + ---@private + local function _get_byte_pos(char_pos) + return util._get_line_byte_from_position(bufnr, { + line = line, + character = char_pos, + }, client.offset_encoding) + end + + local start_col = _get_byte_pos(start_char) + local end_col = _get_byte_pos(start_char + data[i + 2]) + + if token_type then + ranges[#ranges + 1] = { + line = line, + start_col = start_col, + end_col = end_col, + type = token_type, + modifiers = modifiers, + extmark_added = false, + } + end + end + + return ranges +end + +--- Construct a new STHighlighter for the buffer +--- +---@private +---@param bufnr number +function STHighlighter.new(bufnr) + local self = setmetatable({}, { __index = STHighlighter }) + + self.bufnr = bufnr + self.augroup = api.nvim_create_augroup('vim_lsp_semantic_tokens:' .. bufnr, { clear = true }) + self.client_state = {} + + STHighlighter.active[bufnr] = self + + api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, buf) + local highlighter = STHighlighter.active[buf] + if not highlighter then + return true + end + highlighter:on_change() + end, + on_reload = function(_, buf) + local highlighter = STHighlighter.active[buf] + if highlighter then + highlighter:reset() + highlighter:send_request() + end + end, + on_detach = function(_, buf) + local highlighter = STHighlighter.active[buf] + if highlighter then + highlighter:destroy() + end + end, + }) + + api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, { + buffer = self.bufnr, + group = self.augroup, + callback = function() + self:send_request() + end, + }) + + api.nvim_create_autocmd('LspDetach', { + buffer = self.bufnr, + group = self.augroup, + callback = function(args) + self:detach(args.data.client_id) + if vim.tbl_isempty(self.client_state) then + self:destroy() + end + end, + }) + + return self +end + +---@private +function STHighlighter:destroy() + for client_id, _ in pairs(self.client_state) do + self:detach(client_id) + end + + api.nvim_del_augroup_by_id(self.augroup) + STHighlighter.active[self.bufnr] = nil +end + +---@private +function STHighlighter:attach(client_id) + local state = self.client_state[client_id] + if not state then + state = { + namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens:' .. client_id), + active_request = {}, + current_result = {}, + } + self.client_state[client_id] = state + end +end + +---@private +function STHighlighter:detach(client_id) + local state = self.client_state[client_id] + if state then + --TODO: delete namespace if/when that becomes possible + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + self.client_state[client_id] = nil + end +end + +--- This is the entry point for getting all the tokens in a buffer. +--- +--- For the given clients (or all attached, if not provided), this sends a request +--- to ask for semantic tokens. If the server supports delta requests, that will +--- be prioritized if we have a previous requestId and token array. +--- +--- This function will skip servers where there is an already an active request in +--- flight for the same version. If there is a stale request in flight, that is +--- cancelled prior to sending a new one. +--- +--- Finally, if the request was successful, the requestId and document version +--- are saved to facilitate document synchronization in the response. +--- +---@private +function STHighlighter:send_request() + local version = util.buf_versions[self.bufnr] + + self:reset_timer() + + for client_id, state in pairs(self.client_state) do + local client = vim.lsp.get_client_by_id(client_id) + + local current_result = state.current_result + local active_request = state.active_request + + -- Only send a request for this client if the current result is out of date and + -- there isn't a current a request in flight for this version + if client and current_result.version ~= version and active_request.version ~= version then + -- cancel stale in-flight request + if active_request.request_id then + client.cancel_request(active_request.request_id) + active_request = {} + state.active_request = active_request + end + + local spec = client.server_capabilities.semanticTokensProvider.full + local hasEditProvider = type(spec) == 'table' and spec.delta + + local params = { textDocument = util.make_text_document_params(self.bufnr) } + local method = 'textDocument/semanticTokens/full' + + if hasEditProvider and current_result.result_id then + method = method .. '/delta' + params.previousResultId = current_result.result_id + end + local success, request_id = client.request(method, params, function(err, response, ctx) + -- look client up again using ctx.client_id instead of using a captured + -- client object + local c = vim.lsp.get_client_by_id(ctx.client_id) + local highlighter = STHighlighter.active[ctx.bufnr] + if not err and c and highlighter then + highlighter:process_response(response, c, version) + end + end, self.bufnr) + + if success then + active_request.request_id = request_id + active_request.version = version + end + end + end +end + +--- This function will parse the semantic token responses and set up the cache +--- (current_result). It also performs document synchronization by checking the +--- version of the document associated with the resulting request_id and only +--- performing work if the response is not out-of-date. +--- +--- Delta edits are applied if necessary, and new highlight ranges are calculated +--- and stored in the buffer state. +--- +--- Finally, a redraw command is issued to force nvim to redraw the screen to +--- pick up changed highlight tokens. +--- +---@private +function STHighlighter:process_response(response, client, version) + local state = self.client_state[client.id] + if not state then + return + end + + -- ignore stale responses + if state.active_request.version and version ~= state.active_request.version then + return + end + + -- reset active request + state.active_request = {} + + -- if we have a response to a delta request, update the state of our tokens + -- appropriately. if it's a full response, just use that + local tokens + local token_edits = response.edits + if token_edits then + table.sort(token_edits, function(a, b) + return a.start < b.start + end) + + ---@private + local function _splice(list, start, remove_count, data) + local ret = vim.list_slice(list, 1, start) + vim.list_extend(ret, data) + vim.list_extend(ret, list, start + remove_count + 1) + return ret + end + + tokens = state.current_result.tokens + for _, token_edit in ipairs(token_edits) do + tokens = _splice(tokens, token_edit.start, token_edit.deleteCount, token_edit.data) + end + else + tokens = response.data + end + + -- Update the state with the new results + local current_result = state.current_result + current_result.version = version + current_result.result_id = response.resultId + current_result.tokens = tokens + current_result.highlights = tokens_to_ranges(tokens, self.bufnr, client) + current_result.namespace_cleared = false + + api.nvim_command('redraw!') +end + +--- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) +--- +--- If there is a current result for the buffer and the version matches the +--- current document version, then the tokens are valid and can be applied. As +--- the buffer is drawn, this function will add extmark highlights for every +--- token in the range of visible lines. Once a highlight has been added, it +--- sticks around until the document changes and there's a new set of matching +--- highlight tokens available. +--- +--- If this is the first time a buffer is being drawn with a new set of +--- highlights for the current document version, the namespace is cleared to +--- remove extmarks from the last version. It's done here instead of the response +--- handler to avoid the "blink" that occurs due to the timing between the +--- response handler and the actual redraw. +--- +---@private +function STHighlighter:on_win(topline, botline) + for _, state in pairs(self.client_state) do + local current_result = state.current_result + if current_result.version and current_result.version == util.buf_versions[self.bufnr] then + if not current_result.namespace_cleared then + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + current_result.namespace_cleared = true + end + + -- We can't use ephemeral extmarks because the buffer updates are not in + -- sync with the list of semantic tokens. There's a delay between the + -- buffer changing and when the LSP server can respond with updated + -- tokens, and we don't want to "blink" the token highlights while + -- updates are in flight, and we don't want to use stale tokens because + -- they likely won't line up right with the actual buffer. + -- + -- Instead, we have to use normal extmarks that can attach to locations + -- in the buffer and are persisted between redraws. + local highlights = current_result.highlights + local idx = binary_search(highlights, topline) + + for i = idx, #highlights do + local token = highlights[i] + + if token.line > botline then + break + end + + if not token.extmark_added then + -- `strict = false` is necessary here for the 1% of cases where the + -- current result doesn't actually match the buffer contents. Some + -- LSP servers can respond with stale tokens on requests if they are + -- still processing changes from a didChange notification. + -- + -- LSP servers that do this _should_ follow up known stale responses + -- with a refresh notification once they've finished processing the + -- didChange notification, which would re-synchronize the tokens from + -- our end. + -- + -- The server I know of that does this is clangd when the preamble of + -- a file changes and the token request is processed with a stale + -- preamble while the new one is still being built. Once the preamble + -- finishes, clangd sends a refresh request which lets the client + -- re-synchronize the tokens. + api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { + hl_group = '@' .. token.type, + end_col = token.end_col, + priority = vim.highlight.priorities.semantic_tokens, + strict = false, + }) + + --TODO(jdrouhard): do something with the modifiers + + token.extmark_added = true + end + end + end + end +end + +--- Reset the buffer's highlighting state and clears the extmark highlights. +--- +---@private +function STHighlighter:reset() + for client_id, state in pairs(self.client_state) do + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + state.current_result = {} + if state.active_request.request_id then + local client = vim.lsp.get_client_by_id(client_id) + assert(client) + client.cancel_request(state.active_request.request_id) + state.active_request = {} + end + end +end + +--- Mark a client's results as dirty. This method will cancel any active +--- requests to the server and pause new highlights from being added +--- in the on_win callback. The rest of the current results are saved +--- in case the server supports delta requests. +--- +---@private +---@param client_id number +function STHighlighter:mark_dirty(client_id) + local state = self.client_state[client_id] + assert(state) + + -- if we clear the version from current_result, it'll cause the + -- next request to be sent and will also pause new highlights + -- from being added in on_win until a new result comes from + -- the server + if state.current_result then + state.current_result.version = nil + end + + if state.active_request.request_id then + local client = vim.lsp.get_client_by_id(client_id) + assert(client) + client.cancel_request(state.active_request.request_id) + state.active_request = {} + end +end + +---@private +function STHighlighter:on_change() + self:reset_timer() + if self.debounce > 0 then + self.timer = vim.defer_fn(function() + self:send_request() + end, self.debounce) + else + self:send_request() + end +end + +---@private +function STHighlighter:reset_timer() + local timer = self.timer + if timer then + self.timer = nil + if not timer:is_closing() then + timer:stop() + timer:close() + end + end +end + +local M = {} + +--- Start the semantic token highlighting engine for the given buffer with the +--- given client. The client must already be attached to the buffer. +--- +--- NOTE: This is currently called automatically by |vim.lsp.buf_attach_client()|. To +--- opt-out of semantic highlighting with a server that supports it, you can +--- delete the semanticTokensProvider table from the {server_capabilities} of +--- your client in your |LspAttach| callback or your configuration's +--- `on_attach` callback. +--- +---
lua
+---   client.server_capabilities.semanticTokensProvider = nil
+--- 
+--- +---@param bufnr number +---@param client_id number +---@param opts (nil|table) Optional keyword arguments +--- - debounce (number, default: 200): Debounce token requests +--- to the server by the given number in milliseconds +function M.start(bufnr, client_id, opts) + vim.validate({ + bufnr = { bufnr, 'n', false }, + client_id = { client_id, 'n', false }, + }) + + opts = opts or {} + assert( + (not opts.debounce or type(opts.debounce) == 'number'), + 'opts.debounce must be a number with the debounce time in milliseconds' + ) + + local client = vim.lsp.get_client_by_id(client_id) + if not client then + vim.notify('[LSP] No client with id ' .. client_id, vim.log.levels.ERROR) + return + end + + if not vim.lsp.buf_is_attached(bufnr, client_id) then + vim.notify( + '[LSP] Client with id ' .. client_id .. ' not attached to buffer ' .. bufnr, + vim.log.levels.WARN + ) + return + end + + if not vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + vim.notify('[LSP] Server does not support semantic tokens', vim.log.levels.WARN) + return + end + + local highlighter = STHighlighter.active[bufnr] + + if not highlighter then + highlighter = STHighlighter.new(bufnr) + highlighter.debounce = opts.debounce or 200 + else + highlighter.debounce = math.max(highlighter.debounce, opts.debounce or 200) + end + + highlighter:attach(client_id) + highlighter:send_request() +end + +--- Stop the semantic token highlighting engine for the given buffer with the +--- given client. +--- +--- NOTE: This is automatically called by a |LspDetach| autocmd that is set up as part +--- of `start()`, so you should only need this function to manually disengage the semantic +--- token engine without fully detaching the LSP client from the buffer. +--- +---@param bufnr number +---@param client_id number +function M.stop(bufnr, client_id) + vim.validate({ + bufnr = { bufnr, 'n', false }, + client_id = { client_id, 'n', false }, + }) + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + highlighter:detach(client_id) + + if vim.tbl_isempty(highlighter.client_state) then + highlighter:destroy() + end +end + +--- Force a refresh of all semantic tokens +--- +--- Only has an effect if the buffer is currently active for semantic token +--- highlighting (|vim.lsp.semantic_tokens.start()| has been called for it) +--- +---@param bufnr (nil|number) default: current buffer +function M.force_refresh(bufnr) + vim.validate({ + bufnr = { bufnr, 'n', true }, + }) + + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + highlighter:reset() + highlighter:send_request() +end + +--- |lsp-handler| for the method `workspace/semanticTokens/refresh` +--- +--- Refresh requests are sent by the server to indicate a project-wide change +--- that requires all tokens to be re-requested by the client. This handler will +--- invalidate the current results of all buffers and automatically kick off a +--- new request for buffers that are displayed in a window. For those that aren't, a +--- the BufWinEnter event should take care of it next time it's displayed. +--- +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest +handlers['workspace/semanticTokens/refresh'] = function(err, _, ctx) + if err then + return vim.NIL + end + + for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do + local highlighter = STHighlighter.active[bufnr] + if highlighter and highlighter.client_state[ctx.client_id] then + highlighter:mark_dirty(ctx.client_id) + + if not vim.tbl_isempty(vim.fn.win_findbuf(bufnr)) then + highlighter:send_request() + end + end + end + + return vim.NIL +end + +local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens') +api.nvim_set_decoration_provider(namespace, { + on_win = function(_, _, bufnr, topline, botline) + local highlighter = STHighlighter.active[bufnr] + if highlighter then + highlighter:on_win(topline, botline) + end + end, +}) + +--- for testing only! there is no guarantee of API stability with this! +--- +---@private +M.__STHighlighter = STHighlighter + +return M -- cgit From 5e6a288ce7ee079e7695525f2e9e99d071ccdfbf Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Fri, 9 Dec 2022 04:54:09 -0600 Subject: fix(lsp): followup fixes for semantic tokens support (#21357) 1. The algorithm for applying edits was slightly incorrect. It needs to preserve the original token list as the edits are applied instead of mutating it as it iterates. From the spec: Semantic token edits behave conceptually like text edits on documents: if an edit description consists of n edits all n edits are based on the same state Sm of the number array. They will move the number array from state Sm to Sm+1. 2. Schedule the semantic token engine start() call in the client._on_attach() function so that users who schedule_wrap() their config.on_attach() functions (like nvim-lspconfig does) can still disable semantic tokens by deleting the semanticTokensProvider from their server capabilities. --- runtime/lua/vim/lsp.lua | 11 ++++++++--- runtime/lua/vim/lsp/semantic_tokens.lua | 17 +++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 3d3c856fcb..f3ee484024 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1531,9 +1531,14 @@ function lsp.start_client(config) pcall(config.on_attach, client, bufnr) end - if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then - semantic_tokens.start(bufnr, client.id) - end + -- schedule the initialization of semantic tokens to give the above + -- on_attach and LspAttach callbacks the ability to schedule wrap the + -- opt-out (deleting the semanticTokensProvider from capabilities) + vim.schedule(function() + if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + semantic_tokens.start(bufnr, client.id) + end + end) client.attached_buffers[bufnr] = true end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 99cdc20f54..66e656abb6 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -313,18 +313,15 @@ function STHighlighter:process_response(response, client, version) return a.start < b.start end) - ---@private - local function _splice(list, start, remove_count, data) - local ret = vim.list_slice(list, 1, start) - vim.list_extend(ret, data) - vim.list_extend(ret, list, start + remove_count + 1) - return ret - end - - tokens = state.current_result.tokens + tokens = {} + local old_tokens = state.current_result.tokens + local idx = 1 for _, token_edit in ipairs(token_edits) do - tokens = _splice(tokens, token_edit.start, token_edit.deleteCount, token_edit.data) + vim.list_extend(tokens, old_tokens, idx, token_edit.start) + vim.list_extend(tokens, token_edit.data) + idx = token_edit.start + token_edit.deleteCount + 1 end + vim.list_extend(tokens, old_tokens, idx) else tokens = response.data end -- cgit From 49df92da9459bea9eec356d23cea20a0a2383d68 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 9 Dec 2022 19:18:31 +0100 Subject: fix(lsp): correct some type annotations (#21365) --- runtime/lua/vim/lsp.lua | 66 ++++++++++++++++++++++++++------------------- runtime/lua/vim/lsp/log.lua | 2 +- 2 files changed, 39 insertions(+), 29 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index f3ee484024..dc5008399e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -67,7 +67,7 @@ lsp._request_name_to_capability = { ---@private --- Concatenates and writes a list of strings to the Vim error buffer. --- ----@param {...} (List of strings) List to write to the buffer +---@param {...} table[] List to write to the buffer local function err_message(...) nvim_err_writeln(table.concat(vim.tbl_flatten({ ... }))) nvim_command('redraw') @@ -76,7 +76,7 @@ end ---@private --- Returns the buffer number for the given {bufnr}. --- ----@param bufnr (number) Buffer number to resolve. Defaults to the current +---@param bufnr (number|nil) Buffer number to resolve. Defaults to the current ---buffer if not given. ---@returns bufnr (number) Number of requested buffer local function resolve_bufnr(bufnr) @@ -244,9 +244,9 @@ end ---@private --- Augments a validator function with support for optional (nil) values. --- ----@param fn (function(v)) The original validator function; should return a +---@param fn (fun(v)) The original validator function; should return a ---bool. ----@returns (function(v)) The augmented function. Also returns true if {v} is +---@returns (fun(v)) The augmented function. Also returns true if {v} is ---`nil`. local function optional_validator(fn) return function(v) @@ -1366,7 +1366,7 @@ function lsp.start_client(config) --- ---@param method (string) LSP method name. ---@param params (table) LSP request params. - ---@param handler (function, optional) Response |lsp-handler| for this method. + ---@param handler (function|nil) Response |lsp-handler| for this method. ---@param bufnr (number) Buffer handle (0 for current). ---@returns ({status}, [request_id]): {status} is a bool indicating ---whether the request was successful. If it is `false`, then it will @@ -1377,8 +1377,10 @@ function lsp.start_client(config) ---@see |vim.lsp.buf_request()| function client.request(method, params, handler, bufnr) if not handler then - handler = resolve_handler(method) - or error(string.format('not found: %q request handler for client %q.', method, client.name)) + handler = assert( + resolve_handler(method), + string.format('not found: %q request handler for client %q.', method, client.name) + ) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) @@ -1396,7 +1398,7 @@ function lsp.start_client(config) nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false }) end) - if success then + if success and request_id then client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method } nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false }) end @@ -1411,8 +1413,8 @@ function lsp.start_client(config) --- ---@param method (string) LSP method name. ---@param params (table) LSP request params. - ---@param timeout_ms (number, optional, default=1000) Maximum time in - ---milliseconds to wait for a result. + ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for + --- a result. Defaults to 1000 ---@param bufnr (number) Buffer handle (0 for current). ---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|. ---On timeout, cancel or error, returns `(nil, err)` where `err` is a @@ -1435,7 +1437,9 @@ function lsp.start_client(config) end, 10) if not wait_result then - client.cancel_request(request_id) + if request_id then + client.cancel_request(request_id) + end return nil, wait_result_reason[reason] end return request_result @@ -1800,7 +1804,7 @@ end --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ----@param client_id client id or |vim.lsp.client| object, or list thereof +---@param client_id number|table id or |vim.lsp.client| object, or list thereof ---@param force boolean (optional) shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or { client_id } @@ -1815,10 +1819,16 @@ function lsp.stop_client(client_id, force) end end +---@class vim.lsp.get_active_clients.filter +---@field id number|nil Match clients by id +---@field bufnr number|nil match clients attached to the given buffer +---@field name number|nil match clients by name + --- Get active clients. --- ----@param filter (table|nil) A table with key-value pairs used to filter the ---- returned clients. The available keys are: +---@param filter vim.lsp.get_active_clients.filter|nil (table|nil) A table with +--- key-value pairs used to filter the returned clients. +--- The available keys are: --- - id (number): Only return clients with the given id --- - bufnr (number): Only return clients attached to this buffer --- - name (string): Only return clients with the given name @@ -1966,7 +1976,7 @@ end --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params (optional, table) Parameters to send to the server +---@param params (table|nil) Parameters to send to the server ---@param callback (function) The callback to call when all requests are finished. -- Unlike `buf_request`, this will collect all the responses from each server instead of handling them. -- A map of client_id:request_result will be provided to the callback @@ -2008,9 +2018,9 @@ end --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params (optional, table) Parameters to send to the server ----@param timeout_ms (optional, number, default=1000) Maximum time in ---- milliseconds to wait for a result. +---@param params (table|nil) Parameters to send to the server +---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for a +--- result. Defaults to 1000 --- ---@returns Map of client_id:request_result. On timeout, cancel or error, --- returns `(nil, err)` where `err` is a string describing the failure @@ -2035,9 +2045,9 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) end --- Send a notification to a server ----@param bufnr [number] (optional): The number of the buffer ----@param method [string]: Name of the request method ----@param params [string]: Arguments to send to the server +---@param bufnr (number|nil) The number of the buffer +---@param method (string) Name of the request method +---@param params (string) Arguments to send to the server --- ---@returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) @@ -2078,8 +2088,8 @@ end ---@see |complete-items| ---@see |CompleteDone| --- ----@param findstart 0 or 1, decides behavior ----@param base If findstart=0, text to match against +---@param findstart number 0 or 1, decides behavior +---@param base number findstart=0, text to match against --- ---@returns (number) Decided by {findstart}: --- - findstart=0: column where the completion starts, or -2 or -3 @@ -2208,8 +2218,8 @@ end --- Otherwise, uses "workspace/symbol". If no results are returned from --- any LSP servers, falls back to using built-in tags. --- ----@param pattern Pattern used to find a workspace symbol ----@param flags See |tag-function| +---@param pattern string Pattern used to find a workspace symbol +---@param flags string See |tag-function| --- ---@returns A list of matching tags function lsp.tagfunc(...) @@ -2218,7 +2228,7 @@ end ---Checks whether a client is stopped. --- ----@param client_id (Number) +---@param client_id (number) ---@returns true if client is stopped, false otherwise. function lsp.client_is_stopped(client_id) return active_clients[client_id] == nil @@ -2227,7 +2237,7 @@ end --- Gets a map of client_id:client pairs for the given buffer, where each value --- is a |vim.lsp.client| object. --- ----@param bufnr (optional, number): Buffer handle, or 0 for current +---@param bufnr (number|nil): Buffer handle, or 0 for current ---@returns (table) Table of (client_id, client) pairs ---@deprecated Use |vim.lsp.get_active_clients()| instead. function lsp.buf_get_clients(bufnr) @@ -2256,7 +2266,7 @@ lsp.log_levels = log.levels --- ---@see |vim.lsp.log_levels| --- ----@param level [number|string] the case insensitive level name or number +---@param level (number|string) the case insensitive level name or number function lsp.set_log_level(level) if type(level) == 'string' or type(level) == 'number' then log.set_level(level) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index dd9f7d42a4..d1a78572aa 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -141,7 +141,7 @@ end vim.tbl_add_reverse_lookup(log.levels) --- Sets the current log level. ----@param level (string or number) One of `vim.lsp.log.levels` +---@param level (string|number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then current_log_level = -- cgit From 8b84a10db76ef2bd15bbd3c06ae2d5dfaadc1482 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 9 Dec 2022 22:02:04 +0100 Subject: fix(lsp): fix get_active_clients bufnr parameter (#21366) Follow up to https://github.com/neovim/neovim/pull/21337 --- runtime/lua/vim/lsp/buf.lua | 6 +++--- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c476f2754f..226ed980fb 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -464,7 +464,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -489,7 +489,7 @@ function M.add_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, { {} } ) - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then @@ -522,7 +522,7 @@ function M.remove_workspace_folder(workspace_folder) { {} }, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ddcdc31c1c..2bd15c87d9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1931,7 +1931,7 @@ function M._get_offset_encoding(bufnr) local offset_encoding - for _, client in pairs(vim.lsp.get_active_clients({ buffer = bufnr })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do if client.offset_encoding == nil then vim.notify_once( string.format( -- cgit From 6d37d8cb17390419360c1459607beac2d93183b6 Mon Sep 17 00:00:00 2001 From: fsouza <108725+fsouza@users.noreply.github.com> Date: Sat, 10 Dec 2022 06:16:33 -0500 Subject: fix(lsp): ignore null responses for semanticTokens request (#21364) The spec indicates that the response may be `null`, but it doesn't really say what a `null` response means. Since neovim raises an error if the response is `null`, I figured that ignoring it would be the safest bet. Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp/semantic_tokens.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 66e656abb6..83b414bf87 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -304,6 +304,11 @@ function STHighlighter:process_response(response, client, version) -- reset active request state.active_request = {} + -- skip nil responses + if response == nil then + return + end + -- if we have a response to a delta request, update the state of our tokens -- appropriately. if it's a full response, just use that local tokens -- cgit From 1c324cb1927e03b5a3584a8982e3d5029498f14e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Dec 2022 21:41:26 -0500 Subject: docs #20986 - https://github.com/neovim/tree-sitter-vimdoc v1.2.4 eliminates most errors in pi_netrw.txt, so we can remove that workaround from ignore_parse_error(). - improved codeblock --- runtime/lua/vim/lsp/semantic_tokens.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 83b414bf87..11e62ee793 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -495,7 +495,6 @@ local M = {} --- delete the semanticTokensProvider table from the {server_capabilities} of --- your client in your |LspAttach| callback or your configuration's --- `on_attach` callback. ---- ---
lua
 ---   client.server_capabilities.semanticTokensProvider = nil
 --- 
-- cgit From 8b9bf3e3b997f033dcf73d506cceb12231501a22 Mon Sep 17 00:00:00 2001 From: Phelipe Teles <39670535+phelipetls@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:14:50 -0300 Subject: fix: vim.opt_local:append ignoring global option value (#21382) Closes https://github.com/neovim/neovim/issues/18225 --- runtime/lua/vim/_meta.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 9c7972873e..104f29c4c0 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -526,7 +526,7 @@ local function create_option_accessor(scope) return setmetatable({}, { __index = function(_, k) - return make_option(k, a.nvim_get_option_value(k, { scope = scope })) + return make_option(k, a.nvim_get_option_value(k, {})) end, __newindex = function(_, k, v) -- cgit From 3869a2e0cf25323a8e5235840678b147ca908517 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Mon, 12 Dec 2022 11:42:37 -0600 Subject: perf(lsp): update semantic tokens algorithm for parsing modifiers (#21383) Instead of testing for every possible modifier type, only test bits up to the highest set in the token array. Saves many bit ops and comparisons when there are no modifiers or when the highest set bit is a lower bit than the highest possible in the legend on average. Can be further simplified when non-luaJIT gets the full bit module (see #21222) --- runtime/lua/vim/lsp/semantic_tokens.lua | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 11e62ee793..f06d136801 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -55,22 +55,22 @@ end ---@private ---@return string[] local function modifiers_from_number(x, modifiers_table) - ---@private - local function _get_bit(n, k) - --TODO(jdrouhard): remove once `bit` module is available for non-LuaJIT + local modifiers = {} + local idx = 1 + while x > 0 do if _G.bit then - return _G.bit.band(_G.bit.rshift(n, k), 1) + if _G.bit.band(x, 1) == 1 then + modifiers[#modifiers + 1] = modifiers_table[idx] + end + x = _G.bit.rshift(x, 1) else - return math.floor((n / math.pow(2, k)) % 2) - end - end - - local modifiers = {} - for i = 0, #modifiers_table - 1 do - local b = _get_bit(x, i) - if b == 1 then - modifiers[#modifiers + 1] = modifiers_table[i + 1] + --TODO(jdrouhard): remove this branch once `bit` module is available for non-LuaJIT (#21222) + if x % 2 == 1 then + modifiers[#modifiers + 1] = modifiers_table[idx] + end + x = math.floor(x / 2) end + idx = idx + 1 end return modifiers -- cgit From 54d6a32fbdcbd5b26b72f4dca8906e60f5186d2c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 12 Dec 2022 20:43:14 +0100 Subject: feat(lsp): highlight semantic token modifiers (#21390) Apply semantic token modifiers as separate extmarks with corresponding highlight groups (e.g., `@readonly`). This is a low-effort PR to enable the most common use cases (applying, e.g., italics or backgrounds on top of type highlights; language-specific fallbacks like `@global.lua` are also available). This can be replaced by more complicated selector-style themes later on. --- runtime/lua/vim/lsp/semantic_tokens.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index f06d136801..b7ffedab2b 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -409,7 +409,17 @@ function STHighlighter:on_win(topline, botline) strict = false, }) - --TODO(jdrouhard): do something with the modifiers + -- TODO(bfredl) use single extmark when hl_group supports table + if #token.modifiers > 0 then + for _, modifier in pairs(token.modifiers) do + api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { + hl_group = '@' .. modifier, + end_col = token.end_col, + priority = vim.highlight.priorities.semantic_tokens, + strict = false, + }) + end + end token.extmark_added = true end @@ -494,7 +504,7 @@ local M = {} --- opt-out of semantic highlighting with a server that supports it, you can --- delete the semanticTokensProvider table from the {server_capabilities} of --- your client in your |LspAttach| callback or your configuration's ---- `on_attach` callback. +--- `on_attach` callback: ---
lua
 ---   client.server_capabilities.semanticTokensProvider = nil
 --- 
-- cgit From 04da0432446fac57e391c31bd4de0a9c06b1626d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 10 Dec 2022 13:17:07 +0100 Subject: feat(lsp): add function to get semantic tokens at cursor --- runtime/lua/vim/lsp/semantic_tokens.lua | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index b7ffedab2b..d4c414675c 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -585,6 +585,50 @@ function M.stop(bufnr, client_id) end end +--- Return the semantic token(s) at the given position. +--- If called without argument, returns the token under the cursor. +--- +---@param bufnr number|nil Buffer number (0 for current buffer, default) +---@param row number|nil Position row (default cursor position) +---@param col number|nil Position column (default cursor position) +--- +---@return table[]|nil tokens Table of tokens at position +function M.get_at_pos(bufnr, row, col) + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + if row == nil or col == nil then + local cursor = api.nvim_win_get_cursor(0) + row, col = cursor[1] - 1, cursor[2] + end + + local tokens = {} + for _, client in pairs(highlighter.client_state) do + local highlights = client.current_result.highlights + if highlights then + local idx = binary_search(highlights, row) + for i = idx, #highlights do + local token = highlights[i] + + if token.line > row then + break + end + + if token.start_col <= col and token.end_col > col then + tokens[#tokens + 1] = token + end + end + end + end + return tokens +end + --- Force a refresh of all semantic tokens --- --- Only has an effect if the buffer is currently active for semantic token -- cgit From d127c684faa9a112575798a81c87babb1c83a7ea Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 14 Dec 2022 15:00:48 +0000 Subject: fix(treesitter): properly restore `'syntax'` (#21358) --- runtime/lua/vim/treesitter.lua | 6 ------ runtime/lua/vim/treesitter/highlighter.lua | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 7813c2edb2..25f0e7dc5e 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -327,12 +327,8 @@ end ---@param lang (string|nil) Language of the parser (default: buffer filetype) function M.start(bufnr, lang) bufnr = bufnr or a.nvim_get_current_buf() - local parser = M.get_parser(bufnr, lang) - M.highlighter.new(parser) - - vim.b[bufnr].ts_highlight = true end --- Stops treesitter highlighting for a buffer @@ -344,8 +340,6 @@ function M.stop(bufnr) if M.highlighter.active[bufnr] then M.highlighter.active[bufnr]:destroy() end - - vim.bo[bufnr].syntax = 'on' end --- Open a window that displays a textual representation of the nodes in the language tree. diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index f5e5ca1988..99aedf60c5 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -88,7 +88,9 @@ function TSHighlighter.new(tree, opts) end end + self.orig_syntax = vim.bo[self.bufnr].syntax vim.bo[self.bufnr].syntax = '' + vim.b[self.bufnr].ts_highlight = true TSHighlighter.active[self.bufnr] = self @@ -114,6 +116,8 @@ function TSHighlighter:destroy() if TSHighlighter.active[self.bufnr] then TSHighlighter.active[self.bufnr] = nil end + + vim.bo[self.bufnr].syntax = self.orig_syntax end ---@private -- cgit From 4a78ed60a3acd3a5694cc34debd4d91bed96195a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 14 Dec 2022 19:46:17 +0100 Subject: vim-patch:9.0.1057: conflict between supercollider and scala filetype detection (#21417) Problem: Conflict between supercollider and scala filetype detection. Solution: Do not check for "Class : Method", it can appear in both filetypes. (Chris Kipp, closes vim/vim#11699) https://github.com/vim/vim/commit/70ef3f546b6ef83e463e91b7e388d9c68ad58894 Co-authored-by: Chris Kipp --- runtime/lua/vim/filetype/detect.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index a5f20b61a6..ee538dc8c7 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1097,11 +1097,10 @@ function M.sc(bufnr) for _, line in ipairs(getlines(bufnr, 1, 25)) do if findany(line, { - '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', 'var%s<', 'classvar%s<', '%^this.*', - '|%w*|', + '|%w+|', '%+%s%w*%s{', '%*ar%s', }) -- cgit From 0887ad1cbb050d2bc6169ad46aa07cf42c90493f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 14 Dec 2022 22:54:58 +0000 Subject: fix(ts): check buffer is loaded when restoring options (#21419) fix(treesitter): check buffer is loaded when restoring options Also restore spelloptions Fixes #21416 --- runtime/lua/vim/treesitter/highlighter.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 99aedf60c5..e99994c8a9 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -89,6 +89,8 @@ function TSHighlighter.new(tree, opts) end self.orig_syntax = vim.bo[self.bufnr].syntax + self.orig_spelloptions = vim.bo[self.bufnr].spelloptions + vim.bo[self.bufnr].syntax = '' vim.b[self.bufnr].ts_highlight = true @@ -117,7 +119,10 @@ function TSHighlighter:destroy() TSHighlighter.active[self.bufnr] = nil end - vim.bo[self.bufnr].syntax = self.orig_syntax + if vim.api.nvim_buf_is_loaded(self.bufnr) then + vim.bo[self.bufnr].syntax = self.orig_syntax + vim.bo[self.bufnr].spelloptions = self.orig_spelloptions + end end ---@private -- cgit From 26c918d03f5b38df900316c0601065ec1ea96264 Mon Sep 17 00:00:00 2001 From: William Boman Date: Thu, 15 Dec 2022 02:27:23 +0100 Subject: fix(lua): always return nil values in vim.tbl_get when no results While `return` and `return nil` are for most intents and purposes identical, there are situations where they're not. For example, calculating the amount of values via the `select()` function will yield varying results: ```lua local function nothing() return end local function null() return nil end select('#', nothing()) -- 0 select('#', null()) -- 1 ``` `vim.tbl_get` currently returns both nil and no results, which makes it unreliable to use in certain situations without manually accounting for these discrepancies. --- runtime/lua/vim/shared.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 9129500866..5ffd11682c 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -395,15 +395,14 @@ end function vim.tbl_get(o, ...) local keys = { ... } if #keys == 0 then - return + return nil end for i, k in ipairs(keys) do - if type(o[k]) ~= 'table' and next(keys, i) then - return nil - end o = o[k] if o == nil then - return + return nil + elseif type(o) ~= 'table' and next(keys, i) then + return nil end end return o -- cgit From ef91146efcece1b6d97152251e7137d301146189 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 14 Dec 2022 10:46:54 +0100 Subject: feat: `vim.inspect_pos`, `vim.show_pos`, `:Inspect` --- runtime/lua/vim/_init_packages.lua | 3 + runtime/lua/vim/_inspector.lua | 238 ++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp/semantic_tokens.lua | 7 +- runtime/lua/vim/treesitter.lua | 2 +- 4 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 runtime/lua/vim/_inspector.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua index 19c8608732..0c4ee8636d 100644 --- a/runtime/lua/vim/_init_packages.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -56,6 +56,9 @@ setmetatable(vim, { if vim._submodules[key] then t[key] = require('vim.' .. key) return t[key] + elseif key == 'inspect_pos' or key == 'show_pos' then + require('vim._inspector') + return t[key] elseif vim.startswith(key, 'uri_') then local val = require('vim.uri')[key] if val ~= nil then diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua new file mode 100644 index 0000000000..f46a525910 --- /dev/null +++ b/runtime/lua/vim/_inspector.lua @@ -0,0 +1,238 @@ +---@class InspectorFilter +---@field syntax boolean include syntax based highlight groups (defaults to true) +---@field treesitter boolean include treesitter based highlight groups (defaults to true) +---@field extmarks boolean|"all" include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true) +---@field semantic_tokens boolean include semantic tokens (defaults to true) +local defaults = { + syntax = true, + treesitter = true, + extmarks = true, + semantic_tokens = true, +} + +---Get all the items at a given buffer position. +--- +---Can also be pretty-printed with `:Inspect!`. *:Inspect!* +--- +---@param bufnr? number defaults to the current buffer +---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor +---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor +---@param filter? InspectorFilter (table|nil) a table with key-value pairs to filter the items +--- - syntax (boolean): include syntax based highlight groups (defaults to true) +--- - treesitter (boolean): include treesitter based highlight groups (defaults to true) +--- - extmarks (boolean|"all"): include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true) +--- - semantic_tokens (boolean): include semantic tokens (defaults to true) +---@return {treesitter:table,syntax:table,extmarks:table,semantic_tokens:table,buffer:number,col:number,row:number} (table) a table with the following key-value pairs. Items are in "traversal order": +--- - treesitter: a list of treesitter captures +--- - syntax: a list of syntax groups +--- - semantic_tokens: a list of semantic tokens +--- - extmarks: a list of extmarks +--- - buffer: the buffer used to get the items +--- - row: the row used to get the items +--- - col: the col used to get the items +function vim.inspect_pos(bufnr, row, col, filter) + filter = vim.tbl_deep_extend('force', defaults, filter or {}) + + bufnr = bufnr or 0 + if row == nil or col == nil then + -- get the row/col from the first window displaying the buffer + local win = bufnr == 0 and vim.api.nvim_get_current_win() or vim.fn.bufwinid(bufnr) + if win == -1 then + error('row/col is required for buffers not visible in a window') + end + local cursor = vim.api.nvim_win_get_cursor(win) + row, col = cursor[1] - 1, cursor[2] + end + bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr + + local results = { + treesitter = {}, + syntax = {}, + extmarks = {}, + semantic_tokens = {}, + buffer = bufnr, + row = row, + col = col, + } + + -- resolve hl links + ---@private + local function resolve_hl(data) + if data.hl_group then + local hlid = vim.api.nvim_get_hl_id_by_name(data.hl_group) + local name = vim.fn.synIDattr(vim.fn.synIDtrans(hlid), 'name') + data.hl_group_link = name + end + return data + end + + -- treesitter + if filter.treesitter then + for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do + capture.hl_group = '@' .. capture.capture + table.insert(results.treesitter, resolve_hl(capture)) + end + end + + -- syntax + if filter.syntax then + for _, i1 in ipairs(vim.fn.synstack(row + 1, col + 1)) do + table.insert(results.syntax, resolve_hl({ hl_group = vim.fn.synIDattr(i1, 'name') })) + end + end + + -- semantic tokens + if filter.semantic_tokens then + for _, token in ipairs(vim.lsp.semantic_tokens.get_at_pos(bufnr, row, col) or {}) do + token.hl_groups = { + type = resolve_hl({ hl_group = '@' .. token.type }), + modifiers = vim.tbl_map(function(modifier) + return resolve_hl({ hl_group = '@' .. modifier }) + end, token.modifiers or {}), + } + table.insert(results.semantic_tokens, token) + end + end + + -- extmarks + if filter.extmarks then + for ns, nsid in pairs(vim.api.nvim_get_namespaces()) do + if ns:find('vim_lsp_semantic_tokens') ~= 1 then + local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, nsid, 0, -1, { details = true }) + for _, extmark in ipairs(extmarks) do + extmark = { + ns_id = nsid, + ns = ns, + id = extmark[1], + row = extmark[2], + col = extmark[3], + opts = resolve_hl(extmark[4]), + } + local end_row = extmark.opts.end_row or extmark.row -- inclusive + local end_col = extmark.opts.end_col or (extmark.col + 1) -- exclusive + if + (filter.extmarks == 'all' or extmark.opts.hl_group) -- filter hl_group + and (row >= extmark.row and row <= end_row) -- within the rows of the extmark + and (row > extmark.row or col >= extmark.col) -- either not the first row, or in range of the col + and (row < end_row or col < end_col) -- either not in the last row or in range of the col + then + table.insert(results.extmarks, extmark) + end + end + end + end + end + return results +end + +---Show all the items at a given buffer position. +--- +---Can also be shown with `:Inspect`. *:Inspect* +--- +---@param bufnr? number defaults to the current buffer +---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor +---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor +---@param filter? InspectorFilter (table|nil) see |vim.inspect_pos()| +function vim.show_pos(bufnr, row, col, filter) + local items = vim.inspect_pos(bufnr, row, col, filter) + + local lines = { {} } + + ---@private + local function append(str, hl) + table.insert(lines[#lines], { str, hl }) + end + + ---@private + local function nl() + table.insert(lines, {}) + end + + ---@private + local function item(data, comment) + append(' - ') + append(data.hl_group, data.hl_group) + append(' ') + if data.hl_group ~= data.hl_group_link then + append('links to ', 'MoreMsg') + append(data.hl_group_link, data.hl_group_link) + append(' ') + end + if comment then + append(comment, 'Comment') + end + nl() + end + + -- treesitter + if #items.treesitter > 0 then + append('Treesitter', 'Title') + nl() + for _, capture in ipairs(items.treesitter) do + item(capture, capture.lang) + end + nl() + end + + if #items.semantic_tokens > 0 then + append('Semantic Tokens', 'Title') + nl() + for _, token in ipairs(items.semantic_tokens) do + local client = vim.lsp.get_client_by_id(token.client_id) + client = client and (' (' .. client.name .. ')') or '' + item(token.hl_groups.type, 'type' .. client) + for _, modifier in ipairs(token.hl_groups.modifiers) do + item(modifier, 'modifier' .. client) + end + end + nl() + end + + -- syntax + if #items.syntax > 0 then + append('Syntax', 'Title') + nl() + for _, syn in ipairs(items.syntax) do + item(syn) + end + nl() + end + -- extmarks + if #items.extmarks > 0 then + append('Extmarks', 'Title') + nl() + for _, extmark in ipairs(items.extmarks) do + if extmark.opts.hl_group then + item(extmark.opts, extmark.ns) + else + append(' - ') + append(extmark.ns, 'Comment') + nl() + end + end + nl() + end + + if #lines[#lines] == 0 then + table.remove(lines) + end + + local chunks = {} + for _, line in ipairs(lines) do + vim.list_extend(chunks, line) + table.insert(chunks, { '\n' }) + end + if #chunks == 0 then + chunks = { + { + 'No items found at position ' + .. items.row + .. ',' + .. items.col + .. ' in buffer ' + .. items.buffer, + }, + } + end + vim.api.nvim_echo(chunks, false, {}) +end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index d4c414675c..e14d3e51cd 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -586,13 +586,13 @@ function M.stop(bufnr, client_id) end --- Return the semantic token(s) at the given position. ---- If called without argument, returns the token under the cursor. +--- If called without arguments, returns the token under the cursor. --- ---@param bufnr number|nil Buffer number (0 for current buffer, default) ---@param row number|nil Position row (default cursor position) ---@param col number|nil Position column (default cursor position) --- ----@return table[]|nil tokens Table of tokens at position +---@return table|nil (table|nil) List of tokens at position function M.get_at_pos(bufnr, row, col) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() @@ -609,7 +609,7 @@ function M.get_at_pos(bufnr, row, col) end local tokens = {} - for _, client in pairs(highlighter.client_state) do + for client_id, client in pairs(highlighter.client_state) do local highlights = client.current_result.highlights if highlights then local idx = binary_search(highlights, row) @@ -621,6 +621,7 @@ function M.get_at_pos(bufnr, row, col) end if token.start_col <= col and token.end_col > col then + token.client_id = client_id tokens[#tokens + 1] = token end end diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 7813c2edb2..5031aca378 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -240,7 +240,7 @@ function M.get_captures_at_pos(bufnr, row, col) if M.is_in_node_range(node, row, col) then local c = q._query.captures[capture] -- name of the capture in the query if c ~= nil then - table.insert(matches, { capture = c, metadata = metadata }) + table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() }) end end end -- cgit From 1743359235206cbd9da2e0589f1caba37cce3b8c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 18 Dec 2022 03:19:15 +0100 Subject: fix(diagnostic): clear stale cache on reset (#21454) The BufWipeout autocmd is not 100% reliable and may leave stale entries in the cache. This is sort of a hack/workaround to ensure `vim.diagnostic.reset` calls don't fail if there are stale cache entries but instead clears them Fixes errors like Error executing vim.schedule lua callback: /usr/share/nvim/runtime/lua/vim/diagnostic.lua:1458: Invalid buffer id: 22 stack traceback: [C]: in function 'nvim_exec_autocmds' /usr/share/nvim/runtime/lua/vim/diagnostic.lua:1458: in function 'reset' --- runtime/lua/vim/diagnostic.lua | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 7557bc79a2..8ca324d666 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -922,7 +922,7 @@ M.handlers.signs = { end, hide = function(namespace, bufnr) local ns = M.get_namespace(namespace) - if ns.user_data.sign_group then + if ns.user_data.sign_group and api.nvim_buf_is_valid(bufnr) then vim.fn.sign_unplace(ns.user_data.sign_group, { buffer = bufnr }) end end, @@ -977,7 +977,9 @@ M.handlers.underline = { local ns = M.get_namespace(namespace) if ns.user_data.underline_ns then diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {} - api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1) + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1) + end end end, } @@ -1040,7 +1042,9 @@ M.handlers.virtual_text = { local ns = M.get_namespace(namespace) if ns.user_data.virt_text_ns then diagnostic_cache_extmarks[bufnr][ns.user_data.virt_text_ns] = {} - api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) + end end end, } @@ -1470,11 +1474,15 @@ function M.reset(namespace, bufnr) M.hide(iter_namespace, iter_bufnr) end - api.nvim_exec_autocmds('DiagnosticChanged', { - modeline = false, - buffer = iter_bufnr, - data = { diagnostics = {} }, - }) + if api.nvim_buf_is_valid(iter_bufnr) then + api.nvim_exec_autocmds('DiagnosticChanged', { + modeline = false, + buffer = iter_bufnr, + data = { diagnostics = {} }, + }) + else + diagnostic_cache[iter_bufnr] = nil + end end end -- cgit From bf9ad5db962509f3434726b7ad84d31d002fb8a3 Mon Sep 17 00:00:00 2001 From: tae-soo-kim <117524309+tae-soo-kim@users.noreply.github.com> Date: Sun, 18 Dec 2022 10:17:15 -0500 Subject: fix(diagnostic): sort diagnostics by column (#21457) Sort diagnostics by column number in quickfix list --- runtime/lua/vim/diagnostic.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 8ca324d666..84a8701ac7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1673,7 +1673,11 @@ function M.toqflist(diagnostics) end table.sort(list, function(a, b) if a.bufnr == b.bufnr then - return a.lnum < b.lnum + if a.lnum == b.lnum then + return a.col < b.col + else + return a.lnum < b.lnum + end else return a.bufnr < b.bufnr end -- cgit From f4d8e992bfcd6e9d0097b9d7a022060bd32f2069 Mon Sep 17 00:00:00 2001 From: tiagovla <30515389+tiagovla@users.noreply.github.com> Date: Mon, 19 Dec 2022 05:24:27 -0300 Subject: fix(lsp): token_edit.data might be null on deletion (#21462) --- runtime/lua/vim/lsp/semantic_tokens.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index e14d3e51cd..48190b03e1 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -323,7 +323,9 @@ function STHighlighter:process_response(response, client, version) local idx = 1 for _, token_edit in ipairs(token_edits) do vim.list_extend(tokens, old_tokens, idx, token_edit.start) - vim.list_extend(tokens, token_edit.data) + if token_edit.data then + vim.list_extend(tokens, token_edit.data) + end idx = token_edit.start + token_edit.deleteCount + 1 end vim.list_extend(tokens, old_tokens, idx) -- cgit From fb5576c2d36464b55c2c639aec9259a6f2461970 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 13 Dec 2022 13:59:31 +0000 Subject: feat(fs): add opts argument to vim.fs.dir() Added option depth to allow recursively searching a directory tree. --- runtime/lua/vim/fs.lua | 64 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index fdf3d29e94..89a1d0d345 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -72,18 +72,65 @@ function M.basename(file) return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end +---@private +local function join_paths(...) + return table.concat({ ... }, '/') +end + --- Return an iterator over the files and directories located in {path} --- ---@param path (string) An absolute or relative path to the directory to iterate --- over. The path is first normalized |vim.fs.normalize()|. +--- @param opts table|nil Optional keyword arguments: +--- - depth: integer|nil How deep the traverse (default 1) +--- - skip: (fun(dir_name: string): boolean)|nil Predicate +--- to control traversal. Return false to stop searching the current directory. +--- Only useful when depth > 1 +--- ---@return Iterator over files and directories in {path}. Each iteration yields --- two values: name and type. Each "name" is the basename of the file or --- directory relative to {path}. Type is one of "file" or "directory". -function M.dir(path) - return function(fs) - return vim.loop.fs_scandir_next(fs) - end, - vim.loop.fs_scandir(M.normalize(path)) +function M.dir(path, opts) + opts = opts or {} + + vim.validate({ + path = { path, { 'string' } }, + depth = { opts.depth, { 'number' }, true }, + skip = { opts.skip, { 'function' }, true }, + }) + + if not opts.depth or opts.depth == 1 then + return function(fs) + return vim.loop.fs_scandir_next(fs) + end, + vim.loop.fs_scandir(M.normalize(path)) + end + + --- @async + return coroutine.wrap(function() + local dirs = { { path, 1 } } + while #dirs > 0 do + local dir0, level = unpack(table.remove(dirs, 1)) + local dir = level == 1 and dir0 or join_paths(path, dir0) + local fs = vim.loop.fs_scandir(M.normalize(dir)) + while fs do + local name, t = vim.loop.fs_scandir_next(fs) + if not name then + break + end + local f = level == 1 and name or join_paths(dir0, name) + coroutine.yield(f, t) + if + opts.depth + and level < opts.depth + and t == 'directory' + and (not opts.skip or opts.skip(f) ~= false) + then + dirs[#dirs + 1] = { f, level + 1 } + end + end + end + end) end --- Find files or directories in the given path. @@ -155,7 +202,7 @@ function M.find(names, opts) local t = {} for name, type in M.dir(p) do if names(name) and (not opts.type or opts.type == type) then - table.insert(t, p .. '/' .. name) + table.insert(t, join_paths(p, name)) end end return t @@ -164,7 +211,7 @@ function M.find(names, opts) test = function(p) local t = {} for _, name in ipairs(names) do - local f = p .. '/' .. name + local f = join_paths(p, name) local stat = vim.loop.fs_stat(f) if stat and (not opts.type or opts.type == stat.type) then t[#t + 1] = f @@ -201,7 +248,7 @@ function M.find(names, opts) end for other, type_ in M.dir(dir) do - local f = dir .. '/' .. other + local f = join_paths(dir, other) if type(names) == 'function' then if names(other) and (not opts.type or opts.type == type_) then if add(f) then @@ -251,6 +298,7 @@ function M.normalize(path) vim.validate({ path = { path, 's' } }) return ( path + :gsub('^~$', vim.loop.os_homedir()) :gsub('^~/', vim.loop.os_homedir() .. '/') :gsub('%$([%w_]+)', vim.loop.os_getenv) :gsub('\\', '/') -- cgit From 45d1b1c6788a5694850361b5ffa95d63eb75b2f5 Mon Sep 17 00:00:00 2001 From: kylo252 <59826753+kylo252@users.noreply.github.com> Date: Tue, 20 Dec 2022 18:04:47 +0100 Subject: vim-patch:9.0.1082: some jsonc files are not recognized (#21483) Problem: Some jsonc files are not recognized. Solution: Add patterns for jsonc and move some from json to jsonc. (closes vim/vim#11711) https://github.com/vim/vim/commit/104b2ff4d0ec9248ba0b979aa3bbccb65fcad422 Co-authored-by: kylo252 <59826753+kylo252@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 58acca42f7..b47013122d 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1454,11 +1454,15 @@ local filename = { ['ipf.conf'] = 'ipfilter', ['ipf6.conf'] = 'ipfilter', ['ipf.rules'] = 'ipfilter', - ['.eslintrc'] = 'json', - ['.babelrc'] = 'json', ['Pipfile.lock'] = 'json', ['.firebaserc'] = 'json', ['.prettierrc'] = 'json', + ['.babelrc'] = 'jsonc', + ['.eslintrc'] = 'jsonc', + ['.hintrc'] = 'jsonc', + ['.jsfmtrc'] = 'jsonc', + ['.jshintrc'] = 'jsonc', + ['.swrc'] = 'jsonc', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', ['lftp.conf'] = 'lftp', @@ -1912,6 +1916,7 @@ local pattern = { ['.*%.properties_.._..'] = 'jproperties', ['org%.eclipse%..*%.prefs'] = 'jproperties', ['.*%.properties_.._.._.*'] = starsetf('jproperties'), + ['[jt]sconfig.*%.json'] = 'jsonc', ['Kconfig%..*'] = starsetf('kconfig'), ['.*%.[Ss][Uu][Bb]'] = 'krl', ['lilo%.conf.*'] = starsetf('lilo'), -- cgit From 3ea1524cf8a7d80d1034011462fd9611fdf385f1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 23 Dec 2022 17:36:41 +0100 Subject: vim-patch:9.0.1090: FHIR Shorthand files are not recognized (#21515) Problem: FHIR Shorthand files are not recognized. Solution: Add a pattern to detect FSH files. (Matthew Gramigna, closes vim/vim#11738) https://github.com/vim/vim/commit/c9207d5d79310bd4628ce46d8db588fac17878a0 Co-authored-by: mgramigna --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index b47013122d..9952af821e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -419,6 +419,7 @@ local extension = { fs = function(path, bufnr) return require('vim.filetype.detect').fs(bufnr) end, + fsh = 'fsh', fsi = 'fsharp', fsx = 'fsharp', fusion = 'fusion', -- cgit From fe5665be3b49c4ebc1257de8b9c6ed81254fd762 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 26 Dec 2022 18:36:51 +0100 Subject: vim-patch:9.0.1103: jq files are not recognized (#21545) Problem: jq files are not recognized. Solution: Add detection of Jq files. (David McDonald, closes vim/vim#11743) https://github.com/vim/vim/commit/b9a1edfc5434f2a3ac50b1a178d3c85aa417b798 Co-authored-by: David McDonald --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 9952af821e..fb7bf53007 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -557,6 +557,7 @@ local extension = { jov = 'jovial', jovial = 'jovial', properties = 'jproperties', + jq = 'jq', slnf = 'json', json = 'json', jsonp = 'json', -- cgit From 3b9b43063c76e52b119aaac96e50c708d7d39479 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 28 Dec 2022 11:42:02 +0100 Subject: vim-patch:9.0.1106: not all postfix files are recognized (#21568) Problem: Not all postfix files are recognized. Solution: Recognize main.cf.proto files. (closes vim/vim#11732) https://github.com/vim/vim/commit/09ce0b8e1197c85dacf97e75b9b9ac18e0d192df Co-authored-by: KodeToad <3880336+KodeToad@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index fb7bf53007..7eec567b66 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1532,6 +1532,7 @@ local filename = { ['.latexmkrc'] = 'perl', ['pf.conf'] = 'pf', ['main.cf'] = 'pfmain', + ['main.cf.proto'] = 'pfmain', pinerc = 'pine', ['.pinercex'] = 'pine', ['.pinerc'] = 'pine', -- cgit From 469f9859238fa179448a31863e0f3894c9bc9c99 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Fri, 30 Dec 2022 09:40:23 -0600 Subject: fix(lsp): adjust gravity of semantic tokens extmarks (#21574) Fixes #21543 This should provide a better user experience when appending or prepending text to a word that has a semantic token extmark. More often than not, the appended/prepended text to the word will end up becoming part of the token anyway, so just use that extmark as the user types. --- runtime/lua/vim/lsp/semantic_tokens.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 48190b03e1..849a8a1b67 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,6 +408,8 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, + right_gravity = false, + end_right_gravity = true, strict = false, }) @@ -417,7 +419,9 @@ function STHighlighter:on_win(topline, botline) api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { hl_group = '@' .. modifier, end_col = token.end_col, - priority = vim.highlight.priorities.semantic_tokens, + priority = vim.highlight.priorities.semantic_tokens + 1, + right_gravity = false, + end_right_gravity = true, strict = false, }) end -- cgit From f62c30ad0d53fe79bbb00087609d76101371d122 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Dec 2022 23:42:18 +0800 Subject: fix(lsp): fix nil client access in get_active_clients (#21524) Fixes https://github.com/neovim/neovim/issues/21523 --- runtime/lua/vim/lsp.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index dc5008399e..ad0e599f61 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1845,7 +1845,8 @@ function lsp.get_active_clients(filter) for client_id in pairs(t) do local client = active_clients[client_id] if - (filter.id == nil or client.id == filter.id) + client + and (filter.id == nil or client.id == filter.id) and (filter.name == nil or client.name == filter.name) then clients[#clients + 1] = client -- cgit From 4ace9e7e417fe26c8b73ff1d6042e6e4f3df9ebf Mon Sep 17 00:00:00 2001 From: 李晓辉 Date: Sat, 31 Dec 2022 02:23:54 +0800 Subject: feat(diagnostic): don't open quickfix/loclist if no diagnostics #21397 --- runtime/lua/vim/diagnostic.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 84a8701ac7..b9aee7eaf3 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -478,13 +478,17 @@ local function set_list(loclist, opts) -- numbers beyond the end of the buffer local diagnostics = get_diagnostics(bufnr, opts, false) local items = M.toqflist(diagnostics) + if next(items) == nil then + vim.notify('No diagnostics available') + return + end if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) else vim.fn.setqflist({}, ' ', { title = title, items = items }) end if open then - api.nvim_command(loclist and 'lopen' or 'botright copen') + api.nvim_command(loclist and 'lwindow' or 'botright cwindow') end end -- cgit From 6ba34e21fee2a81677e8261dfeaf24c8cd320500 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 31 Dec 2022 16:16:21 +0100 Subject: feat(lsp): add function to clear codelens (#21504) Currently once you retrieve the lenses you're pretty much stuck with them as saving new lenses is additive. Adding a dedicated method to reset lenses allows users to toggle lenses on/off which can be useful for language servers where they are noisy or expensive and you only want to see them temporary. --- runtime/lua/vim/lsp/codelens.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 71b6bfae30..17489ed84d 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -108,13 +108,36 @@ function M.run() end end +---@private +local function resolve_bufnr(bufnr) + return bufnr == 0 and api.nvim_get_current_buf() or bufnr +end + +--- Clear the lenses +--- +---@param client_id number|nil filter by client_id. All clients if nil +---@param bufnr number|nil filter by buffer. All buffers if nil +function M.clear(client_id, bufnr) + local buffers = bufnr and { resolve_bufnr(bufnr) } or vim.tbl_keys(lens_cache_by_buf) + for _, iter_bufnr in pairs(buffers) do + local client_ids = client_id and { client_id } or vim.tbl_keys(namespaces) + for _, iter_client_id in pairs(client_ids) do + local ns = namespaces[iter_client_id] + lens_cache_by_buf[iter_bufnr][iter_client_id] = {} + api.nvim_buf_clear_namespace(iter_bufnr, ns, 0, -1) + end + end +end + --- Display the lenses using virtual text --- ---@param lenses table of lenses to display (`CodeLens[] | null`) ---@param bufnr number ---@param client_id number function M.display(lenses, bufnr, client_id) + local ns = namespaces[client_id] if not lenses or not next(lenses) then + api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) return end local lenses_by_lnum = {} @@ -126,7 +149,6 @@ function M.display(lenses, bufnr, client_id) end table.insert(line_lenses, lens) end - local ns = namespaces[client_id] local num_lines = api.nvim_buf_line_count(bufnr) for i = 0, num_lines do local line_lenses = lenses_by_lnum[i] or {} -- cgit From 85b7c8b004a36a15731a461e5e95f2c9fca732f1 Mon Sep 17 00:00:00 2001 From: Gautam Iyer Date: Sat, 31 Dec 2022 11:26:05 -0500 Subject: fix(filetype): correctly detect tex files Fixes Issue #21594. --- runtime/lua/vim/filetype/detect.lua | 39 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index ee538dc8c7..edffdde9c7 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1256,17 +1256,15 @@ end -- 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords. -- 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc. function M.tex(path, bufnr) - local format = getlines(bufnr, 1):find('^%%&%s*(%a+)') - if format then + local matched, _, format = getlines(bufnr, 1):find('^%%&%s*(%a+)') + if matched then format = format:lower():gsub('pdf', '', 1) - if format == 'tex' then - return 'tex' - elseif format == 'plaintex' then - return 'plaintex' - end elseif path:lower():find('tex/context/.*/.*%.tex') then return 'context' else + -- Default value, may be changed later: + format = vim.g.tex_flavor or 'plaintex' + local lpat = [[documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>]] local cpat = [[start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>]] @@ -1275,26 +1273,25 @@ function M.tex(path, bufnr) -- Find first non-comment line if not l:find('^%s*%%%S') then -- Check the next thousand lines for a LaTeX or ConTeXt keyword. - for _, line in ipairs(getlines(bufnr, i + 1, i + 1000)) do - local lpat_match, cpat_match = - matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)\|^\s*\\\(]] .. cpat .. [[\)]]) - if lpat_match then + for _, line in ipairs(getlines(bufnr, i, i + 1000)) do + if matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)]]) then return 'tex' - elseif cpat_match then + elseif matchregex(line, [[\c^\s*\\\%(]] .. cpat .. [[\)]]) then return 'context' end end end end - -- TODO: add AMSTeX, RevTex, others? - if not vim.g.tex_flavor or vim.g.tex_flavor == 'plain' then - return 'plaintex' - elseif vim.g.tex_flavor == 'context' then - return 'context' - else - -- Probably LaTeX - return 'tex' - end + end -- if matched + + -- Translation from formats to file types. TODO: add AMSTeX, RevTex, others? + if format == 'plain' then + return 'plaintex' + elseif format == 'plaintex' or format == 'context' then + return format + else + -- Probably LaTeX + return 'tex' end end -- cgit From dfb840970c36056584e9a55d77a2030b4e403e9d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 28 Dec 2022 14:20:42 +0100 Subject: docs(lua): fix treesitter parsing errors --- runtime/lua/vim/_editor.lua | 2 +- runtime/lua/vim/shared.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 2913a0ddc6..da8764fbd4 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -391,7 +391,7 @@ end ---@param pos2 integer[] (line, column) tuple marking end of region ---@param regtype string type of selection, see |setreg()| ---@param inclusive boolean indicating whether the selection is end-inclusive ----@return table region lua table of the form {linenr = {startcol,endcol}} +---@return table region Table of the form `{linenr = {startcol,endcol}}` function vim.region(bufnr, pos1, pos2, regtype, inclusive) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 5ffd11682c..6fc40bb905 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -113,7 +113,7 @@ end --- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs ({plain: boolean, trimempty: boolean}|nil) Keyword arguments: +---@param kwargs (table|nil) Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list -- cgit From 7b76a3e7992bffca758b2e52548d2f483a45eaf6 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jan 2023 18:07:27 +0800 Subject: refactor(diagnostic): DRY for loop #21521 Co-authored-by: Justin M. Keyes --- runtime/lua/vim/diagnostic.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index b9aee7eaf3..e2eb952eca 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -429,32 +429,31 @@ local function get_diagnostics(bufnr, opts, clamp) end end + ---@private + local function add_all_diags(buf, diags) + for _, diagnostic in pairs(diags) do + add(buf, diagnostic) + end + end + if namespace == nil and bufnr == nil then for b, t in pairs(diagnostic_cache) do for _, v in pairs(t) do - for _, diagnostic in pairs(v) do - add(b, diagnostic) - end + add_all_diags(b, v) end end elseif namespace == nil then bufnr = get_bufnr(bufnr) for iter_namespace in pairs(diagnostic_cache[bufnr]) do - for _, diagnostic in pairs(diagnostic_cache[bufnr][iter_namespace]) do - add(bufnr, diagnostic) - end + add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace]) end elseif bufnr == nil then for b, t in pairs(diagnostic_cache) do - for _, diagnostic in pairs(t[namespace] or {}) do - add(b, diagnostic) - end + add_all_diags(b, t[namespace] or {}) end else bufnr = get_bufnr(bufnr) - for _, diagnostic in pairs(diagnostic_cache[bufnr][namespace] or {}) do - add(bufnr, diagnostic) - end + add_all_diags(bufnr, diagnostic_cache[bufnr][namespace] or {}) end if opts.severity then -- cgit From 5b22b32e50858b781eb2658ba099eaffaa5ea13b Mon Sep 17 00:00:00 2001 From: Christian Segundo Date: Tue, 3 Jan 2023 13:44:44 +0100 Subject: fix(lsp): change vim.lsp.get_active_clients.filter name annotation to string (#21624) --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index ad0e599f61..1d99283dd9 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1822,7 +1822,7 @@ end ---@class vim.lsp.get_active_clients.filter ---@field id number|nil Match clients by id ---@field bufnr number|nil match clients attached to the given buffer ----@field name number|nil match clients by name +---@field name string|nil match clients by name --- Get active clients. --- -- cgit From 5eed3e741b39a7e52888b0400ba63a2ea70f4dac Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 3 Jan 2023 08:20:20 -0700 Subject: fix(diagnostic): revert notification on missing diagnostics (#21632) This reverts a change introduced in 4ace9e7e417fe26c8b73ff1d6042e6e4f3df9ebf. --- runtime/lua/vim/diagnostic.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index e2eb952eca..f41f4a9c68 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -477,10 +477,6 @@ local function set_list(loclist, opts) -- numbers beyond the end of the buffer local diagnostics = get_diagnostics(bufnr, opts, false) local items = M.toqflist(diagnostics) - if next(items) == nil then - vim.notify('No diagnostics available') - return - end if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) else -- cgit From 1b3c255f608a6c1a4a31bcd4a640ea6696403341 Mon Sep 17 00:00:00 2001 From: Eric Haynes Date: Tue, 3 Jan 2023 12:24:14 -0500 Subject: fix(fs): duplicate path separator #21509 Fixes #21497 --- runtime/lua/vim/fs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 89a1d0d345..65e6ca677c 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -74,7 +74,7 @@ end ---@private local function join_paths(...) - return table.concat({ ... }, '/') + return (table.concat({ ... }, '/'):gsub('//+', '/')) end --- Return an iterator over the files and directories located in {path} -- cgit From d6510eec4f5e4d1ca08f685f9ce7984ca9ab1c9e Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 3 Jan 2023 09:13:35 -0700 Subject: feat(editorconfig): add editorconfig syntax file This is intentionally _not_ copied from Vim because our syntax file makes use of Lua to dynamically generate a list of valid EditorConfig properties. This requires the builtin editorconfig module, which Vim does not have. --- runtime/lua/vim/filetype.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 7eec567b66..c3ab39a1a3 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1378,13 +1378,13 @@ local filename = { npmrc = 'dosini', ['/etc/yum.conf'] = 'dosini', ['.npmrc'] = 'dosini', - ['.editorconfig'] = 'dosini', ['/etc/pacman.conf'] = 'confini', ['mpv.conf'] = 'confini', dune = 'dune', jbuild = 'dune', ['dune-workspace'] = 'dune', ['dune-project'] = 'dune', + ['.editorconfig'] = 'editorconfig', ['elinks.conf'] = 'elinks', ['mix.lock'] = 'elixir', ['filter-rules'] = 'elmfilt', -- cgit From e35b9020b16985eee26e942f9a3f6b045bc3809b Mon Sep 17 00:00:00 2001 From: notomo Date: Wed, 4 Jan 2023 20:48:41 +0900 Subject: docs(lua): adjust some type annotations --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 2 +- runtime/lua/vim/lsp/util.lua | 6 +++--- runtime/lua/vim/shared.lua | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1d99283dd9..13f8c81dfb 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1805,7 +1805,7 @@ end --- already for this client, then force-shutdown is attempted. --- ---@param client_id number|table id or |vim.lsp.client| object, or list thereof ----@param force boolean (optional) shutdown forcefully +---@param force boolean|nil shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or { client_id } for _, id in ipairs(ids) do diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 226ed980fb..8f4bd15eaa 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -383,7 +383,7 @@ end --- Lists all the references to the symbol under the cursor in the quickfix window. --- ----@param context (table) Context for the request +---@param context (table|nil) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ---@param options table|nil additional options --- - on_list: (function) handler for list results. See |lsp-on-list-handler| diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2bd15c87d9..2c6ba823db 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1077,7 +1077,7 @@ end --- ---@param location table (`Location`|`LocationLink`) ---@param offset_encoding "utf-8" | "utf-16" | "utf-32" ----@param opts table options +---@param opts table|nil options --- - reuse_win (boolean) Jump to existing window if buffer is already open. --- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. ---@return boolean `true` if succeeded @@ -1134,7 +1134,7 @@ end --- ---@param location table (`Location`|`LocationLink`) ---@param offset_encoding "utf-8" | "utf-16" | "utf-32" ----@param reuse_win boolean Jump to existing window if buffer is already open. +---@param reuse_win boolean|nil Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) if offset_encoding == nil then @@ -1908,7 +1908,7 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ---@param window number|nil: 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` +---@param offset_encoding string|nil 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(window, offset_encoding) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 6fc40bb905..b53c66ba63 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -516,8 +516,8 @@ end --- ---@generic T ---@param list T[] (list) Table ----@param start number Start range of slice ----@param finish number End range of slice +---@param start number|nil Start range of slice +---@param finish number|nil End range of slice ---@return T[] (list) Copy of table sliced from start to finish (inclusive) function vim.list_slice(list, start, finish) local new_list = {} -- cgit From f7ad46e69ed407579517694ccf3dc1beffe7acdc Mon Sep 17 00:00:00 2001 From: 周 <1207190489@qq.com> Date: Fri, 6 Jan 2023 22:26:31 +0800 Subject: fix(lsp): correct callHierarchy capability to fix lsp.buf.incoming_calls() (#21665) Co-authored-by: maozhongzhou --- runtime/lua/vim/lsp/protocol.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index dfbd01b8f8..92cda0b34f 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -801,6 +801,9 @@ function protocol.make_client_capabilities() end)(), }, }, + callHierarchy = { + dynamicRegistration = false, + }, }, workspace = { symbol = { @@ -828,9 +831,6 @@ function protocol.make_client_capabilities() refreshSupport = true, }, }, - callHierarchy = { - dynamicRegistration = false, - }, experimental = nil, window = { workDoneProgress = true, -- cgit From 0b136a14dc215d94ceac6d72d9f9fcb1c3ea5bc9 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Sun, 8 Jan 2023 00:43:15 -0600 Subject: fix(lsp): partially revert semantic token gravity change from #21574 (#21680) --- runtime/lua/vim/lsp/semantic_tokens.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 849a8a1b67..b035ac43f4 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,7 +408,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, - right_gravity = false, end_right_gravity = true, strict = false, }) @@ -420,7 +419,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. modifier, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens + 1, - right_gravity = false, end_right_gravity = true, strict = false, }) -- cgit From d1b3611f03625467500195b38d3c4646a7bc81db Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 8 Jan 2023 09:23:08 +0100 Subject: feat(lsp): show active clients in :checkhealth vim.lsp (#21670) For users using vim.lsp.start it can be useful to get an overview of active client that is less verbose than a full `:lua =vim.lsp.get_active_clients()` --- runtime/lua/vim/lsp/health.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index ba730e3d6d..987707e661 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -2,8 +2,8 @@ local M = {} --- Performs a healthcheck for LSP function M.check() - local report_info = vim.fn['health#report_info'] - local report_warn = vim.fn['health#report_warn'] + local report_info = vim.health.report_info + local report_warn = vim.health.report_warn local log = require('vim.lsp.log') local current_log_level = log.get_level() @@ -27,6 +27,18 @@ function M.check() local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) report_fn(string.format('Log size: %d KB', log_size / 1000)) + + local clients = vim.lsp.get_active_clients() + vim.health.report_start('vim.lsp: Active Clients') + if next(clients) then + for _, client in pairs(clients) do + report_info( + string.format('%s (id=%s, root_dir=%s)', client.name, client.id, client.config.root_dir) + ) + end + else + report_info('No active clients') + end end return M -- cgit From 870ca1de52b240926b88f01afa697cd9b119bdac Mon Sep 17 00:00:00 2001 From: Sebastian Lyng Johansen Date: Tue, 10 Jan 2023 11:22:41 +0100 Subject: feat(float): open float relative to mouse #21531 Problem: No easy way to position a LSP hover window relative to mouse. Solution: Introduce another option to the `relative` key in `nvim_open_win()`. With this PR it should be possible to override the handler and do something similar to this https://github.com/neovim/neovim/pull/19481#issuecomment-1193248674 to have hover information displayed from the mouse. Test case: ```lua local util = require('vim.lsp.util') local function make_position_param(window, offset_encoding) window = window or 0 local buf = vim.api.nvim_win_get_buf(window) local row, col local mouse = vim.fn.getmousepos() row = mouse.line col = mouse.column offset_encoding = offset_encoding or util._get_offset_encoding(buf) row = row - 1 local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then return { line = 0, character = 0 } end if #line < col then return { line = 0, character = 0 } end col = util._str_utfindex_enc(line, col, offset_encoding) return { line = row, character = col } end local make_params = function(window, offset_encoding) window = window or 0 local buf = vim.api.nvim_win_get_buf(window) offset_encoding = offset_encoding or util._get_offset_encoding(buf) return { textDocument = util.make_text_document_params(buf), position = make_position_param(window, offset_encoding), } end local hover_timer = nil vim.o.mousemoveevent = true vim.keymap.set({ '', 'i' }, '', function() if hover_timer then hover_timer:close() end hover_timer = vim.defer_fn(function() hover_timer = nil local params = make_params() vim.lsp.buf_request( 0, 'textDocument/hover', params, vim.lsp.with(vim.lsp.handlers.hover, { silent = true, focusable = false, relative = 'mouse', }) ) end, 500) return '' end, { expr = true }) ``` --- runtime/lua/vim/lsp/handlers.lua | 4 +++- runtime/lua/vim/lsp/util.lua | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 80df83732e..b383ca1c35 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -335,7 +335,9 @@ function M.hover(_, result, ctx, config) return end if not (result and result.contents) then - vim.notify('No information available') + if config.silent ~= true then + vim.notify('No information available') + end return end local markdown_lines = util.convert_input_to_markdown_lines(result.contents) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2c6ba823db..26f0e180f5 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1015,6 +1015,7 @@ end --- - border (string or table) override `border` --- - focusable (string or table) override `focusable` --- - zindex (string or table) override `zindex`, defaults to 50 +--- - relative ("mouse"|"cursor") defaults to "cursor" ---@returns (table) Options function M.make_floating_popup_options(width, height, opts) validate({ @@ -1029,7 +1030,8 @@ function M.make_floating_popup_options(width, height, opts) local anchor = '' local row, col - local lines_above = vim.fn.winline() - 1 + local lines_above = opts.relative == 'mouse' and vim.fn.getmousepos().line - 1 + or vim.fn.winline() - 1 local lines_below = vim.fn.winheight(0) - lines_above if lines_above < lines_below then @@ -1042,7 +1044,9 @@ function M.make_floating_popup_options(width, height, opts) row = 0 end - if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then + local wincol = opts.relative == 'mouse' and vim.fn.getmousepos().column or vim.fn.wincol() + + if wincol + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then anchor = anchor .. 'W' col = 0 else @@ -1062,7 +1066,7 @@ function M.make_floating_popup_options(width, height, opts) col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = 'cursor', + relative = opts.relative == 'mouse' and 'mouse' or 'cursor', row = row + (opts.offset_y or 0), style = 'minimal', width = width, -- cgit From ea2658e1f7a0791f7bf5b1da2417ea0c618121fc Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Tue, 10 Jan 2023 16:29:25 -0500 Subject: vim-patch:9.0.1174: smali files are not recognized (#21734) Problem: Smali files are not recognized. Solution: Add a pattern for Smali files. (Amaan Qureshi, closes vim/vim#11801) --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index c3ab39a1a3..7a1b79bba2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -949,6 +949,7 @@ local extension = { ice = 'slice', score = 'slrnsc', sol = 'solidity', + smali = 'smali', tpl = 'smarty', ihlp = 'smcl', smcl = 'smcl', -- cgit From 8c5c2136fef12e6fe00739c91ff994344ab5d466 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 11 Jan 2023 17:15:54 +0100 Subject: vim-patch:9.0.1176: smithy files are not recognized (#21751) Problem: smithy files are not recognized. Solution: Add a pattern for Smithy files. (Chris Kipp, closes vim/vim#11804) https://github.com/vim/vim/commit/f68cddabffcbc5b8fbfe9003182cb4b55ff8d72c Co-authored-by: Chris Kipp --- runtime/lua/vim/filetype.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 7a1b79bba2..a6f40774d1 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -327,11 +327,7 @@ local extension = { end, eex = 'eelixir', leex = 'eelixir', - am = function(path, bufnr) - if not path:lower():find('makefile%.am$') then - return 'elf' - end - end, + am = 'elf', exs = 'elixir', elm = 'elm', elv = 'elvish', @@ -956,6 +952,7 @@ local extension = { hlp = 'smcl', smith = 'smith', smt = 'smith', + smithy = 'smithy', sml = 'sml', spt = 'snobol4', sno = 'snobol4', -- cgit From a37c686d21c1ad4e50f455e989642d38435d41ba Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Wed, 11 Jan 2023 20:17:10 +0100 Subject: docs(lsp): update buf_notify and rpc.notify params types (#21753) Small, but I was getting warnings about my usage of `vim.lsp.buf_notify(bufnr, method, {example = example})` since the docs say that `params` must be a string, however this can really be anything when it's passed to `rpc.notify` since we just end up calling `vim.json.encode(payload)` on it. This fixes the docs in those two places and regenerates them. --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/rpc.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 13f8c81dfb..cfd6c938f7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2048,7 +2048,7 @@ end --- Send a notification to a server ---@param bufnr (number|nil) The number of the buffer ---@param method (string) Name of the request method ----@param params (string) Arguments to send to the server +---@param params (any) Arguments to send to the server --- ---@returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index b93b227150..f1492601ff 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -293,7 +293,7 @@ end ---@private --- Sends a notification to the LSP server. ---@param method (string) The invoked LSP method ----@param params (table|nil): Parameters for the invoked LSP method +---@param params (any): Parameters for the invoked LSP method ---@returns (bool) `true` if notification could be sent, `false` if not function Client:notify(method, params) return self:encode_and_send({ -- cgit From 143d3f1f3224bca02bfef7df0932b9d7524a3ff2 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Thu, 12 Jan 2023 00:13:52 -0600 Subject: fix(lsp): revert semantic token gravity change from #21574 (#21763) --- runtime/lua/vim/lsp/semantic_tokens.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index b035ac43f4..b1bc48dac6 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,7 +408,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, - end_right_gravity = true, strict = false, }) @@ -419,7 +418,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. modifier, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens + 1, - end_right_gravity = true, strict = false, }) end -- cgit From 2aabe9b85870912b9eb8fe2ced0c21544de66f58 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 12 Jan 2023 09:21:58 +0100 Subject: vim-patch:9.0.1182: go checksum files are not recognized (#21758) Problem: go checksum files are not recognized. Solution: Add the name of go checksum files. (Amaan Qureshi, closes vim/vim#11803) https://github.com/vim/vim/commit/043d7b2c84cda275354aa023b5769660ea70a168 Co-authored-by: Amaan Q --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index a6f40774d1..169285686a 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1425,6 +1425,7 @@ local filename = { gnashpluginrc = 'gnash', gnashrc = 'gnash', ['.gnuplot'] = 'gnuplot', + ['go.sum'] = 'gosum', ['go.work'] = 'gowork', ['.gprc'] = 'gp', ['/.gnupg/gpg.conf'] = 'gpg', -- cgit From 443bbfd59e2818aeee72d2ed77af1cb24617e46f Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 12 Jan 2023 15:51:19 +0100 Subject: docs(lsp): fix type annotation on convert_input_to_markdown_lines (#21772) This small changes just ensures that if you're using `convert_input_to_markdown_lines` without `contents` you don't get a warning (when using something like neodev) that there is an expected second param, since it can be nil. --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 26f0e180f5..38051e6410 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -856,7 +856,7 @@ end --- `textDocument/signatureHelp`, and potentially others. --- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ----@param contents (table, optional, default `{}`) List of strings to extend with converted lines +---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. ---@returns {contents}, extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) -- cgit From 572cd8031dd7c91ac9e17cbdfab16d87f1fed6b7 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 13 Jan 2023 00:57:39 +0800 Subject: feat(diagnostic): vim.diagnostic.is_disabled() #21527 --- runtime/lua/vim/diagnostic.lua | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index f41f4a9c68..6fd000a029 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -251,19 +251,6 @@ local function get_bufnr(bufnr) return bufnr end ----@private -local function is_disabled(namespace, bufnr) - local ns = M.get_namespace(namespace) - if ns.disabled then - return true - end - - if type(diagnostic_disabled[bufnr]) == 'table' then - return diagnostic_disabled[bufnr][namespace] - end - return diagnostic_disabled[bufnr] -end - ---@private local function diagnostic_lines(diagnostics) if not diagnostics then @@ -1119,6 +1106,27 @@ function M.hide(namespace, bufnr) end end +--- Check whether diagnostics are disabled in a given buffer. +--- +---@param bufnr number|nil Buffer number, or 0 for current buffer. +---@param namespace number|nil Diagnostic namespace. When omitted, checks if +--- all diagnostics are disabled in {bufnr}. +--- Otherwise, only checks if diagnostics from +--- {namespace} are disabled. +---@return boolean +function M.is_disabled(bufnr, namespace) + bufnr = get_bufnr(bufnr) + if namespace and M.get_namespace(namespace).disabled then + return true + end + + if type(diagnostic_disabled[bufnr]) == 'table' then + return diagnostic_disabled[bufnr][namespace] + end + + return diagnostic_disabled[bufnr] ~= nil +end + --- Display diagnostics for the given namespace and buffer. --- ---@param namespace number|nil Diagnostic namespace. When omitted, show @@ -1162,7 +1170,7 @@ function M.show(namespace, bufnr, diagnostics, opts) return end - if is_disabled(namespace, bufnr) then + if M.is_disabled(bufnr, namespace) then return end -- cgit From fd3d30a22c2bcc7acd266214b761b44219aefbe7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 13 Jan 2023 17:27:35 +0100 Subject: vim-patch:9.0.1191: some Bazel files are not recognized (#21784) Problem: Some Bazel files are not recognized. Solution: Add an extra Bazel pattern. (Keith Smily, closes vim/vim#11807) https://github.com/vim/vim/commit/3213952966896ffb1d8fa186bcf8c43359fca0f0 Co-authored-by: Keith Smiley --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 169285686a..b73519f1be 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1321,6 +1321,7 @@ local filename = { ['GNUmakefile.am'] = 'automake', ['named.root'] = 'bindzone', WORKSPACE = 'bzl', + ['WORKSPACE.bzlmod'] = 'bzl', BUILD = 'bzl', ['cabal.project'] = 'cabalproject', ['cabal.config'] = 'cabalconfig', -- cgit From c752c853630898f38bcf46a9a5e9d83e41989eeb Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 15 Jan 2023 16:00:23 +0100 Subject: refactor: format with stylua (#21821) --- runtime/lua/vim/lsp.lua | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index cfd6c938f7..134a009a72 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1557,15 +1557,21 @@ end --- Notify all attached clients that a buffer has changed. local text_document_did_change_handler do - text_document_did_change_handler = - function(_, bufnr, changedtick, firstline, lastline, new_lastline) - -- 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 true - end - util.buf_versions[bufnr] = changedtick - changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + text_document_did_change_handler = function( + _, + bufnr, + changedtick, + firstline, + lastline, + new_lastline + ) + -- 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 true end + util.buf_versions[bufnr] = changedtick + changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + end end ---@private -- cgit From 34b973b1d9e3b0c6f546e3aa661c29edd5a1ab87 Mon Sep 17 00:00:00 2001 From: Naru Date: Mon, 16 Jan 2023 06:32:23 +0900 Subject: docs(lua): use luaref tag instead of www.lua.org #21813 --- runtime/lua/vim/shared.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index b53c66ba63..7967d13943 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -60,6 +60,7 @@ end)() --- Splits a string at each instance of a separator. --- ---@see |vim.split()| +---@see |luaref-patterns| ---@see https://www.lua.org/pil/20.2.html ---@see http://lua-users.org/wiki/StringLibraryTutorial --- @@ -529,6 +530,7 @@ end --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- +---@see |luaref-patterns| ---@see https://www.lua.org/pil/20.2.html ---@param s string String to trim ---@return string String with whitespace removed from its beginning and end -- cgit From 307efe4906de9f0b7d8611ea36bddb85493c1447 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Mon, 16 Jan 2023 04:55:24 -0500 Subject: health: migrate to Lua #21661 * refactor: remove all vimscript from nvim/health * fixup: previous method broke if you had a folder named 'x-lua' --- runtime/lua/vim/health.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index b875da0abc..044880e076 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -23,7 +23,20 @@ end local path2name = function(path) if path:match('%.lua$') then -- Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp" - return path:gsub('.-lua[%\\%/]', '', 1):gsub('[%\\%/]', '.'):gsub('%.health.-$', '') + + -- Get full path, make sure all slashes are '/' + path = vim.fs.normalize(path) + + -- Remove everything up to the last /lua/ folder + path = path:gsub('^.*/lua/', '') + + -- Remove the filename (health.lua) + path = vim.fn.fnamemodify(path, ':h') + + -- Change slashes to dots + path = path:gsub('/', '.') + + return path else -- Vim: transform "../autoload/health/provider.vim" into "provider" return vim.fn.fnamemodify(path, ':t:r') -- cgit From ef89f9fd46ab591183b7f59f31f5a2e55f7a526b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang <59727193+horriblename@users.noreply.github.com> Date: Mon, 16 Jan 2023 13:39:19 +0100 Subject: docs: treesitter.add_directive, add_predicate #21206 --- runtime/lua/vim/treesitter/query.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 4bec5db527..dbf134573d 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -419,7 +419,8 @@ local directive_handlers = { --- Adds a new predicate to be used in queries --- ---@param name string Name of the predicate, without leading # ----@param handler function(match:string, pattern:string, bufnr:number, predicate:function) +---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[]) +--- - see |vim.treesitter.query.add_directive()| for argument meanings function M.add_predicate(name, handler, force) if predicate_handlers[name] and not force then error(string.format('Overriding %s', name)) @@ -436,7 +437,12 @@ end --- metadata table `metadata[capture_id].key = value` --- ---@param name string Name of the directive, without leading # ----@param handler function(match:string, pattern:string, bufnr:number, predicate:function, metadata:table) +---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[], metadata:table) +--- - match: see |treesitter-query| +--- - node-level data are accessible via `match[capture_id]` +--- - pattern: see |treesitter-query| +--- - predicate: list of strings containing the full directive being called, e.g. +--- `(node (#set! conceal "-"))` would get the predicate `{ "#set!", "conceal", "-" }` function M.add_directive(name, handler, force) if directive_handlers[name] and not force then error(string.format('Overriding %s', name)) -- cgit From 20b7be2d1024b6e9eac68e0cb92cf663e95e51ca Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 17 Jan 2023 16:56:23 +0000 Subject: fix(treesitter): really restore syntax - also unset b:ts_highlight on stop() Fixes: #21836 --- runtime/lua/vim/treesitter/highlighter.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index e99994c8a9..d77a0d0d03 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -88,7 +88,6 @@ function TSHighlighter.new(tree, opts) end end - self.orig_syntax = vim.bo[self.bufnr].syntax self.orig_spelloptions = vim.bo[self.bufnr].spelloptions vim.bo[self.bufnr].syntax = '' @@ -120,8 +119,11 @@ function TSHighlighter:destroy() end if vim.api.nvim_buf_is_loaded(self.bufnr) then - vim.bo[self.bufnr].syntax = self.orig_syntax vim.bo[self.bufnr].spelloptions = self.orig_spelloptions + vim.b[self.bufnr].ts_highlight = nil + if vim.g.syntax_on == 1 then + a.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) + end end end -- cgit From d8d39344e38d27acccbab268bef19034ef7cd579 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 20 Jan 2023 14:59:31 +0800 Subject: fix(lsp): fix `removed` param value in add_workspace_folder (#21915) --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8f4bd15eaa..87b3ac6f1e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -487,7 +487,7 @@ function M.add_workspace_folder(workspace_folder) end local params = util.make_workspace_params( { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, - { {} } + {} ) for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do local found = false -- cgit From 7ef5e363d360f86c5d8d403e90ed256f4de798ec Mon Sep 17 00:00:00 2001 From: kishii <24446852+zhoudaxia2016@users.noreply.github.com> Date: Sat, 21 Jan 2023 15:22:34 +0800 Subject: feat(lsp): add triggerKind option for vim.lsp.buf.code_action (#21905) --- runtime/lua/vim/lsp/buf.lua | 5 +++++ runtime/lua/vim/lsp/protocol.lua | 11 +++++++++++ 2 files changed, 16 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 87b3ac6f1e..be237eb494 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -733,6 +733,7 @@ end --- List of LSP `CodeActionKind`s used to filter the code actions. --- Most language servers support values like `refactor` --- or `quickfix`. +--- - triggerKind (number|nil): The reason why code actions were requested. --- - filter: (function|nil) --- Predicate taking an `CodeAction` and returning a boolean. --- - apply: (boolean|nil) @@ -746,6 +747,7 @@ end --- using mark-like indexing. See |api-indexing| --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction +---@see vim.lsp.protocol.constants.CodeActionTriggerKind function M.code_action(options) validate({ options = { options, 't', true } }) options = options or {} @@ -755,6 +757,9 @@ function M.code_action(options) options = { options = options } end local context = options.context or {} + if not context.triggerKind then + context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked + end if not context.diagnostics then local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 92cda0b34f..12345b6c8c 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -304,6 +304,17 @@ local constants = { -- Base kind for an organize imports source action SourceOrganizeImports = 'source.organizeImports', }, + -- The reason why code actions were requested. + ---@enum lsp.CodeActionTriggerKind + CodeActionTriggerKind = { + -- Code actions were explicitly requested by the user or by an extension. + Invoked = 1, + -- Code actions were requested automatically. + -- + -- This typically happens when current selection in a file changes, but can + -- also be triggered when file content changes. + Automatic = 2, + }, } for k, v in pairs(constants) do -- cgit From 80bede1dfc6c2fcd5ec013045585802bb18d9fc9 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sun, 22 Jan 2023 09:08:33 -0500 Subject: vim-patch:9.0.1229: Cap'n Proto files are not recognized (#21950) Problem: Cap'n Proto files are not recognized. Solution: Add a pattern and the "capnp" filetype. (Amaan Qureshi, closes vim/vim#11862) https://github.com/vim/vim/commit/040e795e8da05ff38cc896528d4dcad100f0b584 --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index b73519f1be..34815f89ca 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -190,6 +190,7 @@ local extension = { BUILD = 'bzl', qc = 'c', cabal = 'cabal', + capnp = 'capnp', cdl = 'cdl', toc = 'cdrtoc', cfc = 'cf', -- cgit From 8e52d8a394799ede43ae5e80574f12dede9bc13e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 22 Jan 2023 18:54:09 +0100 Subject: fix(lsp): assert workspace/applyEdit receives params (#21945) According to the specification `workspace/applyEdit` must be called with `ApplyWorkspaceEditParams`. So far the client just returned, which could lead to a misleading error on the server side because `workspace/applyEdit` must respond with a `ApplyWorkspaceEditResult`. This adds an assertion to clarify that the server is violating the specification. See https://github.com/neovim/neovim/issues/21925 --- runtime/lua/vim/lsp/handlers.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index b383ca1c35..5096100a60 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -131,9 +131,10 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit M['workspace/applyEdit'] = function(_, workspace_edit, ctx) - if not workspace_edit then - return - end + assert( + workspace_edit, + 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' + ) -- TODO(ashkan) Do something more with label? local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) -- cgit From 1b642648f6c94c0bc0db521714a922207ef8bbc5 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sun, 22 Jan 2023 18:08:08 -0500 Subject: vim-patch:9.0.1230: Apache Thrift files are not recognized (#21955) Problem: Apache thrift files are not recognized. Solution: Add a pattern for thrift files. (Amaan Qureshi, closes vim/vim#11859) https://github.com/vim/vim/commit/f3da4c8427b1b12d7aaffa307ec085ca97ea9ad9 --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 34815f89ca..9293c828b8 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1022,6 +1022,7 @@ local extension = { texinfo = 'texinfo', text = 'text', tfvars = 'terraform-vars', + thrift = 'thrift', tla = 'tla', tli = 'tli', toml = 'toml', -- cgit From cb757f2663e6950e655c6306d713338dfa66b18d Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Mon, 23 Jan 2023 10:26:46 +0100 Subject: build: make generated source files reproducible #21586 Problem: Build is not reproducible, because generated source files (.c/.h/) are not deterministic, mostly because Lua pairs() is unordered by design (for security). https://github.com/LuaJIT/LuaJIT/issues/626#issuecomment-707005671 https://www.lua.org/manual/5.1/manual.html#pdf-next > The order in which the indices are enumerated is not specified [...] > >> The hardening of the VM deliberately randomizes string hashes. This in >> turn randomizes the iteration order of tables with string keys. Solution: - Update the code generation scripts to be deterministic. - That is only a partial solution: the exported function (funcs_metadata.generated.h) and ui event (ui_events_metadata.generated.h) metadata have some mpack'ed tables, which are not serialized deterministically. - As a workaround, introduce `PRG_GEN_LUA` cmake setting, so you can inject a modified build of luajit (with LUAJIT_SECURITY_PRN=0) that preserves table order. - Longer-term we should change the mpack'ed data structure so it no longer uses tables keyed by strings. Closes #20124 Co-Authored-By: dundargoc Co-Authored-By: Arnout Engelen --- runtime/lua/vim/shared.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 7967d13943..cc48e3f193 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -458,6 +458,33 @@ function vim.tbl_flatten(t) return result end +--- Enumerate a table sorted by its keys. +--- +---@see Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- +---@param t table List-like table +---@return iterator over sorted keys and their values +function vim.spairs(t) + assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) + + -- collect the keys + local keys = {} + for k in pairs(t) do + table.insert(keys, k) + end + table.sort(keys) + + -- Return the iterator function. + -- TODO(justinmk): Return "iterator function, table {t}, and nil", like pairs()? + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] + end + end +end + --- Tests if a Lua table can be treated as an array. --- --- Empty table `{}` is assumed to be an array, unless it was created by -- cgit From bcdbf77537cec4567a334a39fd5965a3f5668a1a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 24 Jan 2023 06:15:43 +0000 Subject: fix(lsp): check method is supported when range formatting (#21970) `vim.lsp.buf.format()` silently did nothing if no servers supported `textDocument/rangeFormatting` when formatting with a range. Issue found by `@hwrd:matrix.org` in the Matrix chat. --- runtime/lua/vim/lsp/buf.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index be237eb494..6ac885c78f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -197,20 +197,21 @@ function M.format(options) clients = vim.tbl_filter(options.filter, clients) end + local mode = api.nvim_get_mode().mode + local range = options.range + if not range and mode == 'v' or mode == 'V' then + range = range_from_selection() + end + local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' + clients = vim.tbl_filter(function(client) - return client.supports_method('textDocument/formatting') + return client.supports_method(method) end, clients) if #clients == 0 then vim.notify('[LSP] Format request failed, no matching language servers.') end - local mode = api.nvim_get_mode().mode - local range = options.range - if not range and mode == 'v' or mode == 'V' then - range = range_from_selection() - end - ---@private local function set_range(client, params) if range then @@ -221,7 +222,6 @@ function M.format(options) return params end - local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' if options.async then local do_format do_format = function(idx, client) -- cgit From 9166116c6730ef1687f35b8c58653583d2c301c3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 24 Jan 2023 18:04:15 +0000 Subject: doc(lsp): format arguments to start_client() (#21980) docs(lsp): format arguments to start_client() --- runtime/lua/vim/lsp.lua | 215 ++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 108 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 134a009a72..c5392ac154 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -897,115 +897,114 @@ end -- --- Starts and initializes a client with the given configuration. --- ---- Parameter `cmd` is required. ---- ---- The following parameters describe fields in the {config} table. ---- ---- ----@param cmd: (table|string|fun(dispatchers: table):table) command string or ---- list treated like |jobstart()|. The command must launch the language server ---- process. `cmd` can also be a function that creates an RPC client. ---- The function receives a dispatchers table and must return a table with the ---- functions `request`, `notify`, `is_closing` and `terminate` ---- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| ---- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| ---- ----@param cmd_cwd: (string, default=|getcwd()|) Directory to launch ---- the `cmd` process. Not related to `root_dir`. ---- ----@param cmd_env: (table) Environment flags to pass to the LSP on ---- spawn. Can be specified using keys like a map or as a list with `k=v` ---- pairs or both. Non-string values are coerced to string. ---- Example: ----
---- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
---- 
---- ----@param detached: (boolean, default true) Daemonize the server process so that it runs in a ---- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to ---- exit cleanly this could leave behind orphaned server processes. ---- ----@param workspace_folders (table) List of workspace folders passed to the ---- language server. For backwards compatibility rootUri and rootPath will be ---- derived from the first workspace folder in this list. See `workspaceFolders` in ---- the LSP spec. ---- ----@param capabilities Map overriding the default capabilities defined by ---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language ---- server on initialization. Hint: use make_client_capabilities() and modify ---- its result. ---- - Note: To send an empty dictionary use ---- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an ---- array. ---- ----@param handlers Map of language server method names to |lsp-handler| ---- ----@param settings Map with language server specific settings. These are ---- returned to the language server if requested via `workspace/configuration`. ---- Keys are case-sensitive. ---- ----@param commands table Table that maps string of clientside commands to user-defined functions. ---- Commands passed to start_client take precedence over the global command registry. Each key ---- must be a unique command name, and the value is a function which is called if any LSP action ---- (code action, code lenses, ...) triggers the command. ---- ----@param init_options Values to pass in the initialization request ---- as `initializationOptions`. See `initialize` in the LSP spec. ---- ----@param name (string, default=client-id) Name in log messages. ---- ----@param get_language_id function(bufnr, filetype) -> language ID as string. ---- Defaults to the filetype. ---- ----@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", ---- or "utf-32" which is the encoding that the LSP server expects. Client does ---- not verify this is correct. ---- ----@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.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 ---- parameters being sent to the server and `config` is the config that was ---- passed to |vim.lsp.start_client()|. You can use this to modify parameters before ---- they are sent. ---- ----@param on_init Callback (client, initialize_result) invoked after LSP ---- "initialize", where `result` is a table of `capabilities` and anything else ---- the server may send. For example, clangd sends ---- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was ---- sent to it. You can only modify the `client.offset_encoding` here before ---- any notifications are sent. Most language servers expect to be sent client specified settings after ---- initialization. Neovim does not make this assumption. A ---- `workspace/didChangeConfiguration` notification should be sent ---- to the server during on_init. ---- ----@param on_exit Callback (code, signal, client_id) invoked on client +--- Field `cmd` in {config} is required. +--- +---@param config (table) Configuration for the server: +--- - cmd: (table|string|fun(dispatchers: table):table) command string or +--- list treated like |jobstart()|. The command must launch the language server +--- process. `cmd` can also be a function that creates an RPC client. +--- The function receives a dispatchers table and must return a table with the +--- functions `request`, `notify`, `is_closing` and `terminate` +--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| +--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| +--- +--- - cmd_cwd: (string, default=|getcwd()|) Directory to launch +--- the `cmd` process. Not related to `root_dir`. +--- +--- - cmd_env: (table) Environment flags to pass to the LSP on +--- spawn. Can be specified using keys like a map or as a list with `k=v` +--- pairs or both. Non-string values are coerced to string. +--- Example: +---
+---                   { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+---       
+--- +--- - detached: (boolean, default true) Daemonize the server process so that it runs in a +--- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to +--- exit cleanly this could leave behind orphaned server processes. +--- +--- - workspace_folders: (table) List of workspace folders passed to the +--- language server. For backwards compatibility rootUri and rootPath will be +--- derived from the first workspace folder in this list. See `workspaceFolders` in +--- the LSP spec. +--- +--- - capabilities: Map overriding the default capabilities defined by +--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- server on initialization. Hint: use make_client_capabilities() and modify +--- its result. +--- - Note: To send an empty dictionary use +--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an +--- array. +--- +--- - handlers: Map of language server method names to |lsp-handler| +--- +--- - settings: Map with language server specific settings. These are +--- returned to the language server if requested via `workspace/configuration`. +--- Keys are case-sensitive. +--- +--- - commands: table Table that maps string of clientside commands to user-defined functions. +--- Commands passed to start_client take precedence over the global command registry. Each key +--- must be a unique command name, and the value is a function which is called if any LSP action +--- (code action, code lenses, ...) triggers the command. +--- +--- - init_options Values to pass in the initialization request +--- as `initializationOptions`. See `initialize` in the LSP spec. +--- +--- - name: (string, default=client-id) Name in log messages. +--- +--- - get_language_id: function(bufnr, filetype) -> language ID as string. +--- Defaults to the filetype. +--- +--- - offset_encoding: (default="utf-16") One of "utf-8", "utf-16", +--- or "utf-32" which is the encoding that the LSP server expects. Client does +--- not verify this is correct. +--- +--- - 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.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, where `params` contains the +--- parameters being sent to the server and `config` is the config that was +--- passed to |vim.lsp.start_client()|. You can use this to modify parameters before +--- they are sent. +--- +--- - on_init: Callback (client, initialize_result) invoked after LSP +--- "initialize", where `result` is a table of `capabilities` and anything else +--- the server may send. For example, clangd sends +--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was +--- sent to it. You can only modify the `client.offset_encoding` here before +--- any notifications are sent. Most language servers expect to be sent client specified settings after +--- initialization. Neovim does not make this assumption. A +--- `workspace/didChangeConfiguration` notification should be sent +--- to the server during on_init. +--- +--- - on_exit Callback (code, signal, client_id) invoked on client --- exit. ---- - code: exit code of the process ---- - signal: number describing the signal used to terminate (if any) ---- - client_id: client handle ---- ----@param on_attach Callback (client, bufnr) invoked when client ---- attaches to a buffer. ---- ----@param trace: "off" | "messages" | "verbose" | nil passed directly to the language ---- server in the initialize request. Invalid/empty values will default to "off" ----@param flags: A table with flags for the client. The current (experimental) flags are: ---- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits ---- - debounce_text_changes (number, default 150): Debounce didChange ---- notifications to the server by the given number in milliseconds. No debounce ---- occurs if nil ---- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to ---- exit cleanly after sending the "shutdown" request before sending kill -15. ---- If set to false, nvim exits immediately after sending the "shutdown" request to the server. ---- ----@param root_dir string Directory where the LSP ---- server will base its workspaceFolders, rootUri, and rootPath ---- on initialization. +--- - code: exit code of the process +--- - signal: number describing the signal used to terminate (if any) +--- - client_id: client handle +--- +--- - on_attach: Callback (client, bufnr) invoked when client +--- attaches to a buffer. +--- +--- - trace: ("off" | "messages" | "verbose" | nil) passed directly to the language +--- server in the initialize request. Invalid/empty values will default to "off" +--- +--- - flags: A table with flags for the client. The current (experimental) flags are: +--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits +--- - debounce_text_changes (number, default 150): Debounce didChange +--- notifications to the server by the given number in milliseconds. No debounce +--- occurs if nil +--- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to +--- exit cleanly after sending the "shutdown" request before sending kill -15. +--- If set to false, nvim exits immediately after sending the "shutdown" request to the server. +--- +--- - root_dir: (string) Directory where the LSP +--- server will base its workspaceFolders, rootUri, and rootPath +--- on initialization. --- ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once -- cgit From 314d3ce1eb1da284baf9b33f30500473afe73144 Mon Sep 17 00:00:00 2001 From: "C.D. MacEachern" Date: Tue, 24 Jan 2023 17:45:30 -0500 Subject: docs(vim.fs): normalize Windows example was incorrect (#21966) --- runtime/lua/vim/fs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 65e6ca677c..a0d2c4c339 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -282,7 +282,7 @@ end --- --- Examples: ---
lua
----   vim.fs.normalize('C:\\Users\\jdoe')
+---   vim.fs.normalize('C:\\\\Users\\\\jdoe')
 ---   --> 'C:/Users/jdoe'
 ---
 ---   vim.fs.normalize('~/src/neovim')
-- 
cgit