commit 701258921ed1dcd81d51c02051bd04967088ed23
parent 25bc41847e495aa55ede1090f29afd2a1709b0b4
Author: TheBlob42 <hessenmobbel@web.de>
Date: Tue, 19 Aug 2025 15:21:32 +0200
feat(snippet): highlight active tabstop (#35378)
Diffstat:
7 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua
@@ -134,6 +134,7 @@ hi('DiagnosticDeprecated', { sp = 'Red', strikethrough = true, cterm =
hi('DiagnosticUnnecessary', { link = 'Comment' })
hi('LspInlayHint', { link = 'NonText' })
hi('SnippetTabstop', { link = 'Visual' })
+hi('SnippetTabstopActive', { link = 'SnippetTabstop' })
-- Text
hi('@markup.raw', { link = 'Comment' })
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
@@ -4354,7 +4354,8 @@ vim.snippet.expand({input}) *vim.snippet.expand()*
https://microsoft.github.io/language-server-protocol/specification/#snippet_syntax
for the specification of valid input.
- Tabstops are highlighted with |hl-SnippetTabstop|.
+ Tabstops are highlighted with |hl-SnippetTabstop| and
+ |hl-SnippetTabstopActive|.
Parameters: ~
• {input} (`string`)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -192,6 +192,7 @@ HIGHLIGHTS
• |hl-DiffTextAdd| highlights added text within a changed line.
• |hl-StderrMsg| |hl-StdoutMsg|
+• |hl-SnippetTabstopActive| highlights the currently active tabstop.
LSP
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
@@ -5343,6 +5343,9 @@ Search Last search pattern highlighting (see 'hlsearch').
Also used for similar items that need to stand out.
*hl-SnippetTabstop*
SnippetTabstop Tabstops in snippets. |vim.snippet|
+ *hl-SnippetTabstopActive*
+SnippetTabstopActive
+ The currently active tabstop. |vim.snippet|
*hl-SpecialKey*
SpecialKey Unprintable characters: Text displayed differently from what
it really is. But not 'listchars' whitespace. |hl-Whitespace|
diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua
@@ -2,6 +2,7 @@ local G = vim.lsp._snippet_grammar
local snippet_group = vim.api.nvim_create_augroup('nvim.snippet', {})
local snippet_ns = vim.api.nvim_create_namespace('nvim.snippet')
local hl_group = 'SnippetTabstop'
+local hl_group_active = 'SnippetTabstopActive'
--- Returns the 0-based cursor position.
---
@@ -124,7 +125,7 @@ function Tabstop.new(index, bufnr, placement, range, choices)
end_right_gravity = false,
end_line = range[3],
end_col = range[4],
- hl_group = hl_group,
+ hl_group = index == 1 and hl_group_active or hl_group,
})
local self = setmetatable({
@@ -168,19 +169,22 @@ function Tabstop:set_text(text)
end
---@alias (private) vim.snippet.TabStopGravity
---- | "expand" Expand the (usually current) tabstop on text insert
+--- | "expand" Expand the current tabstop on text insert
--- | "lock" The tabstop should NOT move on text insert
--- | "shift" The tabstop should move on text insert (default)
--- Sets the right gravity of the tabstop's extmark.
+--- Sets the active highlight group for current ("expand") tabstops
---
---@package
---@param target vim.snippet.TabStopGravity
function Tabstop:set_gravity(target)
+ local hl = hl_group
local right_gravity = true
local end_right_gravity = true
if target == 'expand' then
+ hl = hl_group_active
right_gravity = false
end_right_gravity = true
elseif target == 'lock' then
@@ -189,12 +193,13 @@ function Tabstop:set_gravity(target)
end
local range = self:get_range()
+ vim.api.nvim_buf_del_extmark(self.bufnr, snippet_ns, self.extmark_id)
self.extmark_id = vim.api.nvim_buf_set_extmark(self.bufnr, snippet_ns, range[1], range[2], {
right_gravity = right_gravity,
end_right_gravity = end_right_gravity,
end_line = range[3],
end_col = range[4],
- hl_group = hl_group,
+ hl_group = hl,
})
end
@@ -469,7 +474,7 @@ end
--- Refer to https://microsoft.github.io/language-server-protocol/specification/#snippet_syntax
--- for the specification of valid input.
---
---- Tabstops are highlighted with |hl-SnippetTabstop|.
+--- Tabstops are highlighted with |hl-SnippetTabstop| and |hl-SnippetTabstopActive|.
---
--- @param input string
function M.expand(input)
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
@@ -221,6 +221,7 @@ static const char *highlight_init_both[] = {
"default link LspReferenceTarget LspReferenceText",
"default link LspSignatureActiveParameter Visual",
"default link SnippetTabstop Visual",
+ "default link SnippetTabstopActive SnippetTabstop",
// Diagnostic
"default link DiagnosticFloatingError DiagnosticError",
diff --git a/test/functional/lua/snippet_spec.lua b/test/functional/lua/snippet_spec.lua
@@ -116,6 +116,20 @@ describe('vim.snippet', function()
test_expand_success({ 'print($UNKNOWN)' }, { 'print(UNKNOWN)' })
end)
+ it('highlights active tabstop with SnippetTabstopActive', function()
+ local function get_extmark_details(col, end_col)
+ return api.nvim_buf_get_extmarks(0, -1, { 0, col }, { 0, end_col }, { details = true })[1][4]
+ end
+
+ test_expand_success({ 'local ${1:name} = ${2:value}' }, { 'local name = value' })
+ eq('SnippetTabstopActive', get_extmark_details(6, 10).hl_group)
+ eq('SnippetTabstop', get_extmark_details(13, 18).hl_group)
+ feed('<Tab>')
+ poke_eventloop()
+ eq('SnippetTabstop', get_extmark_details(6, 10).hl_group)
+ eq('SnippetTabstopActive', get_extmark_details(13, 18).hl_group)
+ end)
+
it('does not jump outside snippet range', function()
test_expand_success({ 'function $1($2)', ' $0', 'end' }, { 'function ()', ' ', 'end' })
eq(false, exec_lua('return vim.snippet.active({ direction = -1 })'))