Skip to content

Commit 339840f

Browse files
committed
lsp: modify snippet-insertion-behaviour.
Explained in one of the comments, essentially we want the snippetTextEdits to be visited one after the other, beginning with the first. This is not the default-behaviour, and thus needs some extra work.
1 parent bcbef84 commit 339840f

File tree

1 file changed

+87
-21
lines changed

1 file changed

+87
-21
lines changed

lua/luasnip/extras/lsp.lua

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
11
local luasnip_ns_id = require("luasnip.session").ns_id
22
local ls = require("luasnip")
3+
local session = ls.session
4+
local log = require("luasnip.util.log").new("lsp")
5+
local util = require("luasnip.util.util")
36

47
local M = {}
58

9+
-- copied from init.lua, maybe find some better way to get it.
10+
local function _jump_into_default(snippet)
11+
local current_buf = vim.api.nvim_get_current_buf()
12+
if session.current_nodes[current_buf] then
13+
local current_node = session.current_nodes[current_buf]
14+
if current_node.pos > 0 then
15+
-- snippet is nested, notify current insertNode about expansion.
16+
current_node.inner_active = true
17+
else
18+
-- snippet was expanded behind a previously active one, leave the i(0)
19+
-- properly (and remove the snippet on error).
20+
local ok, err = pcall(current_node.input_leave, current_node)
21+
if not ok then
22+
log.warn("Error while leaving snippet: ", err)
23+
current_node.parent.snippet:remove_from_jumplist()
24+
end
25+
end
26+
end
27+
28+
return util.no_region_check_wrap(snippet.jump_into, snippet, 1)
29+
end
30+
631
---Apply text/snippetTextEdits (at most one snippetText though).
732
---@param snippet_or_text_edits `(snippetTextEdit|textEdit)[]`
833
--- snippetTextEdit as defined in https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#snippet-textedit)
@@ -13,28 +38,27 @@ local M = {}
1338
function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply_text_edits_fn)
1439
-- plain textEdits, applied using via `apply_text_edits_fn`.
1540
local text_edits = {}
16-
-- contains keys
41+
42+
-- list of snippet-parameters. These contain keys
1743
-- - snippet (parsed snippet)
1844
-- - mark (extmark, textrange replaced by the snippet)
19-
local snippet_params
45+
local all_snippet_params = {}
2046

2147
for _, v in ipairs(snippet_or_text_edits) do
2248
if v.newText and v.insertTextFormat == 2 then
23-
assert(snippet_params == nil, "Only one snippetTextEdit may be applied at once.")
24-
2549
-- from vim.lsp.apply_text_edits.
2650
local start_row = v.range.start.line
2751
local start_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range.start, offset_encoding)
2852
local end_row = v.range['end'].line
2953
local end_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range['end'], offset_encoding)
3054

31-
snippet_params = {
55+
table.insert(all_snippet_params, {
3256
snippet_body = v.newText,
3357
mark = vim.api.nvim_buf_set_extmark(bufnr, luasnip_ns_id, start_row, start_col, {
3458
end_row = end_row,
3559
end_col = end_col
3660
}),
37-
}
61+
})
3862
else
3963
table.insert(text_edits, v)
4064
end
@@ -43,21 +67,63 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply
4367
-- first apply regular textEdits...
4468
apply_text_edits_fn(text_edits, bufnr, offset_encoding)
4569

46-
-- ...then the snippet.
47-
local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true})
48-
local mark_begin_pos = {mark_info[1], mark_info[2]}
49-
local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col}
50-
51-
-- luasnip can only expand snippets in the active buffer, so switch (nop if
52-
-- buf already active).
53-
vim.api.nvim_set_current_buf(bufnr)
54-
ls.lsp_expand(snippet_params.snippet_body, {
55-
pos = mark_begin_pos,
56-
clear_region = {
57-
from = mark_begin_pos,
58-
to = mark_end_pos,
59-
},
60-
})
70+
-- ...then the snippetTextEdits.
71+
72+
-- store expanded snippets, if there are multiple we need to properly chain them together.
73+
local expanded_snippets = {}
74+
for i, snippet_params in ipairs(all_snippet_params) do
75+
local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true})
76+
local mark_begin_pos = {mark_info[1], mark_info[2]}
77+
local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col}
78+
79+
-- luasnip can only expand snippets in the active buffer, so switch (nop if
80+
-- buf already active).
81+
vim.api.nvim_set_current_buf(bufnr)
82+
83+
-- use expand_opts to chain snippets behind each other and store the
84+
-- expanded snippets.
85+
-- With the regular expand_opts, we will immediately jump into the
86+
-- first snippet, if it contains an i(1), the following snippets will
87+
-- belong inside it, which we don't want here: we want the i(0) of a
88+
-- snippet to lead to the next (also skipping the i(-1)).
89+
-- Even worse: by default, we would jump into the snippets during
90+
-- snip_expand, which should only happen for the first, the later
91+
-- snippets should be reached by jumping through the previous ones.
92+
local expand_opts = {
93+
pos = mark_begin_pos,
94+
clear_region = {
95+
from = mark_begin_pos,
96+
to = mark_end_pos,
97+
},
98+
}
99+
100+
if i == 1 then
101+
-- for first snippet: jump into it, and store the expanded snippet.
102+
expand_opts.jump_into_func = function(snip)
103+
expanded_snippets[i] = snip
104+
local cr = _jump_into_default(snip)
105+
print(cr)
106+
return cr
107+
end
108+
else
109+
-- don't jump into the snippet, just store it.
110+
expand_opts.jump_into_func = function(snip)
111+
expanded_snippets[i] = snip
112+
113+
print(session.current_nodes[bufnr])
114+
115+
-- let the already-active node stay active.
116+
return session.current_nodes[bufnr]
117+
end
118+
-- jump from previous i0 directly into this snippet (ignore start_node).
119+
expand_opts.jumplist_insert_func = function(snippet, _, _, _)
120+
snippet.prev = expanded_snippets[i-1].insert_nodes[0]
121+
expanded_snippets[i-1].insert_nodes[0].next = snippet
122+
end
123+
end
124+
125+
ls.lsp_expand(snippet_params.snippet_body, expand_opts)
126+
end
61127
end
62128

63129
function M.update_capabilities(capabilities)

0 commit comments

Comments
 (0)