diff options
author | kylo252 <[email protected]> | 2022-11-25 12:07:35 +0100 |
---|---|---|
committer | opalmay <[email protected]> | 2023-02-06 21:40:05 +0200 |
commit | 2c8b5e52753357632c75ce2af911a8d3fb23accf (patch) | |
tree | 5834aa46f2cea018b7e28b99ff384591a354e181 /lua/lvim/core/luasnip.lua | |
parent | 077a2e9e91b4466b9ded7f3d9204cd34c3c710f0 (diff) |
refactor(luasnip): simplify structure
Diffstat (limited to 'lua/lvim/core/luasnip.lua')
-rw-r--r-- | lua/lvim/core/luasnip.lua | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/lua/lvim/core/luasnip.lua b/lua/lvim/core/luasnip.lua new file mode 100644 index 00000000..10782d0e --- /dev/null +++ b/lua/lvim/core/luasnip.lua @@ -0,0 +1,163 @@ +local M = {} + +-- see luasnip/util/types.lua +local types = { + textNode = 1, + insertNode = 2, + functionNode = 3, + snippetNode = 4, + choiceNode = 5, + dynamicNode = 6, + snippet = 7, + exitNode = 8, + restoreNode = 9, + node_types = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, +} + +function M.config() + lvim.builtin.luasnip = { + sources = { + friendly_snippets = true, + }, + config = { + history = false, + updateevents = "TextChanged,TextChangedI", + ext_opts = { + -- Will be populated within config function + }, + }, + ext_opts = { + -- Show virtual text to signal when you are inside an sippets + [types.insertNode] = { + active = { + virt_text = { { "<-- snip insert", "BufferInactiveIndex" } }, + }, + }, + -- Helps to notice when you are within a choice node + [types.choiceNode] = { + active = { + virt_text = { { "<-- choice", "BufferInactiveIndex" } }, + }, + }, + }, + } +end + +function M.setup() + local utils = require "lvim.utils" + local paths = {} + if lvim.builtin.luasnip.sources.friendly_snippets then + paths[#paths + 1] = utils.join_paths(get_runtime_dir(), "site", "pack", "packer", "start", "friendly-snippets") + end + + local user_snippets = utils.join_paths(get_config_dir(), "snippets") + if utils.is_directory(user_snippets) then + paths[#paths + 1] = user_snippets + end + + local luasnip = require "luasnip" + luasnip.config.set_config(lvim.builtin.luasnip.config) + + -- When no paths are provided, luasnip will search in the runtimepath + require("luasnip.loaders.from_lua").lazy_load() + require("luasnip.loaders.from_vscode").lazy_load { paths = paths } + require("luasnip.loaders.from_snipmate").lazy_load() +end + +---when inside a snippet, seeks to the nearest luasnip field if possible, and checks if it is jumpable +---@param dir number 1 for forward, -1 for backward; defaults to 1 +---@return boolean true if a jumpable luasnip field is found while inside a snippet +local function jumpable(dir) + local luasnip_ok, luasnip = pcall(require, "luasnip") + if not luasnip_ok then + return false + end + + local win_get_cursor = vim.api.nvim_win_get_cursor + local get_current_buf = vim.api.nvim_get_current_buf + + ---sets the current buffer's luasnip to the one nearest the cursor + ---@return boolean true if a node is found, false otherwise + local function seek_luasnip_cursor_node() + -- TODO(kylo252): upstream this + -- for outdated versions of luasnip + if not luasnip.session.current_nodes then + return false + end + + local node = luasnip.session.current_nodes[get_current_buf()] + if not node then + return false + end + + local snippet = node.parent.snippet + local exit_node = snippet.insert_nodes[0] + + local pos = win_get_cursor(0) + pos[1] = pos[1] - 1 + + -- exit early if we're past the exit node + if exit_node then + local exit_pos_end = exit_node.mark:pos_end() + if (pos[1] > exit_pos_end[1]) or (pos[1] == exit_pos_end[1] and pos[2] > exit_pos_end[2]) then + snippet:remove_from_jumplist() + luasnip.session.current_nodes[get_current_buf()] = nil + + return false + end + end + + node = snippet.inner_first:jump_into(1, true) + while node ~= nil and node.next ~= nil and node ~= snippet do + local n_next = node.next + local next_pos = n_next and n_next.mark:pos_begin() + local candidate = n_next ~= snippet and next_pos and (pos[1] < next_pos[1]) + or (pos[1] == next_pos[1] and pos[2] < next_pos[2]) + + -- Past unmarked exit node, exit early + if n_next == nil or n_next == snippet.next then + snippet:remove_from_jumplist() + luasnip.session.current_nodes[get_current_buf()] = nil + + return false + end + + if candidate then + luasnip.session.current_nodes[get_current_buf()] = node + return true + end + + local ok + ok, node = pcall(node.jump_from, node, 1, true) -- no_move until last stop + if not ok then + snippet:remove_from_jumplist() + luasnip.session.current_nodes[get_current_buf()] = nil + + return false + end + end + + -- No candidate, but have an exit node + if exit_node then + -- to jump to the exit node, seek to snippet + luasnip.session.current_nodes[get_current_buf()] = snippet + return true + end + + -- No exit node, exit from snippet + snippet:remove_from_jumplist() + luasnip.session.current_nodes[get_current_buf()] = nil + return false + end + + if dir == -1 then + return luasnip.in_snippet() and luasnip.jumpable(-1) else + return luasnip.in_snippet() and seek_luasnip_cursor_node() and luasnip.jumpable(1) + end +end + +M.methods = {} + +M.methods.jumpable = jumpable + +return M |