mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat(lsp)!: add rule-based sem token highlighting (#22022)
feat(lsp)!: change semantic token highlighting Change the default highlights used, and add more highlights per token. Add an LspTokenUpdate event and a highlight_token function. :Inspect now shows any highlights applied by token highlighting rules, default or user-defined. BREAKING CHANGE: change the default highlight groups used by semantic token highlighting.
This commit is contained in:
parent
98f2df931a
commit
1cc23e1109
@ -482,6 +482,71 @@ LspSignatureActiveParameter
|
|||||||
Used to highlight the active parameter in the signature help. See
|
Used to highlight the active parameter in the signature help. See
|
||||||
|vim.lsp.handlers.signature_help()|.
|
|vim.lsp.handlers.signature_help()|.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
LSP SEMANTIC HIGHLIGHTS *lsp-semantic-highlight*
|
||||||
|
|
||||||
|
When available, the LSP client highlights code using |lsp-semantic_tokens|,
|
||||||
|
which are another way that LSP servers can provide information about source
|
||||||
|
code. Note that this is in addition to treesitter syntax highlighting;
|
||||||
|
semantic highlighting does not replace syntax highlighting.
|
||||||
|
|
||||||
|
The server will typically provide one token per identifier in the source code.
|
||||||
|
The token will have a `type` such as "function" or "variable", and 0 or more
|
||||||
|
`modifier`s such as "readonly" or "deprecated." The standard types and
|
||||||
|
modifiers are described here:
|
||||||
|
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens
|
||||||
|
LSP servers may also use off-spec types and modifiers.
|
||||||
|
|
||||||
|
The LSP client adds one or more highlights for each token. The highlight
|
||||||
|
groups are derived from the token's type and modifiers:
|
||||||
|
• `@lsp.type.<type>.<ft>` for the type
|
||||||
|
• `@lsp.mod.<mod>.<ft>` for each modifier
|
||||||
|
• `@lsp.typemod.<type>.<mod>.<ft>` for each modifier
|
||||||
|
Use |:Inspect| to view the higlights for a specific token. Use |:hi| or
|
||||||
|
|nvim_set_hl()| to change the appearance of semantic highlights: >vim
|
||||||
|
|
||||||
|
hi @lsp.type.function guifg=Yellow " function names are yellow
|
||||||
|
hi @lsp.type.variable.lua guifg=Green " variables in lua are green
|
||||||
|
hi @lsp.mod.deprecated gui=strikethrough " deprecated is crossed out
|
||||||
|
hi @lsp.typemod.function.async guifg=Blue " async functions are blue
|
||||||
|
<
|
||||||
|
The value |vim.highlight.priorities|`.semantic_tokens` is the priority of the
|
||||||
|
`@lsp.type.*` highlights. The `@lsp.mod.*` and `@lsp.typemod.*` highlights
|
||||||
|
have priorities one and two higher, respectively.
|
||||||
|
|
||||||
|
You can disable semantic highlights by clearing the highlight groups: >lua
|
||||||
|
|
||||||
|
-- Hide semantic highlights for functions
|
||||||
|
vim.api.nvim_set_hl(0, '@lsp.type.function', {})
|
||||||
|
|
||||||
|
-- Hide all semantic highlights
|
||||||
|
for _, group in ipairs(vim.fn.getcompletion("@lsp", "highlight")) do
|
||||||
|
vim.api.nvim_set_hl(0, group, {})
|
||||||
|
end
|
||||||
|
<
|
||||||
|
You probably want these inside a |ColorScheme| autocommand.
|
||||||
|
|
||||||
|
Use |LspTokenUpdate| and |vim.lsp.semantic_tokens.highlight_token()| for more
|
||||||
|
complex highlighting.
|
||||||
|
|
||||||
|
The following groups are linked by default to standard |group-name|s:
|
||||||
|
>
|
||||||
|
@lsp.type.class Structure
|
||||||
|
@lsp.type.decorator Function
|
||||||
|
@lsp.type.enum Structure
|
||||||
|
@lsp.type.enumMember Constant
|
||||||
|
@lsp.type.function Function
|
||||||
|
@lsp.type.interface Structure
|
||||||
|
@lsp.type.macro Macro
|
||||||
|
@lsp.type.method Function
|
||||||
|
@lsp.type.namespace Structure
|
||||||
|
@lsp.type.parameter Identifier
|
||||||
|
@lsp.type.property Identifier
|
||||||
|
@lsp.type.struct Structure
|
||||||
|
@lsp.type.type Type
|
||||||
|
@lsp.type.typeParameter TypeDef
|
||||||
|
@lsp.type.variable Identifier
|
||||||
|
<
|
||||||
==============================================================================
|
==============================================================================
|
||||||
EVENTS *lsp-events*
|
EVENTS *lsp-events*
|
||||||
|
|
||||||
@ -516,6 +581,29 @@ callback in the "data" table. Example: >lua
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
<
|
<
|
||||||
|
|
||||||
|
LspTokenUpdate *LspTokenUpdate*
|
||||||
|
|
||||||
|
When a visible semantic token is sent or updated by the LSP server, or when an
|
||||||
|
existing token becomes visible for the first time. The |autocmd-pattern| is
|
||||||
|
the name of the buffer. When used from Lua, the token and client ID are passed
|
||||||
|
to the callback in the "data" table. The token fields are documented in
|
||||||
|
|vim.lsp.semantic_tokens.get_at_pos()|. Example: >lua
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd('LspTokenUpdate', {
|
||||||
|
callback = function(args)
|
||||||
|
local token = args.data.token
|
||||||
|
if token.type == 'variable' and not token.modifiers.readonly then
|
||||||
|
vim.lsp.semantic_tokens.highlight_token(
|
||||||
|
token, args.buf, args.data.client_id, 'MyMutableVariableHighlight'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
<
|
||||||
|
Note: doing anything other than calling
|
||||||
|
|vim.lsp.semantic_tokens.highlight_token()| is considered experimental.
|
||||||
|
|
||||||
Also the following |User| |autocommand|s are provided:
|
Also the following |User| |autocommand|s are provided:
|
||||||
|
|
||||||
LspProgressUpdate *LspProgressUpdate*
|
LspProgressUpdate *LspProgressUpdate*
|
||||||
@ -1332,7 +1420,8 @@ force_refresh({bufnr}) *vim.lsp.semantic_tokens.force_refresh()*
|
|||||||
highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
|
highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
• {bufnr} (nil|number) default: current buffer
|
• {bufnr} (number|nil) filter by buffer. All buffers if nil, current
|
||||||
|
buffer if 0
|
||||||
|
|
||||||
*vim.lsp.semantic_tokens.get_at_pos()*
|
*vim.lsp.semantic_tokens.get_at_pos()*
|
||||||
get_at_pos({bufnr}, {row}, {col})
|
get_at_pos({bufnr}, {row}, {col})
|
||||||
@ -1345,7 +1434,34 @@ get_at_pos({bufnr}, {row}, {col})
|
|||||||
• {col} (number|nil) Position column (default cursor position)
|
• {col} (number|nil) Position column (default cursor position)
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(table|nil) List of tokens at position
|
(table|nil) List of tokens at position. Each token has the following
|
||||||
|
fields:
|
||||||
|
• line (number) line number, 0-based
|
||||||
|
• start_col (number) start column, 0-based
|
||||||
|
• end_col (number) end column, 0-based
|
||||||
|
• type (string) token type as string, e.g. "variable"
|
||||||
|
• modifiers (table) token modifiers as a set. E.g., { static = true,
|
||||||
|
readonly = true }
|
||||||
|
|
||||||
|
*vim.lsp.semantic_tokens.highlight_token()*
|
||||||
|
highlight_token({token}, {bufnr}, {client_id}, {hl_group}, {opts})
|
||||||
|
Highlight a semantic token.
|
||||||
|
|
||||||
|
Apply an extmark with a given highlight group for a semantic token. The
|
||||||
|
mark will be deleted by the semantic token engine when appropriate; for
|
||||||
|
example, when the LSP sends updated tokens. This function is intended for
|
||||||
|
use inside |LspTokenUpdate| callbacks.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {token} (table) a semantic token, found as `args.data.token` in
|
||||||
|
|LspTokenUpdate|.
|
||||||
|
• {bufnr} (number) the buffer to highlight
|
||||||
|
• {client_id} (number) The ID of the |vim.lsp.client|
|
||||||
|
• {hl_group} (string) Highlight group name
|
||||||
|
• {opts} (table|nil) Optional parameters.
|
||||||
|
• priority: (number|nil) Priority for the applied
|
||||||
|
extmark. Defaults to
|
||||||
|
`vim.highlight.priorities.semantic_tokens + 3`
|
||||||
|
|
||||||
start({bufnr}, {client_id}, {opts}) *vim.lsp.semantic_tokens.start()*
|
start({bufnr}, {client_id}, {opts}) *vim.lsp.semantic_tokens.start()*
|
||||||
Start the semantic token highlighting engine for the given buffer with the
|
Start the semantic token highlighting engine for the given buffer with the
|
||||||
|
@ -90,7 +90,7 @@ The following new APIs or features were added.
|
|||||||
`semanticTokensProvider` from the LSP client's {server_capabilities} in the
|
`semanticTokensProvider` from the LSP client's {server_capabilities} in the
|
||||||
`LspAttach` callback.
|
`LspAttach` callback.
|
||||||
|
|
||||||
See |lsp-semantic_tokens| for more information.
|
See |lsp-semantic-highlight| for more information.
|
||||||
|
|
||||||
• |vim.treesitter.inspect_tree()| and |:InspectTree| opens a split window
|
• |vim.treesitter.inspect_tree()| and |:InspectTree| opens a split window
|
||||||
showing a text representation of the nodes in a language tree for the current
|
showing a text representation of the nodes in a language tree for the current
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
---@field syntax boolean include syntax based highlight groups (defaults to true)
|
---@field syntax boolean include syntax based highlight groups (defaults to true)
|
||||||
---@field treesitter boolean include treesitter 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 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)
|
---@field semantic_tokens boolean include semantic token highlights (defaults to true)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
syntax = true,
|
syntax = true,
|
||||||
treesitter = true,
|
treesitter = true,
|
||||||
@ -81,47 +81,54 @@ function vim.inspect_pos(bufnr, row, col, filter)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- semantic tokens
|
--- Convert an extmark tuple into a map-like table
|
||||||
if filter.semantic_tokens then
|
--- @private
|
||||||
for _, token in ipairs(vim.lsp.semantic_tokens.get_at_pos(bufnr, row, col) or {}) do
|
local function to_map(extmark)
|
||||||
token.hl_groups = {
|
extmark = {
|
||||||
type = resolve_hl({ hl_group = '@' .. token.type }),
|
id = extmark[1],
|
||||||
modifiers = vim.tbl_map(function(modifier)
|
row = extmark[2],
|
||||||
return resolve_hl({ hl_group = '@' .. modifier })
|
col = extmark[3],
|
||||||
end, token.modifiers or {}),
|
opts = resolve_hl(extmark[4]),
|
||||||
}
|
}
|
||||||
table.insert(results.semantic_tokens, token)
|
extmark.end_row = extmark.opts.end_row or extmark.row -- inclusive
|
||||||
end
|
extmark.end_col = extmark.opts.end_col or (extmark.col + 1) -- exclusive
|
||||||
|
return extmark
|
||||||
end
|
end
|
||||||
|
|
||||||
-- extmarks
|
--- Check if an extmark overlaps this position
|
||||||
if filter.extmarks then
|
--- @private
|
||||||
for ns, nsid in pairs(vim.api.nvim_get_namespaces()) do
|
local function is_here(extmark)
|
||||||
if ns:find('vim_lsp_semantic_tokens') ~= 1 then
|
return (row >= extmark.row and row <= extmark.end_row) -- within the rows of the extmark
|
||||||
local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, nsid, 0, -1, { details = true })
|
and (row > extmark.row or col >= extmark.col) -- either not the first row, or in range of the col
|
||||||
for _, extmark in ipairs(extmarks) do
|
and (row < extmark.end_row or col < extmark.end_col) -- either not in the last row or in range of the col
|
||||||
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
|
end
|
||||||
|
|
||||||
|
-- all extmarks at this position
|
||||||
|
local extmarks = {}
|
||||||
|
for ns, nsid in pairs(vim.api.nvim_get_namespaces()) do
|
||||||
|
local ns_marks = vim.api.nvim_buf_get_extmarks(bufnr, nsid, 0, -1, { details = true })
|
||||||
|
ns_marks = vim.tbl_map(to_map, ns_marks)
|
||||||
|
ns_marks = vim.tbl_filter(is_here, ns_marks)
|
||||||
|
for _, mark in ipairs(ns_marks) do
|
||||||
|
mark.ns_id = nsid
|
||||||
|
mark.ns = ns
|
||||||
|
end
|
||||||
|
vim.list_extend(extmarks, ns_marks)
|
||||||
|
end
|
||||||
|
|
||||||
|
if filter.semantic_tokens then
|
||||||
|
results.semantic_tokens = vim.tbl_filter(function(extmark)
|
||||||
|
return extmark.ns:find('vim_lsp_semantic_tokens') == 1
|
||||||
|
end, extmarks)
|
||||||
|
end
|
||||||
|
|
||||||
|
if filter.extmarks then
|
||||||
|
results.extmarks = vim.tbl_filter(function(extmark)
|
||||||
|
return extmark.ns:find('vim_lsp_semantic_tokens') ~= 1
|
||||||
|
and (filter.extmarks == 'all' or extmark.opts.hl_group)
|
||||||
|
end, extmarks)
|
||||||
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -174,16 +181,17 @@ function vim.show_pos(bufnr, row, col, filter)
|
|||||||
nl()
|
nl()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- semantic tokens
|
||||||
if #items.semantic_tokens > 0 then
|
if #items.semantic_tokens > 0 then
|
||||||
append('Semantic Tokens', 'Title')
|
append('Semantic Tokens', 'Title')
|
||||||
nl()
|
nl()
|
||||||
for _, token in ipairs(items.semantic_tokens) do
|
local sorted_marks = vim.fn.sort(items.semantic_tokens, function(left, right)
|
||||||
local client = vim.lsp.get_client_by_id(token.client_id)
|
local left_first = left.opts.priority < right.opts.priority
|
||||||
client = client and (' (' .. client.name .. ')') or ''
|
or left.opts.priority == right.opts.priority and left.opts.hl_group < right.opts.hl_group
|
||||||
item(token.hl_groups.type, 'type' .. client)
|
return left_first and -1 or 1
|
||||||
for _, modifier in ipairs(token.hl_groups.modifiers) do
|
end)
|
||||||
item(modifier, 'modifier' .. client)
|
for _, extmark in ipairs(sorted_marks) do
|
||||||
end
|
item(extmark.opts, 'priority: ' .. extmark.opts.priority)
|
||||||
end
|
end
|
||||||
nl()
|
nl()
|
||||||
end
|
end
|
||||||
@ -197,6 +205,7 @@ function vim.show_pos(bufnr, row, col, filter)
|
|||||||
end
|
end
|
||||||
nl()
|
nl()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- extmarks
|
-- extmarks
|
||||||
if #items.extmarks > 0 then
|
if #items.extmarks > 0 then
|
||||||
append('Extmarks', 'Title')
|
append('Extmarks', 'Title')
|
||||||
|
@ -8,8 +8,8 @@ local bit = require('bit')
|
|||||||
--- @field start_col number start column 0-based
|
--- @field start_col number start column 0-based
|
||||||
--- @field end_col number end column 0-based
|
--- @field end_col number end column 0-based
|
||||||
--- @field type string token type as string
|
--- @field type string token type as string
|
||||||
--- @field modifiers string[] token modifiers as strings
|
--- @field modifiers table token modifiers as a set. E.g., { static = true, readonly = true }
|
||||||
--- @field extmark_added boolean whether this extmark has been added to the buffer yet
|
--- @field marked boolean whether this token has had extmarks applied
|
||||||
---
|
---
|
||||||
--- @class STCurrentResult
|
--- @class STCurrentResult
|
||||||
--- @field version number document version associated with this result
|
--- @field version number document version associated with this result
|
||||||
@ -36,10 +36,13 @@ local bit = require('bit')
|
|||||||
---@field client_state table<number, STClientState>
|
---@field client_state table<number, STClientState>
|
||||||
local STHighlighter = { active = {} }
|
local STHighlighter = { active = {} }
|
||||||
|
|
||||||
|
--- Do a binary search of the tokens in the half-open range [lo, hi).
|
||||||
|
---
|
||||||
|
--- Return the index i in range such that tokens[j].line < line for all j < i, and
|
||||||
|
--- tokens[j].line >= line for all j >= i, or return hi if no such index is found.
|
||||||
|
---
|
||||||
---@private
|
---@private
|
||||||
local function binary_search(tokens, line)
|
local function lower_bound(tokens, line, lo, hi)
|
||||||
local lo = 1
|
|
||||||
local hi = #tokens
|
|
||||||
while lo < hi do
|
while lo < hi do
|
||||||
local mid = math.floor((lo + hi) / 2)
|
local mid = math.floor((lo + hi) / 2)
|
||||||
if tokens[mid].line < line then
|
if tokens[mid].line < line then
|
||||||
@ -51,16 +54,34 @@ local function binary_search(tokens, line)
|
|||||||
return lo
|
return lo
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Do a binary search of the tokens in the half-open range [lo, hi).
|
||||||
|
---
|
||||||
|
--- Return the index i in range such that tokens[j].line <= line for all j < i, and
|
||||||
|
--- tokens[j].line > line for all j >= i, or return hi if no such index is found.
|
||||||
|
---
|
||||||
|
---@private
|
||||||
|
local function upper_bound(tokens, line, lo, hi)
|
||||||
|
while lo < hi do
|
||||||
|
local mid = math.floor((lo + hi) / 2)
|
||||||
|
if line < tokens[mid].line then
|
||||||
|
hi = mid
|
||||||
|
else
|
||||||
|
lo = mid + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return lo
|
||||||
|
end
|
||||||
|
|
||||||
--- Extracts modifier strings from the encoded number in the token array
|
--- Extracts modifier strings from the encoded number in the token array
|
||||||
---
|
---
|
||||||
---@private
|
---@private
|
||||||
---@return string[]
|
---@return table<string, boolean>
|
||||||
local function modifiers_from_number(x, modifiers_table)
|
local function modifiers_from_number(x, modifiers_table)
|
||||||
local modifiers = {}
|
local modifiers = {}
|
||||||
local idx = 1
|
local idx = 1
|
||||||
while x > 0 do
|
while x > 0 do
|
||||||
if bit.band(x, 1) == 1 then
|
if bit.band(x, 1) == 1 then
|
||||||
modifiers[#modifiers + 1] = modifiers_table[idx]
|
modifiers[modifiers_table[idx]] = true
|
||||||
end
|
end
|
||||||
x = bit.rshift(x, 1)
|
x = bit.rshift(x, 1)
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
@ -109,7 +130,7 @@ local function tokens_to_ranges(data, bufnr, client)
|
|||||||
end_col = end_col,
|
end_col = end_col,
|
||||||
type = token_type,
|
type = token_type,
|
||||||
modifiers = modifiers,
|
modifiers = modifiers,
|
||||||
extmark_added = false,
|
marked = false,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -355,7 +376,7 @@ end
|
|||||||
---
|
---
|
||||||
---@private
|
---@private
|
||||||
function STHighlighter:on_win(topline, botline)
|
function STHighlighter:on_win(topline, botline)
|
||||||
for _, state in pairs(self.client_state) do
|
for client_id, state in pairs(self.client_state) do
|
||||||
local current_result = state.current_result
|
local current_result = state.current_result
|
||||||
if current_result.version and current_result.version == util.buf_versions[self.bufnr] then
|
if current_result.version and current_result.version == util.buf_versions[self.bufnr] then
|
||||||
if not current_result.namespace_cleared then
|
if not current_result.namespace_cleared then
|
||||||
@ -372,52 +393,55 @@ function STHighlighter:on_win(topline, botline)
|
|||||||
--
|
--
|
||||||
-- Instead, we have to use normal extmarks that can attach to locations
|
-- Instead, we have to use normal extmarks that can attach to locations
|
||||||
-- in the buffer and are persisted between redraws.
|
-- in the buffer and are persisted between redraws.
|
||||||
|
--
|
||||||
|
-- `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.
|
||||||
|
|
||||||
|
local set_mark = function(token, hl_group, delta)
|
||||||
|
vim.api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, {
|
||||||
|
hl_group = hl_group,
|
||||||
|
end_col = token.end_col,
|
||||||
|
priority = vim.highlight.priorities.semantic_tokens + delta,
|
||||||
|
strict = false,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local ft = vim.bo[self.bufnr].filetype
|
||||||
local highlights = current_result.highlights
|
local highlights = current_result.highlights
|
||||||
local idx = binary_search(highlights, topline)
|
local first = lower_bound(highlights, topline, 1, #highlights + 1)
|
||||||
|
local last = upper_bound(highlights, botline, first, #highlights + 1) - 1
|
||||||
|
|
||||||
for i = idx, #highlights do
|
for i = first, last do
|
||||||
local token = highlights[i]
|
local token = highlights[i]
|
||||||
|
if not token.marked then
|
||||||
if token.line > botline then
|
set_mark(token, string.format('@lsp.type.%s.%s', token.type, ft), 0)
|
||||||
break
|
for modifier, _ in pairs(token.modifiers) do
|
||||||
end
|
set_mark(token, string.format('@lsp.mod.%s.%s', modifier, ft), 1)
|
||||||
|
set_mark(token, string.format('@lsp.typemod.%s.%s.%s', token.type, modifier, ft), 2)
|
||||||
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(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 + 1,
|
|
||||||
strict = false,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
token.marked = true
|
||||||
|
|
||||||
token.extmark_added = true
|
api.nvim_exec_autocmds('LspTokenUpdate', {
|
||||||
|
pattern = vim.api.nvim_buf_get_name(self.bufnr),
|
||||||
|
modeline = false,
|
||||||
|
data = {
|
||||||
|
token = token,
|
||||||
|
client_id = client_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -588,7 +612,13 @@ end
|
|||||||
---@param row number|nil Position row (default cursor position)
|
---@param row number|nil Position row (default cursor position)
|
||||||
---@param col number|nil Position column (default cursor position)
|
---@param col number|nil Position column (default cursor position)
|
||||||
---
|
---
|
||||||
---@return table|nil (table|nil) List of tokens at position
|
---@return table|nil (table|nil) List of tokens at position. Each token has
|
||||||
|
--- the following fields:
|
||||||
|
--- - line (number) line number, 0-based
|
||||||
|
--- - start_col (number) start column, 0-based
|
||||||
|
--- - end_col (number) end column, 0-based
|
||||||
|
--- - type (string) token type as string, e.g. "variable"
|
||||||
|
--- - modifiers (table) token modifiers as a set. E.g., { static = true, readonly = true }
|
||||||
function M.get_at_pos(bufnr, row, col)
|
function M.get_at_pos(bufnr, row, col)
|
||||||
if bufnr == nil or bufnr == 0 then
|
if bufnr == nil or bufnr == 0 then
|
||||||
bufnr = api.nvim_get_current_buf()
|
bufnr = api.nvim_get_current_buf()
|
||||||
@ -608,7 +638,7 @@ function M.get_at_pos(bufnr, row, col)
|
|||||||
for client_id, client in pairs(highlighter.client_state) do
|
for client_id, client in pairs(highlighter.client_state) do
|
||||||
local highlights = client.current_result.highlights
|
local highlights = client.current_result.highlights
|
||||||
if highlights then
|
if highlights then
|
||||||
local idx = binary_search(highlights, row)
|
local idx = lower_bound(highlights, row, 1, #highlights + 1)
|
||||||
for i = idx, #highlights do
|
for i = idx, #highlights do
|
||||||
local token = highlights[i]
|
local token = highlights[i]
|
||||||
|
|
||||||
@ -631,23 +661,60 @@ end
|
|||||||
--- Only has an effect if the buffer is currently active for semantic token
|
--- Only has an effect if the buffer is currently active for semantic token
|
||||||
--- highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
|
--- highlighting (|vim.lsp.semantic_tokens.start()| has been called for it)
|
||||||
---
|
---
|
||||||
---@param bufnr (nil|number) default: current buffer
|
---@param bufnr (number|nil) filter by buffer. All buffers if nil, current
|
||||||
|
--- buffer if 0
|
||||||
function M.force_refresh(bufnr)
|
function M.force_refresh(bufnr)
|
||||||
vim.validate({
|
vim.validate({
|
||||||
bufnr = { bufnr, 'n', true },
|
bufnr = { bufnr, 'n', true },
|
||||||
})
|
})
|
||||||
|
|
||||||
if bufnr == nil or bufnr == 0 then
|
local buffers = bufnr == nil and vim.tbl_keys(STHighlighter.active)
|
||||||
bufnr = api.nvim_get_current_buf()
|
or bufnr == 0 and { api.nvim_get_current_buf() }
|
||||||
end
|
or { bufnr }
|
||||||
|
|
||||||
|
for _, buffer in ipairs(buffers) do
|
||||||
|
local highlighter = STHighlighter.active[buffer]
|
||||||
|
if highlighter then
|
||||||
|
highlighter:reset()
|
||||||
|
highlighter:send_request()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Highlight a semantic token.
|
||||||
|
---
|
||||||
|
--- Apply an extmark with a given highlight group for a semantic token. The
|
||||||
|
--- mark will be deleted by the semantic token engine when appropriate; for
|
||||||
|
--- example, when the LSP sends updated tokens. This function is intended for
|
||||||
|
--- use inside |LspTokenUpdate| callbacks.
|
||||||
|
---@param token (table) a semantic token, found as `args.data.token` in
|
||||||
|
--- |LspTokenUpdate|.
|
||||||
|
---@param bufnr (number) the buffer to highlight
|
||||||
|
---@param client_id (number) The ID of the |vim.lsp.client|
|
||||||
|
---@param hl_group (string) Highlight group name
|
||||||
|
---@param opts (table|nil) Optional parameters.
|
||||||
|
--- - priority: (number|nil) Priority for the applied extmark. Defaults
|
||||||
|
--- to `vim.highlight.priorities.semantic_tokens + 3`
|
||||||
|
function M.highlight_token(token, bufnr, client_id, hl_group, opts)
|
||||||
local highlighter = STHighlighter.active[bufnr]
|
local highlighter = STHighlighter.active[bufnr]
|
||||||
if not highlighter then
|
if not highlighter then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
highlighter:reset()
|
local state = highlighter.client_state[client_id]
|
||||||
highlighter:send_request()
|
if not state then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
opts = opts or {}
|
||||||
|
local priority = opts.priority or vim.highlight.priorities.semantic_tokens + 3
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, state.namespace, token.line, token.start_col, {
|
||||||
|
hl_group = hl_group,
|
||||||
|
end_col = token.end_col,
|
||||||
|
priority = priority,
|
||||||
|
strict = false,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- |lsp-handler| for the method `workspace/semanticTokens/refresh`
|
--- |lsp-handler| for the method `workspace/semanticTokens/refresh`
|
||||||
|
@ -72,6 +72,7 @@ return {
|
|||||||
'InsertLeavePre', -- just before leaving Insert mode
|
'InsertLeavePre', -- just before leaving Insert mode
|
||||||
'LspAttach', -- after an LSP client attaches to a buffer
|
'LspAttach', -- after an LSP client attaches to a buffer
|
||||||
'LspDetach', -- after an LSP client detaches from a buffer
|
'LspDetach', -- after an LSP client detaches from a buffer
|
||||||
|
'LspTokenUpdate', -- after a visible LSP token is updated
|
||||||
'MenuPopup', -- just before popup menu is displayed
|
'MenuPopup', -- just before popup menu is displayed
|
||||||
'ModeChanged', -- after changing the mode
|
'ModeChanged', -- after changing the mode
|
||||||
'OptionSet', -- after setting any option
|
'OptionSet', -- after setting any option
|
||||||
@ -151,6 +152,7 @@ return {
|
|||||||
DiagnosticChanged=true,
|
DiagnosticChanged=true,
|
||||||
LspAttach=true,
|
LspAttach=true,
|
||||||
LspDetach=true,
|
LspDetach=true,
|
||||||
|
LspTokenUpdate=true,
|
||||||
RecordingEnter=true,
|
RecordingEnter=true,
|
||||||
RecordingLeave=true,
|
RecordingLeave=true,
|
||||||
Signal=true,
|
Signal=true,
|
||||||
|
@ -270,16 +270,22 @@ static const char *highlight_init_both[] = {
|
|||||||
"default link @tag Tag",
|
"default link @tag Tag",
|
||||||
|
|
||||||
// LSP semantic tokens
|
// LSP semantic tokens
|
||||||
"default link @class Structure",
|
"default link @lsp.type.class Structure",
|
||||||
"default link @struct Structure",
|
"default link @lsp.type.decorator Function",
|
||||||
"default link @enum Type",
|
"default link @lsp.type.enum Structure",
|
||||||
"default link @enumMember Constant",
|
"default link @lsp.type.enumMember Constant",
|
||||||
"default link @event Identifier",
|
"default link @lsp.type.function Function",
|
||||||
"default link @interface Identifier",
|
"default link @lsp.type.interface Structure",
|
||||||
"default link @modifier Identifier",
|
"default link @lsp.type.macro Macro",
|
||||||
"default link @regexp SpecialChar",
|
"default link @lsp.type.method Function",
|
||||||
"default link @typeParameter Type",
|
"default link @lsp.type.namespace Structure",
|
||||||
"default link @decorator Identifier",
|
"default link @lsp.type.parameter Identifier",
|
||||||
|
"default link @lsp.type.property Identifier",
|
||||||
|
"default link @lsp.type.struct Structure",
|
||||||
|
"default link @lsp.type.type Type",
|
||||||
|
"default link @lsp.type.typeParameter TypeDef",
|
||||||
|
"default link @lsp.type.variable Identifier",
|
||||||
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,10 +37,12 @@ describe('semantic token highlighting', function()
|
|||||||
[6] = { foreground = Screen.colors.Blue1 };
|
[6] = { foreground = Screen.colors.Blue1 };
|
||||||
[7] = { bold = true, foreground = Screen.colors.DarkCyan };
|
[7] = { bold = true, foreground = Screen.colors.DarkCyan };
|
||||||
[8] = { bold = true, foreground = Screen.colors.SlateBlue };
|
[8] = { bold = true, foreground = Screen.colors.SlateBlue };
|
||||||
|
[9] = { bold = true, foreground = tonumber('0x6a0dad') };
|
||||||
}
|
}
|
||||||
command([[ hi link @namespace Type ]])
|
command([[ hi link @lsp.type.namespace Type ]])
|
||||||
command([[ hi link @function Special ]])
|
command([[ hi link @lsp.type.function Special ]])
|
||||||
command([[ hi @declaration gui=bold ]])
|
command([[ hi link @lsp.type.comment Comment ]])
|
||||||
|
command([[ hi @lsp.mod.declaration gui=bold ]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('general', function()
|
describe('general', function()
|
||||||
@ -129,6 +131,46 @@ describe('semantic token highlighting', function()
|
|||||||
]] }
|
]] }
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('use LspTokenUpdate and highlight_token', function()
|
||||||
|
exec_lua([[
|
||||||
|
vim.api.nvim_create_autocmd("LspTokenUpdate", {
|
||||||
|
callback = function(args)
|
||||||
|
local token = args.data.token
|
||||||
|
if token.type == "function" and token.modifiers.declaration then
|
||||||
|
vim.lsp.semantic_tokens.highlight_token(
|
||||||
|
token, args.buf, args.data.client_id, "Macro"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_win_set_buf(0, bufnr)
|
||||||
|
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||||
|
]])
|
||||||
|
|
||||||
|
insert(text)
|
||||||
|
|
||||||
|
screen:expect { grid = [[
|
||||||
|
#include <iostream> |
|
||||||
|
|
|
||||||
|
int {9:main}() |
|
||||||
|
{ |
|
||||||
|
int {7:x}; |
|
||||||
|
#ifdef {5:__cplusplus} |
|
||||||
|
{4:std}::{2:cout} << {2:x} << "\n"; |
|
||||||
|
{6:#else} |
|
||||||
|
{6: printf("%d\n", x);} |
|
||||||
|
{6:#endif} |
|
||||||
|
} |
|
||||||
|
^} |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]] }
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
it('buffer is unhighlighted when client is detached', function()
|
it('buffer is unhighlighted when client is detached', function()
|
||||||
exec_lua([[
|
exec_lua([[
|
||||||
bufnr = vim.api.nvim_get_current_buf()
|
bufnr = vim.api.nvim_get_current_buf()
|
||||||
@ -580,14 +622,11 @@ describe('semantic token highlighting', function()
|
|||||||
expected = {
|
expected = {
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
modifiers = {
|
modifiers = { declaration = true, globalScope = true },
|
||||||
'declaration',
|
|
||||||
'globalScope',
|
|
||||||
},
|
|
||||||
start_col = 6,
|
start_col = 6,
|
||||||
end_col = 9,
|
end_col = 9,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -615,67 +654,67 @@ int main()
|
|||||||
expected = {
|
expected = {
|
||||||
{ -- main
|
{ -- main
|
||||||
line = 1,
|
line = 1,
|
||||||
modifiers = { 'declaration', 'globalScope' },
|
modifiers = { declaration = true, globalScope = true },
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 8,
|
end_col = 8,
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- __cplusplus
|
{ -- __cplusplus
|
||||||
line = 3,
|
line = 3,
|
||||||
modifiers = { 'globalScope' },
|
modifiers = { globalScope = true },
|
||||||
start_col = 9,
|
start_col = 9,
|
||||||
end_col = 20,
|
end_col = 20,
|
||||||
type = 'macro',
|
type = 'macro',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- x
|
{ -- x
|
||||||
line = 4,
|
line = 4,
|
||||||
modifiers = { 'declaration', 'readonly', 'functionScope' },
|
modifiers = { declaration = true, readonly = true, functionScope = true },
|
||||||
start_col = 12,
|
start_col = 12,
|
||||||
end_col = 13,
|
end_col = 13,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- std
|
{ -- std
|
||||||
line = 5,
|
line = 5,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
start_col = 2,
|
start_col = 2,
|
||||||
end_col = 5,
|
end_col = 5,
|
||||||
type = 'namespace',
|
type = 'namespace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- cout
|
{ -- cout
|
||||||
line = 5,
|
line = 5,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
start_col = 7,
|
start_col = 7,
|
||||||
end_col = 11,
|
end_col = 11,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- x
|
{ -- x
|
||||||
line = 5,
|
line = 5,
|
||||||
modifiers = { 'readonly', 'functionScope' },
|
modifiers = { readonly = true, functionScope = true },
|
||||||
start_col = 15,
|
start_col = 15,
|
||||||
end_col = 16,
|
end_col = 16,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- std
|
{ -- std
|
||||||
line = 5,
|
line = 5,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
start_col = 20,
|
start_col = 20,
|
||||||
end_col = 23,
|
end_col = 23,
|
||||||
type = 'namespace',
|
type = 'namespace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- endl
|
{ -- endl
|
||||||
line = 5,
|
line = 5,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
start_col = 25,
|
start_col = 25,
|
||||||
end_col = 29,
|
end_col = 29,
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{ -- #else comment #endif
|
{ -- #else comment #endif
|
||||||
line = 6,
|
line = 6,
|
||||||
@ -683,7 +722,7 @@ int main()
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 7,
|
end_col = 7,
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 7,
|
line = 7,
|
||||||
@ -691,7 +730,7 @@ int main()
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 11,
|
end_col = 11,
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 8,
|
line = 8,
|
||||||
@ -699,7 +738,7 @@ int main()
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 8,
|
end_col = 8,
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -724,23 +763,23 @@ b = "as"]],
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 10,
|
end_col = 10,
|
||||||
type = 'comment', -- comment
|
type = 'comment', -- comment
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 1,
|
line = 1,
|
||||||
modifiers = { 'declaration' }, -- a
|
modifiers = { declaration = true }, -- a
|
||||||
start_col = 6,
|
start_col = 6,
|
||||||
end_col = 7,
|
end_col = 7,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 2,
|
line = 2,
|
||||||
modifiers = { 'static' }, -- b (global)
|
modifiers = { static = true }, -- b (global)
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 1,
|
end_col = 1,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -770,7 +809,7 @@ b = "as"]],
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 3, -- pub
|
end_col = 3, -- pub
|
||||||
type = 'keyword',
|
type = 'keyword',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
@ -778,15 +817,15 @@ b = "as"]],
|
|||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 6, -- fn
|
end_col = 6, -- fn
|
||||||
type = 'keyword',
|
type = 'keyword',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
modifiers = { 'declaration', 'public' },
|
modifiers = { declaration = true, public = true },
|
||||||
start_col = 7,
|
start_col = 7,
|
||||||
end_col = 11, -- main
|
end_col = 11, -- main
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
@ -794,7 +833,7 @@ b = "as"]],
|
|||||||
start_col = 11,
|
start_col = 11,
|
||||||
end_col = 12,
|
end_col = 12,
|
||||||
type = 'parenthesis',
|
type = 'parenthesis',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
@ -802,7 +841,7 @@ b = "as"]],
|
|||||||
start_col = 12,
|
start_col = 12,
|
||||||
end_col = 13,
|
end_col = 13,
|
||||||
type = 'parenthesis',
|
type = 'parenthesis',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
@ -810,15 +849,15 @@ b = "as"]],
|
|||||||
start_col = 14,
|
start_col = 14,
|
||||||
end_col = 15,
|
end_col = 15,
|
||||||
type = 'brace',
|
type = 'brace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 1,
|
line = 1,
|
||||||
modifiers = { 'controlFlow' },
|
modifiers = { controlFlow = true },
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 9, -- break
|
end_col = 9, -- break
|
||||||
type = 'keyword',
|
type = 'keyword',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 1,
|
line = 1,
|
||||||
@ -826,7 +865,7 @@ b = "as"]],
|
|||||||
start_col = 10,
|
start_col = 10,
|
||||||
end_col = 13, -- rust
|
end_col = 13, -- rust
|
||||||
type = 'unresolvedReference',
|
type = 'unresolvedReference',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 1,
|
line = 1,
|
||||||
@ -834,15 +873,15 @@ b = "as"]],
|
|||||||
start_col = 13,
|
start_col = 13,
|
||||||
end_col = 13,
|
end_col = 13,
|
||||||
type = 'semicolon',
|
type = 'semicolon',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 2,
|
line = 2,
|
||||||
modifiers = { 'documentation' },
|
modifiers = { documentation = true },
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 11,
|
end_col = 11,
|
||||||
type = 'comment', -- /// what?
|
type = 'comment', -- /// what?
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 3,
|
line = 3,
|
||||||
@ -850,7 +889,7 @@ b = "as"]],
|
|||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 1,
|
end_col = 1,
|
||||||
type = 'brace',
|
type = 'brace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -908,26 +947,26 @@ b = "as"]],
|
|||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
modifiers = {
|
modifiers = {
|
||||||
'declaration',
|
declaration = true,
|
||||||
'globalScope',
|
globalScope = true,
|
||||||
},
|
},
|
||||||
start_col = 6,
|
start_col = 6,
|
||||||
end_col = 9,
|
end_col = 9,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected2 = {
|
expected2 = {
|
||||||
{
|
{
|
||||||
line = 1,
|
line = 1,
|
||||||
modifiers = {
|
modifiers = {
|
||||||
'declaration',
|
declaration = true,
|
||||||
'globalScope',
|
globalScope = true,
|
||||||
},
|
},
|
||||||
start_col = 6,
|
start_col = 6,
|
||||||
end_col = 9,
|
end_col = 9,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected_screen1 = function()
|
expected_screen1 = function()
|
||||||
@ -1018,55 +1057,55 @@ int main()
|
|||||||
line = 2,
|
line = 2,
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 8,
|
end_col = 8,
|
||||||
modifiers = { 'declaration', 'globalScope' },
|
modifiers = { declaration = true, globalScope = true },
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 4,
|
line = 4,
|
||||||
start_col = 8,
|
start_col = 8,
|
||||||
end_col = 9,
|
end_col = 9,
|
||||||
modifiers = { 'declaration', 'functionScope' },
|
modifiers = { declaration = true, functionScope = true },
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 5,
|
line = 5,
|
||||||
start_col = 7,
|
start_col = 7,
|
||||||
end_col = 18,
|
end_col = 18,
|
||||||
modifiers = { 'globalScope' },
|
modifiers = { globalScope = true },
|
||||||
type = 'macro',
|
type = 'macro',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 6,
|
line = 6,
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 7,
|
end_col = 7,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
type = 'namespace',
|
type = 'namespace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 6,
|
line = 6,
|
||||||
start_col = 9,
|
start_col = 9,
|
||||||
end_col = 13,
|
end_col = 13,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 6,
|
line = 6,
|
||||||
start_col = 17,
|
start_col = 17,
|
||||||
end_col = 18,
|
end_col = 18,
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
modifiers = { 'functionScope' },
|
modifiers = { functionScope = true },
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 7,
|
line = 7,
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 5,
|
end_col = 5,
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
},
|
},
|
||||||
@ -1076,7 +1115,7 @@ int main()
|
|||||||
modifiers = {},
|
modifiers = {},
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 9,
|
line = 9,
|
||||||
@ -1084,7 +1123,7 @@ int main()
|
|||||||
end_col = 6,
|
end_col = 6,
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected2 = {
|
expected2 = {
|
||||||
@ -1092,63 +1131,63 @@ int main()
|
|||||||
line = 2,
|
line = 2,
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 8,
|
end_col = 8,
|
||||||
modifiers = { 'declaration', 'globalScope' },
|
modifiers = { declaration = true, globalScope = true },
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 4,
|
line = 4,
|
||||||
start_col = 8,
|
start_col = 8,
|
||||||
end_col = 9,
|
end_col = 9,
|
||||||
modifiers = { 'declaration', 'globalScope' },
|
modifiers = { declaration = true, globalScope = true },
|
||||||
type = 'function',
|
type = 'function',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 5,
|
line = 5,
|
||||||
end_col = 12,
|
end_col = 12,
|
||||||
start_col = 11,
|
start_col = 11,
|
||||||
modifiers = { 'declaration', 'functionScope' },
|
modifiers = { declaration = true, functionScope = true },
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 6,
|
line = 6,
|
||||||
start_col = 7,
|
start_col = 7,
|
||||||
end_col = 18,
|
end_col = 18,
|
||||||
modifiers = { 'globalScope' },
|
modifiers = { globalScope = true },
|
||||||
type = 'macro',
|
type = 'macro',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 7,
|
line = 7,
|
||||||
start_col = 4,
|
start_col = 4,
|
||||||
end_col = 7,
|
end_col = 7,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
type = 'namespace',
|
type = 'namespace',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 7,
|
line = 7,
|
||||||
start_col = 9,
|
start_col = 9,
|
||||||
end_col = 13,
|
end_col = 13,
|
||||||
modifiers = { 'defaultLibrary', 'globalScope' },
|
modifiers = { defaultLibrary = true, globalScope = true },
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 7,
|
line = 7,
|
||||||
start_col = 17,
|
start_col = 17,
|
||||||
end_col = 18,
|
end_col = 18,
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
modifiers = { 'globalScope' },
|
modifiers = { globalScope = true },
|
||||||
type = 'function',
|
type = 'function',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 8,
|
line = 8,
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 5,
|
end_col = 5,
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
},
|
},
|
||||||
@ -1158,7 +1197,7 @@ int main()
|
|||||||
modifiers = {},
|
modifiers = {},
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
line = 10,
|
line = 10,
|
||||||
@ -1166,7 +1205,7 @@ int main()
|
|||||||
end_col = 6,
|
end_col = 6,
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
type = 'comment',
|
type = 'comment',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected_screen1 = function()
|
expected_screen1 = function()
|
||||||
@ -1228,12 +1267,12 @@ int main()
|
|||||||
{
|
{
|
||||||
line = 0,
|
line = 0,
|
||||||
modifiers = {
|
modifiers = {
|
||||||
'declaration',
|
declaration = true,
|
||||||
},
|
},
|
||||||
start_col = 0,
|
start_col = 0,
|
||||||
end_col = 6,
|
end_col = 6,
|
||||||
type = 'variable',
|
type = 'variable',
|
||||||
extmark_added = true,
|
marked = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected2 = {
|
expected2 = {
|
||||||
|
Loading…
Reference in New Issue
Block a user