Compare commits

...

8 Commits

Author SHA1 Message Date
Yi Ming 3fdcba2647
Merge fb0a481038 into 435dee74bb 2024-04-27 07:55:25 +07:00
zeertzjq 435dee74bb
vim-patch:9.1.0374: wrong botline in BufEnter (#28530)
Problem:  When :edit an existing buffer, line('w$') may return a
          wrong result.
Solution: Reset w_valid in curwin_init() (Jaehwang Jung)

`do_ecmd()` reinitializes the current window (`curwin_init()`) whose
`w_valid` field may have `VALID_BOTLINE` set. Resetting `w_botline`
without marking it as invalid makes subsequent `validate_botline()`
calls a no-op, thus resulting in wrong `line('w$')` value.

closes: vim/vim#14642

eb80b8304e

Co-authored-by: Jaehwang Jung <tomtomjhj@gmail.com>
2024-04-27 06:32:25 +08:00
zeertzjq 694756252b
Merge pull request #28529 from zeertzjq/vim-fe1e2b5e2d65
vim-patch: clarify syntax vs matching mechanism
2024-04-27 06:31:55 +08:00
zeertzjq e81eb34aa1 vim-patch:9525f6213604
runtime(doc): fix typo synconcealend -> synconcealed (vim/vim#14644)

9525f62136

Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-04-27 05:52:47 +08:00
zeertzjq a1568f5df0 vim-patch:00ae5c5cba7b
runtime(doc): fix typo

00ae5c5cba

Co-authored-by: Christian Brabandt <cb@256bit.org>
2024-04-27 05:52:15 +08:00
zeertzjq f1f5fb911b vim-patch:fe1e2b5e2d65
runtime(doc): clarify syntax vs matching mechanism

fixes: vim/vim#14643

fe1e2b5e2d

Co-authored-by: Christian Brabandt <cb@256bit.org>
2024-04-27 05:51:52 +08:00
Yi Ming fb0a481038 feat(lsp): allow enable/disable inlay_hint by `client_id` 2024-04-26 23:21:46 +08:00
Yi Ming 1eea321689 refactor(lsp): distinguish the namespace of different clients 2024-04-26 22:11:37 +08:00
11 changed files with 175 additions and 73 deletions

View File

@ -8208,6 +8208,10 @@ synconcealed({lnum}, {col}) *synconcealed()*
synconcealed(lnum, 5) [1, 'X', 2]
synconcealed(lnum, 6) [0, '', 0]
Note: Doesn't consider |matchadd()| highlighting items,
since syntax and matching highlighting are two different
mechanisms |syntax-vs-match|.
synstack({lnum}, {col}) *synstack()*
Return a |List|, which is the stack of syntax items at the
position {lnum} and {col} in the current window. {lnum} is

View File

@ -1611,6 +1611,7 @@ enable({enable}, {filter}) *vim.lsp.inlay_hint.enable()*
• {filter} (`table?`) Optional filters |kwargs|, or `nil` for all.
• {bufnr} (`integer?`) Buffer number, or 0/nil for current
buffer.
• {client_id} (`integer?`) Client id, or nil for all.
get({filter}) *vim.lsp.inlay_hint.get()*
Get the list of inlay hints, (optionally) restricted by buffer or range.
@ -1635,6 +1636,7 @@ get({filter}) *vim.lsp.inlay_hint.get()*
Parameters: ~
• {filter} (`table?`) Optional filters |kwargs|:
• {bufnr} (`integer?`)
• {client_id} (`integer?`)
• {range} (`lsp.Range?`)
Return: ~
@ -1643,13 +1645,14 @@ get({filter}) *vim.lsp.inlay_hint.get()*
• {client_id} (`integer`)
• {inlay_hint} (`lsp.InlayHint`)
is_enabled({bufnr}) *vim.lsp.inlay_hint.is_enabled()*
is_enabled({bufnr}, {client_id}) *vim.lsp.inlay_hint.is_enabled()*
Note: ~
• This API is pre-release (unstable).
Parameters: ~
• {bufnr} (`integer?`) Buffer handle, or 0 for current
• {bufnr} (`integer?`) Buffer handle, or 0 for current
• {client_id} (`integer?`) Client id, or nil for all
Return: ~
(`boolean`)

View File

@ -1375,6 +1375,19 @@ Finally, these constructs are unique to Perl:
==============================================================================
10. Highlighting matches *match-highlight*
*syntax-vs-match*
Note that the match highlight mechanism is independent
of |syntax-highlighting|, which is (usually) a buffer-local
highlighting, while matching is window-local, both methods
can be freely mixed. Match highlighting functions give you
a bit more flexibility in when and how to apply, but are
typically only used for temporary highlighting, without strict
rules. Both methods can be used to conceal text.
Thus the matching functions like |matchadd()| won't consider
syntax rules and functions like |synconcealed()| and the
other way around.
*:mat* *:match*
:mat[ch] {group} /{pattern}/
Define a pattern to highlight in the current window. It will

View File

@ -3825,7 +3825,9 @@ Whether or not it is actually concealed depends on the value of the
'conceallevel' option. The 'concealcursor' option is used to decide whether
concealable items in the current line are displayed unconcealed to be able to
edit the line.
Another way to conceal text is with |matchadd()|.
Another way to conceal text is with |matchadd()|, but internally this works a
bit differently |syntax-vs-match|.
concealends *:syn-concealends*
@ -3833,7 +3835,9 @@ When the "concealends" argument is given, the start and end matches of
the region, but not the contents of the region, are marked as concealable.
Whether or not they are actually concealed depends on the setting on the
'conceallevel' option. The ends of a region can only be concealed separately
in this way when they have their own highlighting via "matchgroup"
in this way when they have their own highlighting via "matchgroup". The
|synconcealed()| function can be used to retrieve information about conealed
items.
cchar *:syn-cchar*
*E844*

View File

@ -948,7 +948,7 @@ Syntax and highlighting: *syntax-functions* *highlighting-functions*
synIDattr() get a specific attribute of a syntax ID
synIDtrans() get translated syntax ID
synstack() get list of syntax IDs at a specific position
synconcealed() get info about concealing
synconcealed() get info about (syntax) concealing
diff_hlID() get highlight ID for diff mode at a position
matchadd() define a pattern to highlight (a "match")
matchaddpos() define a list of positions to highlight

View File

@ -9752,6 +9752,10 @@ function vim.fn.synIDtrans(synID) end
--- synconcealed(lnum, 5) [1, 'X', 2]
--- synconcealed(lnum, 6) [0, '', 0]
---
--- Note: Doesn't consider |matchadd()| highlighting items,
--- since syntax and matching highlighting are two different
--- mechanisms |syntax-vs-match|.
---
--- @param lnum integer
--- @param col integer
--- @return {[1]: integer, [2]: string, [3]: integer}

View File

@ -8,11 +8,15 @@ local M = {}
---@field version? integer
---@field client_hints? table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints)
---@field applied table<integer, integer> Last version of hints applied to this line
---@field enabled boolean Whether inlay hints are enabled for this buffer
---@field enabled boolean|integer[] The client_id of inlay_hint is enabled on this buffer, true for all
---@type table<integer, vim.lsp.inlay_hint.bufstate>
local bufstates = {}
local namespace = api.nvim_create_namespace('vim_lsp_inlayhint')
---@type table<integer, integer> client_id -> namespace
local namespaces = vim.defaulttable(function(client_id)
return api.nvim_create_namespace('vim_lsp_inlayhint:' .. client_id)
end)
local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {})
--- |lsp-handler| for the method `textDocument/inlayHint`
@ -107,6 +111,7 @@ end
--- @class vim.lsp.inlay_hint.get.Filter
--- @inlinedoc
--- @field bufnr integer?
--- @field client_id integer?
--- @field range lsp.Range?
--- @class vim.lsp.inlay_hint.get.ret
@ -165,6 +170,10 @@ function M.get(filter)
if #clients == 0 then
return {}
end
---@param c vim.lsp.Client
clients = vim.tbl_filter(function(c)
return not filter.client_id or c.id == filter.client_id
end, clients)
local range = filter.range
if not range then
@ -202,7 +211,8 @@ end
--- Clear inlay hints
---@param bufnr (integer) Buffer handle, or 0 for current
local function clear(bufnr)
---@param client_id (integer|nil) Client id, or nil for all
local function clear(bufnr, client_id)
if bufnr == nil or bufnr == 0 then
bufnr = api.nvim_get_current_buf()
end
@ -217,41 +227,58 @@ local function clear(bufnr)
bufstate.client_hints[iter_client_id] = {}
end
end
api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
if client_id then
api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], 0, -1)
else
for _, namespace in pairs(namespaces) do
api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
end
end
api.nvim__buf_redraw_range(bufnr, 0, -1)
end
--- Disable inlay hints for a buffer
---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current
local function _disable(bufnr)
---@param client_id (integer|nil) Client id, or nil for all
local function _disable(bufnr, client_id)
if bufnr == nil or bufnr == 0 then
bufnr = api.nvim_get_current_buf()
end
clear(bufnr)
clear(bufnr, client_id)
if bufstates[bufnr] then
bufstates[bufnr] = { enabled = false, applied = {} }
local enabled = bufstates[bufnr].enabled
if client_id and type(enabled) == 'table' and #enabled ~= 1 then
vim.list_remove(enabled, client_id)
bufstates[bufnr] = { enabled = enabled, applied = {} }
else
bufstates[bufnr] = { enabled = false, applied = {} }
end
end
end
--- Refresh inlay hints, only if we have attached clients that support it
---@param bufnr (integer) Buffer handle, or 0 for current
---@param client_id (integer|nil) Client id, or nil for all
---@param opts? vim.lsp.util._refresh.Opts Additional options to pass to util._refresh
---@private
local function _refresh(bufnr, opts)
local function _refresh(bufnr, client_id, opts)
opts = opts or {}
opts['bufnr'] = bufnr
opts['client_id'] = client_id
util._refresh(ms.textDocument_inlayHint, opts)
end
--- Enable inlay hints for a buffer
---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current
local function _enable(bufnr)
---@param client_id (integer|nil) Client id, or nil for all
local function _enable(bufnr, client_id)
if bufnr == nil or bufnr == 0 then
bufnr = api.nvim_get_current_buf()
end
local bufstate = bufstates[bufnr]
if not bufstate then
bufstates[bufnr] = { applied = {}, enabled = true }
bufstates[bufnr] = { applied = {}, enabled = client_id and { client_id } or true }
api.nvim_create_autocmd('LspNotify', {
buffer = bufnr,
callback = function(opts)
@ -262,7 +289,10 @@ local function _enable(bufnr)
return
end
if bufstates[bufnr] and bufstates[bufnr].enabled then
_refresh(bufnr, { client_id = opts.data.client_id })
local enabled = bufstates[bufnr].enabled
if type(enabled) ~= 'table' or vim.tbl_contains(enabled, opts.data.client_id) then
_refresh(bufnr, opts.data.client_id)
end
end
end,
group = augroup,
@ -296,72 +326,97 @@ local function _enable(bufnr)
group = augroup,
})
else
bufstate.enabled = true
local enabled = bufstate.enabled
if type(enabled) == 'table' then
if not vim.tbl_contains(enabled, client_id) then
table.insert(enabled, client_id)
end
else
bufstate.enabled = true
end
_refresh(bufnr)
end
end
api.nvim_set_decoration_provider(namespace, {
on_win = function(_, _, bufnr, topline, botline)
local bufstate = bufstates[bufnr]
if not bufstate then
return
end
if bufstate.version ~= util.buf_versions[bufnr] then
return
end
if not bufstate.client_hints then
return
end
local client_hints = assert(bufstate.client_hints)
for lnum = topline, botline do
if bufstate.applied[lnum] ~= bufstate.version then
api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1)
for _, lnum_hints in pairs(client_hints) do
local hints = lnum_hints[lnum] or {}
for _, hint in pairs(hints) do
local text = ''
local label = hint.label
if type(label) == 'string' then
text = label
else
for _, part in ipairs(label) do
text = text .. part.value
end
end
local vt = {} --- @type {[1]: string, [2]: string?}[]
if hint.paddingLeft then
vt[#vt + 1] = { ' ' }
end
vt[#vt + 1] = { text, 'LspInlayHint' }
if hint.paddingRight then
vt[#vt + 1] = { ' ' }
end
api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, {
virt_text_pos = 'inline',
ephemeral = false,
virt_text = vt,
})
end
end
bufstate.applied[lnum] = bufstate.version
do
local namespace = api.nvim_create_namespace('vim_lsp_inlayhint')
api.nvim_set_decoration_provider(namespace, {
on_win = function(_, _, bufnr, topline, botline)
local bufstate = bufstates[bufnr]
if not bufstate then
return
end
end
end,
})
if bufstate.version ~= util.buf_versions[bufnr] then
return
end
if not bufstate.client_hints then
return
end
local client_hints = assert(bufstate.client_hints)
for lnum = topline, botline do
if bufstate.applied[lnum] ~= bufstate.version then
for client_id, lnum_hints in pairs(client_hints) do
api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], lnum, lnum + 1)
local hints = lnum_hints[lnum] or {}
for _, hint in pairs(hints) do
local text = ''
local label = hint.label
if type(label) == 'string' then
text = label
else
for _, part in ipairs(label) do
text = text .. part.value
end
end
local vt = {} --- @type {[1]: string, [2]: string?}[]
if hint.paddingLeft then
vt[#vt + 1] = { ' ' }
end
vt[#vt + 1] = { text, 'LspInlayHint' }
if hint.paddingRight then
vt[#vt + 1] = { ' ' }
end
api.nvim_buf_set_extmark(
bufnr,
namespaces[client_id],
lnum,
hint.position.character,
{
virt_text_pos = 'inline',
ephemeral = false,
virt_text = vt,
}
)
end
end
bufstate.applied[lnum] = bufstate.version
end
end
end,
})
end
--- @param bufnr (integer|nil) Buffer handle, or 0 for current
--- @param client_id (integer|nil) Client id, or nil for all
--- @return boolean
--- @since 12
function M.is_enabled(bufnr)
function M.is_enabled(bufnr, client_id)
vim.validate({ bufnr = { bufnr, 'number', true } })
if bufnr == nil or bufnr == 0 then
bufnr = api.nvim_get_current_buf()
end
return bufstates[bufnr] and bufstates[bufnr].enabled or false
if bufstates[bufnr] then
local enabled = bufstates[bufnr].enabled
if type(enabled) == 'table' then
return vim.tbl_contains(enabled, client_id)
end
return enabled
else
return false
end
end
--- Optional filters |kwargs|, or `nil` for all.
@ -369,6 +424,8 @@ end
--- @inlinedoc
--- Buffer number, or 0/nil for current buffer.
--- @field bufnr integer?
--- Client id, or nil for all.
--- @field client_id integer?
--- Enables or disables inlay hints for a buffer.
---
@ -394,9 +451,9 @@ function M.enable(enable, filter)
vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } })
filter = filter or {}
if enable == false then
_disable(filter.bufnr)
_disable(filter.bufnr, filter.client_id)
else
_enable(filter.bufnr)
_enable(filter.bufnr, filter.client_id)
end
end

