From 6385146ce7faf8fea39a20c3325d8e5d25341a49 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Sun, 4 Sep 2022 12:59:35 +0200 Subject: [PATCH 1/9] implement interface for applying snippetTextEdits. --- lua/luasnip/extras/lsp.lua | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 lua/luasnip/extras/lsp.lua diff --git a/lua/luasnip/extras/lsp.lua b/lua/luasnip/extras/lsp.lua new file mode 100644 index 000000000..740282124 --- /dev/null +++ b/lua/luasnip/extras/lsp.lua @@ -0,0 +1,63 @@ +local luasnip_ns_id = require("luasnip.session").ns_id +local ls = require("luasnip") + +local M = {} + +---Apply text/snippetTextEdits (at most one snippetText though). +---@param snippet_or_text_edits `(snippetTextEdit|textEdit)[]` +--- snippetTextEdit as defined in https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#snippet-textedit) +---@param bufnr number, buffer where the snippet should be expanded. +---@param offset_encoding string|nil, 'utf-8,16,32' or ni +---@param apply_text_edits_fn function, has to apply regular textEdits, most +--- likely `vim.lsp.util.apply_text_edits` (we expect its' interface). +function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply_text_edits_fn) + -- plain textEdits, applied using via `apply_text_edits_fn`. + local text_edits = {} + -- contains keys + -- - snippet (parsed snippet) + -- - mark (extmark, textrange replaced by the snippet) + local snippet_params + + for _, v in ipairs(snippet_or_text_edits) do + if v.newText and v.insertTextFormat == 2 then + assert(snippet_params == nil, "Only one snippetTextEdit may be applied at once.") + + -- from vim.lsp.apply_text_edits. + local start_row = v.range.start.line + local start_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range.start, offset_encoding) + local end_row = v.range['end'].line + local end_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range['end'], offset_encoding) + + snippet_params = { + snippet_body = v.newText, + mark = vim.api.nvim_buf_set_extmark(bufnr, luasnip_ns_id, start_row, start_col, { + end_row = end_row, + end_col = end_col + }), + } + else + table.insert(text_edits, v) + end + end + + -- first apply regular textEdits... + apply_text_edits_fn(text_edits, bufnr, offset_encoding) + + -- ...then the snippet. + local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true}) + local mark_begin_pos = {mark_info[1], mark_info[2]} + local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col} + + -- luasnip can only expand snippets in the active buffer, so switch (nop if + -- buf already active). + vim.api.nvim_set_current_buf(bufnr) + ls.lsp_expand(snippet_params.snippet_body, { + pos = mark_begin_pos, + clear_region = { + from = mark_begin_pos, + to = mark_end_pos, + }, + }) +end + +return M From bba9164965b89a51f93ef139255f60d92e6c814c Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Sun, 4 Sep 2022 23:55:37 +0200 Subject: [PATCH 2/9] Add update_capabilities. --- lua/luasnip/extras/lsp.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lua/luasnip/extras/lsp.lua b/lua/luasnip/extras/lsp.lua index 740282124..7bc756264 100644 --- a/lua/luasnip/extras/lsp.lua +++ b/lua/luasnip/extras/lsp.lua @@ -60,4 +60,13 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply }) end +function M.update_capabilities(capabilities) + if not capabilities.experimental then + capabilities.experimental = {} + end + capabilities.experimental.snippetTextEdit = true + + return capabilities +end + return M From 03fb957cca91a9a606f4c3ae7d7b0d15eeaa28af Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 00:04:48 +0100 Subject: [PATCH 3/9] refactor/feat: allow customization of jumplist_insert-behavior. Default is not changed here. --- DOC.md | 6 ++++++ lua/luasnip/init.lua | 4 +++- lua/luasnip/nodes/snippet.lua | 25 +++++++++++++------------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/DOC.md b/DOC.md index 3c15e210a..7143c01ed 100644 --- a/DOC.md +++ b/DOC.md @@ -2851,6 +2851,12 @@ print a short message to the log. return snip.insert_nodes[0] end ``` + - `jumplist_insert_func`: fn(snippet, start_node, end_node, current_node): + Use this callback to change how the snippet in inserted into the jumplist. + `start_node` and `end_node` are the respective nodes of `snippet`, the + snippet which is expanded. + By default, this function: + TODO: describe the default-behavior :( `opts` and any of its parameters may be nil. diff --git a/lua/luasnip/init.lua b/lua/luasnip/init.lua index d8d01fac9..3b9b6c18f 100644 --- a/lua/luasnip/init.lua +++ b/lua/luasnip/init.lua @@ -197,6 +197,7 @@ local function snip_expand(snippet, opts) -- override with current position if none given. opts.pos = opts.pos or util.get_cursor_0ind() opts.jump_into_func = opts.jump_into_func or _jump_into_default + opts.jumplist_insert_func = opts.jumplist_insert_func or require("luasnip.nodes.snippet").default_jumplist_insert snip.trigger = opts.expand_params.trigger or snip.trigger snip.captures = opts.expand_params.captures or {} @@ -233,7 +234,8 @@ local function snip_expand(snippet, opts) snip:trigger_expand( session.current_nodes[vim.api.nvim_get_current_buf()], pos_id, - env + env, + opts.jumplist_insert_func ) local current_buf = vim.api.nvim_get_current_buf() diff --git a/lua/luasnip/nodes/snippet.lua b/lua/luasnip/nodes/snippet.lua index d46a39105..8346ce123 100644 --- a/lua/luasnip/nodes/snippet.lua +++ b/lua/luasnip/nodes/snippet.lua @@ -385,7 +385,7 @@ function Snippet:remove_from_jumplist() end end -local function insert_into_jumplist(snippet, start_node, current_node) +local function insert_into_jumplist(snippet, start_node, end_node, current_node) if current_node then -- currently at the endpoint (i(0)) of another snippet, this snippet -- is inserted _behind_ that snippet. @@ -394,13 +394,13 @@ local function insert_into_jumplist(snippet, start_node, current_node) if current_node.next.pos == -1 then -- next is beginning of another snippet, this snippet is -- inserted before that one. - current_node.next.prev = snippet.insert_nodes[0] + current_node.next.prev = end_node else -- next is outer insertNode. - current_node.next.inner_last = snippet.insert_nodes[0] + current_node.next.inner_last = end_node end end - snippet.insert_nodes[0].next = current_node.next + end_node.next = current_node.next current_node.next = start_node start_node.prev = current_node elseif current_node.pos == -1 then @@ -411,27 +411,27 @@ local function insert_into_jumplist(snippet, start_node, current_node) current_node.prev.inner_first = snippet end end - snippet.insert_nodes[0].next = current_node + end_node.next = current_node start_node.prev = current_node.prev - current_node.prev = snippet.insert_nodes[0] + current_node.prev = end_node else - snippet.insert_nodes[0].next = current_node + end_node.next = current_node -- jump into snippet directly. current_node.inner_first = snippet - current_node.inner_last = snippet.insert_nodes[0] + current_node.inner_last = end_node start_node.prev = current_node end end -- snippet is between i(-1)(startNode) and i(0). - snippet.next = snippet.insert_nodes[0] + snippet.next = end_node snippet.prev = start_node - snippet.insert_nodes[0].prev = snippet + end_node.prev = snippet start_node.next = snippet end -function Snippet:trigger_expand(current_node, pos_id, env) +function Snippet:trigger_expand(current_node, pos_id, env, jumplist_insert_func) local pos = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, pos_id, {}) local pre_expand_res = self:event(events.pre_expand, { expand_pos = pos }) or {} @@ -509,7 +509,7 @@ function Snippet:trigger_expand(current_node, pos_id, env) start_node.pos = -1 start_node.parent = self - insert_into_jumplist(self, start_node, current_node) + jumplist_insert_func(self, start_node, self.insert_nodes[0], current_node) end -- returns copy of snip if it matches, nil if not. @@ -1205,4 +1205,5 @@ return { wrap_nodes_in_snippetNode = wrap_nodes_in_snippetNode, init_snippet_context = init_snippet_context, init_snippet_opts = init_snippet_opts, + default_jumplist_insert = insert_into_jumplist } From a1a60eb47cdd66f54f286871db3b6c9f43d3c653 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 09:34:14 +0100 Subject: [PATCH 4/9] refactor: link snippet with end_ and start_node in trigger_expand. If jumplist_insert is configurable, we should clean this up a bit. IMO it might be expected that the links between a snippets and its i(0), i(-1) exist, so we'll just add them. If this is not desired, they can still be overridden easily. --- lua/luasnip/nodes/snippet.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lua/luasnip/nodes/snippet.lua b/lua/luasnip/nodes/snippet.lua index 8346ce123..03ffa6671 100644 --- a/lua/luasnip/nodes/snippet.lua +++ b/lua/luasnip/nodes/snippet.lua @@ -422,13 +422,6 @@ local function insert_into_jumplist(snippet, start_node, end_node, current_node) start_node.prev = current_node end end - - -- snippet is between i(-1)(startNode) and i(0). - snippet.next = end_node - snippet.prev = start_node - - end_node.prev = snippet - start_node.next = snippet end function Snippet:trigger_expand(current_node, pos_id, env, jumplist_insert_func) @@ -509,6 +502,12 @@ function Snippet:trigger_expand(current_node, pos_id, env, jumplist_insert_func) start_node.pos = -1 start_node.parent = self + -- snippet is between i(-1)(startNode) and i(0). + self.insert_nodes[0].prev = self + start_node.next = self + self.next = self.insert_nodes[0] + self.prev = start_node + jumplist_insert_func(self, start_node, self.insert_nodes[0], current_node) end From bcbef84d888bfe82551828c6e091d4c345d0bd0e Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 10:16:10 +0100 Subject: [PATCH 5/9] make jump_into_func responsible for adjusting the currently active node. This might break some previously-working jump_into_funcs. --- lua/luasnip/init.lua | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lua/luasnip/init.lua b/lua/luasnip/init.lua index 3b9b6c18f..56e6e595a 100644 --- a/lua/luasnip/init.lua +++ b/lua/luasnip/init.lua @@ -185,6 +185,23 @@ local function locally_jumpable(dir) end local function _jump_into_default(snippet) + local current_buf = vim.api.nvim_get_current_buf() + if session.current_nodes[current_buf] then + local current_node = session.current_nodes[current_buf] + if current_node.pos > 0 then + -- snippet is nested, notify current insertNode about expansion. + current_node.inner_active = true + else + -- snippet was expanded behind a previously active one, leave the i(0) + -- properly (and remove the snippet on error). + local ok, err = pcall(current_node.input_leave, current_node) + if not ok then + log.warn("Error while leaving snippet: ", err) + current_node.parent.snippet:remove_from_jumplist() + end + end + end + return util.no_region_check_wrap(snippet.jump_into, snippet, 1) end @@ -238,24 +255,6 @@ local function snip_expand(snippet, opts) opts.jumplist_insert_func ) - local current_buf = vim.api.nvim_get_current_buf() - - if session.current_nodes[current_buf] then - local current_node = session.current_nodes[current_buf] - if current_node.pos > 0 then - -- snippet is nested, notify current insertNode about expansion. - current_node.inner_active = true - else - -- snippet was expanded behind a previously active one, leave the i(0) - -- properly (and remove the snippet on error). - local ok, err = pcall(current_node.input_leave, current_node) - if not ok then - log.warn("Error while leaving snippet: ", err) - current_node.parent.snippet:remove_from_jumplist() - end - end - end - -- jump_into-callback returns new active node. session.current_nodes[vim.api.nvim_get_current_buf()] = opts.jump_into_func(snip) From 339840f0ed82a2e049c7128f7a52353d8d030171 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 10:18:00 +0100 Subject: [PATCH 6/9] 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. --- lua/luasnip/extras/lsp.lua | 108 +++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 21 deletions(-) diff --git a/lua/luasnip/extras/lsp.lua b/lua/luasnip/extras/lsp.lua index 7bc756264..fde131671 100644 --- a/lua/luasnip/extras/lsp.lua +++ b/lua/luasnip/extras/lsp.lua @@ -1,8 +1,33 @@ local luasnip_ns_id = require("luasnip.session").ns_id local ls = require("luasnip") +local session = ls.session +local log = require("luasnip.util.log").new("lsp") +local util = require("luasnip.util.util") local M = {} +-- copied from init.lua, maybe find some better way to get it. +local function _jump_into_default(snippet) + local current_buf = vim.api.nvim_get_current_buf() + if session.current_nodes[current_buf] then + local current_node = session.current_nodes[current_buf] + if current_node.pos > 0 then + -- snippet is nested, notify current insertNode about expansion. + current_node.inner_active = true + else + -- snippet was expanded behind a previously active one, leave the i(0) + -- properly (and remove the snippet on error). + local ok, err = pcall(current_node.input_leave, current_node) + if not ok then + log.warn("Error while leaving snippet: ", err) + current_node.parent.snippet:remove_from_jumplist() + end + end + end + + return util.no_region_check_wrap(snippet.jump_into, snippet, 1) +end + ---Apply text/snippetTextEdits (at most one snippetText though). ---@param snippet_or_text_edits `(snippetTextEdit|textEdit)[]` --- 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 = {} function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply_text_edits_fn) -- plain textEdits, applied using via `apply_text_edits_fn`. local text_edits = {} - -- contains keys + + -- list of snippet-parameters. These contain keys -- - snippet (parsed snippet) -- - mark (extmark, textrange replaced by the snippet) - local snippet_params + local all_snippet_params = {} for _, v in ipairs(snippet_or_text_edits) do if v.newText and v.insertTextFormat == 2 then - assert(snippet_params == nil, "Only one snippetTextEdit may be applied at once.") - -- from vim.lsp.apply_text_edits. local start_row = v.range.start.line local start_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range.start, offset_encoding) local end_row = v.range['end'].line local end_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range['end'], offset_encoding) - snippet_params = { + table.insert(all_snippet_params, { snippet_body = v.newText, mark = vim.api.nvim_buf_set_extmark(bufnr, luasnip_ns_id, start_row, start_col, { end_row = end_row, end_col = end_col }), - } + }) else table.insert(text_edits, v) end @@ -43,21 +67,63 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply -- first apply regular textEdits... apply_text_edits_fn(text_edits, bufnr, offset_encoding) - -- ...then the snippet. - local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true}) - local mark_begin_pos = {mark_info[1], mark_info[2]} - local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col} - - -- luasnip can only expand snippets in the active buffer, so switch (nop if - -- buf already active). - vim.api.nvim_set_current_buf(bufnr) - ls.lsp_expand(snippet_params.snippet_body, { - pos = mark_begin_pos, - clear_region = { - from = mark_begin_pos, - to = mark_end_pos, - }, - }) + -- ...then the snippetTextEdits. + + -- store expanded snippets, if there are multiple we need to properly chain them together. + local expanded_snippets = {} + for i, snippet_params in ipairs(all_snippet_params) do + local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true}) + local mark_begin_pos = {mark_info[1], mark_info[2]} + local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col} + + -- luasnip can only expand snippets in the active buffer, so switch (nop if + -- buf already active). + vim.api.nvim_set_current_buf(bufnr) + + -- use expand_opts to chain snippets behind each other and store the + -- expanded snippets. + -- With the regular expand_opts, we will immediately jump into the + -- first snippet, if it contains an i(1), the following snippets will + -- belong inside it, which we don't want here: we want the i(0) of a + -- snippet to lead to the next (also skipping the i(-1)). + -- Even worse: by default, we would jump into the snippets during + -- snip_expand, which should only happen for the first, the later + -- snippets should be reached by jumping through the previous ones. + local expand_opts = { + pos = mark_begin_pos, + clear_region = { + from = mark_begin_pos, + to = mark_end_pos, + }, + } + + if i == 1 then + -- for first snippet: jump into it, and store the expanded snippet. + expand_opts.jump_into_func = function(snip) + expanded_snippets[i] = snip + local cr = _jump_into_default(snip) + print(cr) + return cr + end + else + -- don't jump into the snippet, just store it. + expand_opts.jump_into_func = function(snip) + expanded_snippets[i] = snip + + print(session.current_nodes[bufnr]) + + -- let the already-active node stay active. + return session.current_nodes[bufnr] + end + -- jump from previous i0 directly into this snippet (ignore start_node). + expand_opts.jumplist_insert_func = function(snippet, _, _, _) + snippet.prev = expanded_snippets[i-1].insert_nodes[0] + expanded_snippets[i-1].insert_nodes[0].next = snippet + end + end + + ls.lsp_expand(snippet_params.snippet_body, expand_opts) + end end function M.update_capabilities(capabilities) From f3c95f35234d097cbb4842772f80481b171b3168 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 11:52:42 +0100 Subject: [PATCH 7/9] don't modify jumplist-insertion too much, does not match some assumptions. In particular, snip.next.next of a i(0), or snip.prev.prev of a i(-1) is often assumed to be the next/previous snippet, which is not the case if we do the modifications from the parent. Instead: just modify behaviour of i(-1), such that it is just ignored while jumping. --- lua/luasnip/extras/lsp.lua | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lua/luasnip/extras/lsp.lua b/lua/luasnip/extras/lsp.lua index fde131671..e595363f4 100644 --- a/lua/luasnip/extras/lsp.lua +++ b/lua/luasnip/extras/lsp.lua @@ -102,7 +102,6 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply expand_opts.jump_into_func = function(snip) expanded_snippets[i] = snip local cr = _jump_into_default(snip) - print(cr) return cr end else @@ -110,15 +109,19 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply expand_opts.jump_into_func = function(snip) expanded_snippets[i] = snip - print(session.current_nodes[bufnr]) - -- let the already-active node stay active. return session.current_nodes[bufnr] end - -- jump from previous i0 directly into this snippet (ignore start_node). - expand_opts.jumplist_insert_func = function(snippet, _, _, _) - snippet.prev = expanded_snippets[i-1].insert_nodes[0] - expanded_snippets[i-1].insert_nodes[0].next = snippet + -- jump from previous i0 directly to start_node. + expand_opts.jumplist_insert_func = function(_, start_node, _, _) + start_node.prev = expanded_snippets[i-1].insert_nodes[0] + expanded_snippets[i-1].insert_nodes[0].next = start_node + + -- skip start_node while jumping around. + -- start_node of first snippet behaves normally! + function start_node:jump_into(dir, no_move) + return (dir == 1 and self.next or self.prev):jump_into(dir, no_move) + end end end From 018e365beaaa16fffe8f062aa4c64f570a89d9e4 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 10:56:37 +0000 Subject: [PATCH 8/9] Auto generate docs --- doc/luasnip.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/luasnip.txt b/doc/luasnip.txt index 69ada5a68..8ae54da76 100644 --- a/doc/luasnip.txt +++ b/doc/luasnip.txt @@ -1,4 +1,4 @@ -*luasnip.txt* For NVIM v0.8.0 Last change: 2023 January 27 +*luasnip.txt* For NVIM v0.8.0 Last change: 2023 February 02 ============================================================================== Table of Contents *luasnip-table-of-contents* @@ -2799,6 +2799,12 @@ print a short message to the log. `lua function(snip) -- jump_into set the placeholder of the snippet, 1 -- to jump forwards. return snip:jump_into(1)` While this can be used to only insert the snippet `lua function(snip) return snip.insert_nodes[0] end` + - `jumplist_insert_func`: fn(snippet, start_node, end_node, current_node): + Use this callback to change how the snippet in inserted into the jumplist. + `start_node` and `end_node` are the respective nodes of `snippet`, the + snippet which is expanded. + By default, this function: + TODO: describe the default-behavior :( `opts` and any of its parameters may be nil. - `get_active_snip()`: returns the currently active snippet (not node!). - `choice_active()`: true if inside a choiceNode. From bdeb796e3a52af731b676b28cc988bc68922789e Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Thu, 2 Feb 2023 10:56:42 +0000 Subject: [PATCH 9/9] Format with stylua --- lua/luasnip/extras/lsp.lua | 65 +++++++++++++++++++++++++---------- lua/luasnip/init.lua | 3 +- lua/luasnip/nodes/snippet.lua | 2 +- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/lua/luasnip/extras/lsp.lua b/lua/luasnip/extras/lsp.lua index e595363f4..75e3d8091 100644 --- a/lua/luasnip/extras/lsp.lua +++ b/lua/luasnip/extras/lsp.lua @@ -35,7 +35,12 @@ end ---@param offset_encoding string|nil, 'utf-8,16,32' or ni ---@param apply_text_edits_fn function, has to apply regular textEdits, most --- likely `vim.lsp.util.apply_text_edits` (we expect its' interface). -function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply_text_edits_fn) +function M.apply_text_edits( + snippet_or_text_edits, + bufnr, + offset_encoding, + apply_text_edits_fn +) -- plain textEdits, applied using via `apply_text_edits_fn`. local text_edits = {} @@ -48,16 +53,30 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply if v.newText and v.insertTextFormat == 2 then -- from vim.lsp.apply_text_edits. local start_row = v.range.start.line - local start_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range.start, offset_encoding) - local end_row = v.range['end'].line - local end_col = vim.lsp.util._get_line_byte_from_position(bufnr, v.range['end'], offset_encoding) + local start_col = vim.lsp.util._get_line_byte_from_position( + bufnr, + v.range.start, + offset_encoding + ) + local end_row = v.range["end"].line + local end_col = vim.lsp.util._get_line_byte_from_position( + bufnr, + v.range["end"], + offset_encoding + ) table.insert(all_snippet_params, { snippet_body = v.newText, - mark = vim.api.nvim_buf_set_extmark(bufnr, luasnip_ns_id, start_row, start_col, { - end_row = end_row, - end_col = end_col - }), + mark = vim.api.nvim_buf_set_extmark( + bufnr, + luasnip_ns_id, + start_row, + start_col, + { + end_row = end_row, + end_col = end_col, + } + ), }) else table.insert(text_edits, v) @@ -72,9 +91,14 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply -- store expanded snippets, if there are multiple we need to properly chain them together. local expanded_snippets = {} for i, snippet_params in ipairs(all_snippet_params) do - local mark_info = vim.api.nvim_buf_get_extmark_by_id(bufnr, luasnip_ns_id, snippet_params.mark, {details = true}) - local mark_begin_pos = {mark_info[1], mark_info[2]} - local mark_end_pos = {mark_info[3].end_row, mark_info[3].end_col} + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + bufnr, + luasnip_ns_id, + snippet_params.mark, + { details = true } + ) + local mark_begin_pos = { mark_info[1], mark_info[2] } + local mark_end_pos = { mark_info[3].end_row, mark_info[3].end_col } -- luasnip can only expand snippets in the active buffer, so switch (nop if -- buf already active). @@ -114,14 +138,17 @@ function M.apply_text_edits(snippet_or_text_edits, bufnr, offset_encoding, apply end -- jump from previous i0 directly to start_node. expand_opts.jumplist_insert_func = function(_, start_node, _, _) - start_node.prev = expanded_snippets[i-1].insert_nodes[0] - expanded_snippets[i-1].insert_nodes[0].next = start_node - - -- skip start_node while jumping around. - -- start_node of first snippet behaves normally! - function start_node:jump_into(dir, no_move) - return (dir == 1 and self.next or self.prev):jump_into(dir, no_move) - end + start_node.prev = expanded_snippets[i - 1].insert_nodes[0] + expanded_snippets[i - 1].insert_nodes[0].next = start_node + + -- skip start_node while jumping around. + -- start_node of first snippet behaves normally! + function start_node:jump_into(dir, no_move) + return (dir == 1 and self.next or self.prev):jump_into( + dir, + no_move + ) + end end end diff --git a/lua/luasnip/init.lua b/lua/luasnip/init.lua index 56e6e595a..1ebac9d3b 100644 --- a/lua/luasnip/init.lua +++ b/lua/luasnip/init.lua @@ -214,7 +214,8 @@ local function snip_expand(snippet, opts) -- override with current position if none given. opts.pos = opts.pos or util.get_cursor_0ind() opts.jump_into_func = opts.jump_into_func or _jump_into_default - opts.jumplist_insert_func = opts.jumplist_insert_func or require("luasnip.nodes.snippet").default_jumplist_insert + opts.jumplist_insert_func = opts.jumplist_insert_func + or require("luasnip.nodes.snippet").default_jumplist_insert snip.trigger = opts.expand_params.trigger or snip.trigger snip.captures = opts.expand_params.captures or {} diff --git a/lua/luasnip/nodes/snippet.lua b/lua/luasnip/nodes/snippet.lua index 03ffa6671..61ebdb70c 100644 --- a/lua/luasnip/nodes/snippet.lua +++ b/lua/luasnip/nodes/snippet.lua @@ -1204,5 +1204,5 @@ return { wrap_nodes_in_snippetNode = wrap_nodes_in_snippetNode, init_snippet_context = init_snippet_context, init_snippet_opts = init_snippet_opts, - default_jumplist_insert = insert_into_jumplist + default_jumplist_insert = insert_into_jumplist, }