Snippet support (#72)

To know more about snippet integration, please visit `:h snippet-integration`, or https://github.com/danymat/neogen#snippet-support !

Co-authored-by: danymat <d.danymat@gmail.com>
This commit is contained in:
mg979
2022-02-26 12:48:11 +01:00
committed by GitHub
parent cd10ee6f55
commit d5f447e5cc
6 changed files with 182 additions and 29 deletions

View File

@@ -13,6 +13,7 @@ local granulator = require("neogen.granulator")
local mark = require("neogen.mark")
local nodes = require("neogen.utilities.nodes")
local default_locator = require("neogen.locators.default")
local snippet = require("neogen.snippet")
local JUMP_TEXT = "$1"
local function get_parent_node(filetype, typ, language)
@@ -33,7 +34,7 @@ end
---@param n integer
---@return string
local function prefix_generator(template, commentstring, n)
local prefix = (" "):rep(n)
local prefix = (vim.bo.expandtab and " " or "\t"):rep(n)
-- Do not append the comment string if not wanted
if template.use_default_comment ~= false then
@@ -155,7 +156,7 @@ local function generate_content(parent, data, template, required_type)
end
return setmetatable({}, {
__call = function(_, filetype, typ)
__call = function(_, filetype, typ, return_snippet)
if filetype == "" then
notify("No filetype detected", vim.log.levels.WARN)
return
@@ -210,19 +211,42 @@ return setmetatable({}, {
end
end
-- Append content to row
vim.api.nvim_buf_set_lines(0, row, row, true, content)
if return_snippet then
-- User just wants the snippet, so we give him the snippet plus placement informations
local generated_snippet = snippet.to_snippet(content, marks_pos, { row, 0 })
return generated_snippet, row
end
if #marks_pos > 0 then
-- Start session of marks
mark:start()
for _, pos in ipairs(marks_pos) do
mark:add_mark(pos)
local snippet_engine = conf.snippet_engine
if snippet_engine then
-- User want to use a snippet engine instead of native handling
local engines = snippet.engines
if not vim.tbl_contains(vim.tbl_keys(engines), snippet_engine) then
notify(string.format("Snippet engine '%s' not supported", snippet_engine), vim.log.levels.ERROR)
return
end
-- Converts the content to a lsp compatible snippet
local generated_snippet = snippet.to_snippet(content, marks_pos, { row, 0 })
-- Calls the snippet expand function for required snippet engine
engines[snippet_engine](generated_snippet, { row, 0 })
return
else
-- We use default marks for jumping between annotations
-- Append content to row
vim.api.nvim_buf_set_lines(0, row, row, true, content)
if #marks_pos > 0 then
-- Start session of marks
mark:start()
for _, pos in ipairs(marks_pos) do
mark:add_mark(pos)
end
vim.cmd("startinsert")
mark:jump()
-- Add range mark after first jump
mark:add_range_mark({ row, 0, row + #template_content, 1 })
end
vim.cmd("startinsert")
mark:jump()
-- Add range mark after first jump
mark:add_range_mark({ row, 0, row + #template_content, 1 })
end
end,
})

View File

@@ -91,12 +91,15 @@ end
---
--- - to configure a language, just add your configurations in the `languages` table.
--- For example, for the `lua` lang:
--- >
--- >
--- languages = {
--- lua = { -- Configuration here }
--- }
--- <
--- Default configurations for a languages can be found in `lua/neogen/configurations/<you_language>.lua`
--- <
--- Default configurations for a languages can be found in `lua/neogen/configurations/<your_language>.lua`
---
--- - To know which snippet engines are supported, take a look at |snippet-integration|.
--- Example: `snippet_engine = "luasnip"`
---
---@toc_entry Configure the setup
---@tag neogen-configuration
@@ -109,6 +112,9 @@ neogen.configuration = {
-- Configuration for default languages
languages = {},
-- Use a snippet engine to generate annotations.
snippet_engine = nil,
}
--minidoc_afterlines_end
@@ -120,9 +126,15 @@ neogen.configuration = {
--- For example, if you are inside a function, and called `generate({ type = "func" })`,
--- Neogen will go until the start of the function and start annotating for you.
---
---@param opts table Options to change default behaviour of generation.
--- - {opts.type} `(string?, default: "func")` Which type we are trying to use for generating annotations.
---@param opts table Optional configs to change default behaviour of generation.
--- - {opts.type} `(string, default: "func")` Which type we are trying to use for generating annotations.
--- Currently supported: `func`, `class`, `type`, `file`
--- - {opts.return_snippet} `boolean` if true, will return 3 values from the function call.
--- This option is useful if you want to get the snippet to use with a unsupported snippet engine
--- Below are the returned values:
--- - 1: (type: `string[]`) the resulting lsp snippet
--- - 2: (type: `number`) the `row` to insert the annotations
--- - 3: (type: `number`) the `col` to insert the annotations
---@toc_entry Generate annotations
neogen.generate = function(opts)
if not conf or not conf.enabled then
@@ -130,7 +142,7 @@ neogen.generate = function(opts)
return
end
require("neogen.generator")(vim.bo.filetype, opts and opts.type)
return require("neogen.generator")(vim.bo.filetype, opts and opts.type, opts and opts.return_snippet)
end
-- Expose more API ============================================================
@@ -204,6 +216,10 @@ end
---
--- Note: We will only document `major` and `minor` versions, not `patch` ones.
---
--- ## 2.3.0~
--- - Added bundled support with snippet engines !
--- Check out |snippet-integration| for basic setup
--- For more information of possible options, check out |neogen.generate()|
--- ## 2.2.0~
--- ### Python~
--- - Add support for `*args` and `**kwargs`

53
lua/neogen/snippet.lua Normal file
View File

@@ -0,0 +1,53 @@
local notify = require("neogen.utilities.helpers").notify
---
--- To use a snippet engine, pass the option into neogen setup:
--- >
--- require('neogen').setup({
--- snippet_engine = "luasnip",
--- ...
--- })
--- <
--- Some snippet engines come out of the box bundled with neogen:
--- - `"luasnip"` (https://github.com/L3MON4D3/LuaSnip)
---@tag snippet-integration
---@toc_entry Use popular snippet engines
local snippet = {}
snippet.engines = {}
--- Converts a template to a lsp-compatible snippet
---@param template string[] the generated annotations to parse
---@param marks table generated marks for the annotations
---@param pos table a tuple of row,col
---@return table resulting snippet lines
---@private
snippet.to_snippet = function(template, marks, pos)
local offset = {}
for i, m in ipairs(marks) do
local r, col = m[1] - pos[1] + 1, m[2]
if offset[r] then
offset[r] = offset[r] + tostring(i - 1):len() + 1
else
offset[r] = 0
end
local pre = template[r]:sub(1, col + offset[r])
template[r] = pre .. "$" .. i .. template[r]:sub(col + 1 + offset[r])
end
return template
end
--- Expand snippet for luasnip engine
---@param snip string the snippet to expand
---@param pos table a tuple of row, col
---@private
snippet.engines.luasnip = function(snip, pos)
local ok, luasnip = pcall(require, "luasnip")
if not ok then
notify("Luasnip not found, aborting...", vim.log.levels.ERROR)
return
end
vim.fn.append(pos[1], "")
luasnip.lsp_expand(table.concat(snip, "\n"), { pos = { pos[1], pos[2] } })
end
return snippet