mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00: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:
		@@ -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 = {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user