View File

@ -47,8 +47,8 @@ typedef struct {
#define VALID_VIRTCOL 0x04 // w_virtcol (file col) is valid
#define VALID_CHEIGHT 0x08 // w_cline_height and w_cline_folded valid
#define VALID_CROW 0x10 // w_cline_row is valid
#define VALID_BOTLINE 0x20 // w_botine and w_empty_rows are valid
#define VALID_BOTLINE_AP 0x40 // w_botine is approximated
#define VALID_BOTLINE 0x20 // w_botline and w_empty_rows are valid
#define VALID_BOTLINE_AP 0x40 // w_botline is approximated
#define VALID_TOPLINE 0x80 // w_topline is valid (for cursor position)
// flags for b_flags

View File

@ -11621,6 +11621,10 @@ M.funcs = {
synconcealed(lnum, 4) [1, 'X', 2]
synconcealed(lnum, 5) [1, 'X', 2]
synconcealed(lnum, 6) [0, '', 0]
Note: Doesn't consider |matchadd()| highlighting items,
since syntax and matching highlighting are two different
mechanisms |syntax-vs-match|.
]=],
name = 'synconcealed',
params = { { 'lnum', 'integer' }, { 'col', 'integer' } },

View File

@ -2467,6 +2467,7 @@ void win_init_empty(win_T *wp)
wp->w_topline = 1;
wp->w_topfill = 0;
wp->w_botline = 2;
wp->w_valid = 0;
wp->w_s = &wp->w_buffer->b_s;
}

View File

@ -4105,4 +4105,16 @@ func Test_SwapExists_set_other_buf_modified()
bwipe!
endfunc
func Test_BufEnter_botline()
set hidden
call writefile(range(10), 'Xxx1', 'D')
call writefile(range(20), 'Xxx2', 'D')
edit Xxx1
edit Xxx2
au BufEnter Xxx1 call assert_true(line('w$') > 1)
edit Xxx1
au! BufEnter Xxx1
set hidden&vim
endfunc
" vim: shiftwidth=2 sts=2 expandtab