luasnip.lua 4.6 KB

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