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