LuaSnip icon indicating copy to clipboard operation
LuaSnip copied to clipboard

Configuration option: forget the current snippet when leaving the insert mode

Open naquad opened this issue 3 years ago • 9 comments

I would like to be able to configure LuaSnip so it would abandon the current snippet when I'm leaving the insert mode.

The rationale.

After triggering the snippet, one may fill only some placeholders skipping others. Later I hit Tab (performs expand and/or jump in my setup) and LuaSnip sends me back to some placeholder I didn't before. I've tried to use the locally_jumpablefunction, but it solves the problem only partially.

Here's the demo: asciicast

I would like to configure LuaSnip in the same way as NeoSnippet works: when leaving the insert mode all placeholders must be forgotten.

P. S. I have tried to implement such behavior myself using autocommands on InsertLeave and InsertEnter, but because LuaSnip is switching modes during jumps such approach unlinks the snippet even when it shouldn't.

naquad avatar Nov 13 '22 15:11 naquad

Does #258 work for you? I think it implements exactly the DIY-approach you mentioned, and handles mode-changes due to jumping around

L3MON4D3 avatar Nov 13 '22 18:11 L3MON4D3

It helps indeed! Thank you. I've had to tweak it a bit for my config, but it seems to be working as expected. I'll play with it for the next couple of days.

IMHO, it would be great to at least mention it in the documentation.

naquad avatar Nov 13 '22 21:11 naquad

Nice👍 Adding it to the doc.. we don't have a section on jumping around, and tweaks for that so far, but it might be nice to add something like it. We could also add this functionality in extras, that way it's easier to access, and we can confirm it continues to work, and isn't broken accidentally.

L3MON4D3 avatar Nov 14 '22 06:11 L3MON4D3

Just in case, here's a somewhat tweaked version using autocmd API & matching rather than code checks:

local luasnip = require('luasnip')

local unlinkgrp = vim.api.nvim_create_augroup(
  'UnlinkSnippetOnModeChange',
  { clear = true }
)

vim.api.nvim_create_autocmd('ModeChanged', {
  group = unlinkgrp,
  pattern = {'s:n', 'i:*'},
  desc = 'Forget the current snippet when leaving the insert mode',
  callback = function(evt)
    if
      luasnip.session
      and luasnip.session.current_nodes[evt.buf]
      and not luasnip.session.jump_active
    then
      luasnip.unlink_current()
    end
  end,
})

naquad avatar Nov 14 '22 08:11 naquad

An alternative version, courtesy of @bekaboo, which further takes into account the cursor-position can be found here

L3MON4D3 avatar Jan 27 '23 21:01 L3MON4D3

Regarding unlink_current, I have a question: Would it be possible to relink the snippet, after it has been unlinked?

My use case is: (1) Expand snippet (2) Jump around in the snippet (3a) Leave Insert Mode (running some command or do any other thing) OR (3b) manually trigger unlink_current and run some command, etc. (4). Enter Insert Mode an resume jumping around in the snippet

Remich avatar Mar 28 '23 11:03 Remich

Possibly, but I don't really see the point, the command will still modify the extmarks (if that's what you want to avoid) :/ Maybe just check the mode before performing a jump?

L3MON4D3 avatar Mar 28 '23 16:03 L3MON4D3

Just in case, here's a somewhat tweaked version using autocmd API & matching rather than code checks:

local luasnip = require('luasnip')

local unlinkgrp = vim.api.nvim_create_augroup(
  'UnlinkSnippetOnModeChange',
  { clear = true }
)

vim.api.nvim_create_autocmd('ModeChanged', {
  group = unlinkgrp,
  pattern = {'s:n', 'i:*'},
  desc = 'Forget the current snippet when leaving the insert mode',
  callback = function(evt)
    if
      luasnip.session
      and luasnip.session.current_nodes[evt.buf]
      and not luasnip.session.jump_active
    then
      luasnip.unlink_current()
    end
  end,
})

This along with ext_opts was incredibly helpful in improving my luasnip experience. However I encountered a small flaw in this, in that it only unlinks the most recent snippet. So, for example if I use 2 snippets while in insert mode, only the most recent snippet will be unlinked. To get that behavior right I wrapped it in a loop:

vim.api.nvim_create_autocmd("ModeChanged", {
  group = vim.api.nvim_create_augroup("UnlinkLuaSnipSnippetOnModeChange", {
    clear = true,
  }),
  pattern = { "s:n", "i:*" },
  desc = "Forget the current snippet when leaving the insert mode",
  callback = function(evt)
    -- If we have n active nodes, n - 1 will still remain after a `unlink_current()` call.
    -- We unlink all of them by wrapping the calls in a loop.
    while true do
      if luasnip.session and luasnip.session.current_nodes[evt.buf] and not luasnip.session.jump_active then
        luasnip.unlink_current()
      else
        break
      end
    end
  end,
})

ditsuke avatar Apr 08 '23 11:04 ditsuke

Does #258 work for you? I think it implements exactly the DIY-approach you mentioned, and handles mode-changes due to jumping around

Is there any way to update nodes before forgetting snippet?

RiazanovKS avatar Nov 01 '24 19:11 RiazanovKS