luasnip.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. local M = {}
  2. -- see luasnip/util/types.lua
  3. local types = {
  4. textNode = 1,
  5. insertNode = 2,
  6. functionNode = 3,
  7. snippetNode = 4,
  8. choiceNode = 5,
  9. dynamicNode = 6,
  10. snippet = 7,
  11. exitNode = 8,
  12. restoreNode = 9,
  13. node_types = { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
  14. }
  15. function M.config()
  16. lvim.builtin.luasnip = {
  17. active = true,
  18. sources = {
  19. friendly_snippets = true,
  20. },
  21. opts = {
  22. history = false,
  23. update_events = "InsertLeave",
  24. enable_autosnippets = false,
  25. ext_opts = {
  26. -- Show virtual text to signal when you are inside an sippets
  27. [types.insertNode] = {
  28. active = {
  29. virt_text = { { "<-- snip", "BufferInactiveIndex" } },
  30. },
  31. },
  32. -- Helps to notice when you are within a choice node
  33. [types.choiceNode] = {
  34. active = {
  35. virt_text = { { "<-- choice", "BufferInactiveIndex" } },
  36. },
  37. },
  38. },
  39. },
  40. }
  41. end
  42. function M.setup()
  43. local utils = require "lvim.utils"
  44. local paths = {}
  45. if lvim.builtin.luasnip.sources.friendly_snippets then
  46. paths[#paths + 1] = utils.join_paths(get_runtime_dir(), "site", "pack", "lazy", "opt", "friendly-snippets")
  47. end
  48. local user_snippets = utils.join_paths(get_config_dir(), "snippets")
  49. if utils.is_directory(user_snippets) then
  50. paths[#paths + 1] = user_snippets
  51. end
  52. local luasnip = require "luasnip"
  53. luasnip.config.set_config(lvim.builtin.luasnip.opts)
  54. -- When no paths are provided, luasnip will search in the runtimepath
  55. require("luasnip.loaders.from_lua").lazy_load()
  56. require("luasnip.loaders.from_vscode").lazy_load { paths = paths }
  57. require("luasnip.loaders.from_snipmate").lazy_load()
  58. end
  59. ---when inside a snippet, seeks to the nearest luasnip field if possible, and checks if it is jumpable
  60. ---@param dir number 1 for forward, -1 for backward; defaults to 1
  61. ---@return boolean|nil true if a jumpable luasnip field is found while inside a snippet
  62. local function jumpable(dir)
  63. local luasnip_ok, luasnip = pcall(require, "luasnip")
  64. if not luasnip_ok then
  65. return false
  66. end
  67. local win_get_cursor = vim.api.nvim_win_get_cursor
  68. local get_current_buf = vim.api.nvim_get_current_buf
  69. ---sets the current buffer's luasnip to the one nearest the cursor
  70. ---@return boolean true if a node is found, false otherwise
  71. local function seek_luasnip_cursor_node()
  72. -- TODO(kylo252): upstream this
  73. -- for outdated versions of luasnip
  74. if not luasnip.session.current_nodes then
  75. return false
  76. end
  77. local node = luasnip.session.current_nodes[get_current_buf()]
  78. if not node then
  79. return false
  80. end
  81. local snippet = node.parent.snippet
  82. local exit_node = snippet.insert_nodes[0]
  83. local pos = win_get_cursor(0)
  84. pos[1] = pos[1] - 1
  85. -- exit early if we're past the exit node
  86. if exit_node then
  87. local exit_pos_end = exit_node.mark:pos_end()
  88. if (pos[1] > exit_pos_end[1]) or (pos[1] == exit_pos_end[1] and pos[2] > exit_pos_end[2]) then
  89. snippet:remove_from_jumplist()
  90. luasnip.session.current_nodes[get_current_buf()] = nil
  91. return false
  92. end
  93. end
  94. node = snippet.inner_first:jump_into(1, true)
  95. while node ~= nil and node.next ~= nil and node ~= snippet do
  96. local n_next = node.next
  97. local next_pos = n_next and n_next.mark:pos_begin()
  98. local candidate = n_next ~= snippet and next_pos and (pos[1] < next_pos[1])
  99. or (pos[1] == next_pos[1] and pos[2] < next_pos[2])
  100. -- Past unmarked exit node, exit early
  101. if n_next == nil or n_next == snippet.next then
  102. snippet:remove_from_jumplist()
  103. luasnip.session.current_nodes[get_current_buf()] = nil
  104. return false
  105. end
  106. if candidate then
  107. luasnip.session.current_nodes[get_current_buf()] = node
  108. return true
  109. end
  110. local ok
  111. ok, node = pcall(node.jump_from, node, 1, true) -- no_move until last stop
  112. if not ok then
  113. snippet:remove_from_jumplist()
  114. luasnip.session.current_nodes[get_current_buf()] = nil
  115. return false
  116. end
  117. end
  118. -- No candidate, but have an exit node
  119. if exit_node then
  120. -- to jump to the exit node, seek to snippet
  121. luasnip.session.current_nodes[get_current_buf()] = snippet
  122. return true
  123. end
  124. -- No exit node, exit from snippet
  125. snippet:remove_from_jumplist()
  126. luasnip.session.current_nodes[get_current_buf()] = nil
  127. return false
  128. end
  129. if dir == -1 then
  130. return luasnip.in_snippet() and luasnip.jumpable(-1)
  131. else
  132. return luasnip.in_snippet() and seek_luasnip_cursor_node() and luasnip.jumpable(1)
  133. end
  134. end
  135. M.methods = {}
  136. M.methods.jumpable = jumpable
  137. return M