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

@@ -46,7 +46,7 @@ use {
end, end,
requires = "nvim-treesitter/nvim-treesitter", requires = "nvim-treesitter/nvim-treesitter",
-- Uncomment next line if you want to follow only stable versions -- Uncomment next line if you want to follow only stable versions
-- tag = "*" -- tag = "*"
} }
``` ```
@@ -93,11 +93,38 @@ local opts = { noremap = true, silent = true }
vim.api.nvim_set_keymap("n", "<Leader>nc", ":lua require('neogen').generate({ type = 'class' })<CR>", opts) vim.api.nvim_set_keymap("n", "<Leader>nc", ":lua require('neogen').generate({ type = 'class' })<CR>", opts)
``` ```
### Cycle between annotations ### Snippet support
I added support passing cursor positionings in templates. That means you can now cycle your cursor between different parts of the annotation. We added snippet support, and we provide defaults for some snippet engines.
And this is done via the `snippet_engine` option in neogen's setup:
If you want to map some keys to the cycling feature, you can do like so: - `snippet_engine` option will use provided engine to place the annotations:
Currently supported: `luasnip`.
```lua
require('neogen').setup({ snippet_engine = "luasnip" })
```
That's all ! You can now use your favorite snippet engine to control the annotation, like jumping between placeholders.
Or, if you want to return the snippet as a string (to integrate with other snippet engines, for example),
you can do it by using the `return_snippet` option in the `generate` function:
- `return_snippet` option will return the annotations as lsp snippets.
```lua
local snippet, row, col = require('neogen').generate({ snippet_engine = "luasnip" })
```
And then pass the snippet to the plugin's snippet expansion function.
### Default cycling support
_Note that this part is only useful if you don't use the snippets integration._
If you don't want to use a snippet engine with Neogen, you can leverage Neogen's native jumps between placeholders.
To map some keys to the cycling feature, you can do like so:
```lua ```lua
local opts = { noremap = true, silent = true } local opts = { noremap = true, silent = true }

View File

@@ -8,6 +8,7 @@ Table of contents:
Generate annotations.....................................|neogen.generate()| Generate annotations.....................................|neogen.generate()|
Contributing................................................|neogen-develop| Contributing................................................|neogen-develop|
Changes in neogen plugin..................................|neogen-changelog| Changes in neogen plugin..................................|neogen-changelog|
Use popular snippet engines............................|snippet-integration|
Configurations for the template table........|neogen-template-configuration| Configurations for the template table........|neogen-template-configuration|
How to create/customize an annotation....................|neogen-annotation| How to create/customize an annotation....................|neogen-annotation|
@@ -97,18 +98,24 @@ Neogen provides those defaults, and you can change them to suit your needs
-- Configuration for default languages -- Configuration for default languages
languages = {}, languages = {},
-- Use a snippet engine to generate annotations.
snippet_engine = nil,
} }
< <
# Notes~ # Notes~
- to configure a language, just add your configurations in the `languages` table. - to configure a language, just add your configurations in the `languages` table.
For example, for the `lua` lang: For example, for the `lua` lang:
> >
languages = { languages = {
lua = { -- Configuration here } 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"`
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
@@ -121,9 +128,15 @@ 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. Neogen will go until the start of the function and start annotating for you.
Parameters~ Parameters~
{opts} `(table)` Options to change default behaviour of generation. {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. - {opts.type} `(string, default: "func")` Which type we are trying to use for generating annotations.
Currently supported: `func`, `class`, `type`, `file` 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
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*neogen-develop* *neogen-develop*
@@ -152,6 +165,10 @@ Here is the current Neogen version:
Note: We will only document `major` and `minor` versions, not `patch` ones. 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~ ## 2.2.0~
### Python~ ### Python~
- Add support for `*args` and `**kwargs` - Add support for `*args` and `**kwargs`
@@ -167,6 +184,22 @@ Note: We will only document `major` and `minor` versions, not `patch` ones.
with multiple annotation conventions. with multiple annotation conventions.
==============================================================================
------------------------------------------------------------------------------
*snippet-integration*
`snippet`
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)
============================================================================== ==============================================================================
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*neogen-template-configuration* *neogen-template-configuration*

View File

@@ -13,6 +13,7 @@ local granulator = require("neogen.granulator")
local mark = require("neogen.mark") local mark = require("neogen.mark")
local nodes = require("neogen.utilities.nodes") local nodes = require("neogen.utilities.nodes")
local default_locator = require("neogen.locators.default") local default_locator = require("neogen.locators.default")
local snippet = require("neogen.snippet")
local JUMP_TEXT = "$1" local JUMP_TEXT = "$1"
local function get_parent_node(filetype, typ, language) local function get_parent_node(filetype, typ, language)
@@ -33,7 +34,7 @@ end
---@param n integer ---@param n integer
---@return string ---@return string
local function prefix_generator(template, commentstring, n) 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 -- Do not append the comment string if not wanted
if template.use_default_comment ~= false then if template.use_default_comment ~= false then
@@ -155,7 +156,7 @@ local function generate_content(parent, data, template, required_type)
end end
return setmetatable({}, { return setmetatable({}, {
__call = function(_, filetype, typ) __call = function(_, filetype, typ, return_snippet)
if filetype == "" then if filetype == "" then
notify("No filetype detected", vim.log.levels.WARN) notify("No filetype detected", vim.log.levels.WARN)
return return
@@ -210,19 +211,42 @@ return setmetatable({}, {
end end
end end
-- Append content to row if return_snippet then
vim.api.nvim_buf_set_lines(0, row, row, true, content) -- 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 local snippet_engine = conf.snippet_engine
-- Start session of marks if snippet_engine then
mark:start() -- User want to use a snippet engine instead of native handling
for _, pos in ipairs(marks_pos) do local engines = snippet.engines
mark:add_mark(pos) 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 end
vim.cmd("startinsert")
mark:jump()
-- Add range mark after first jump
mark:add_range_mark({ row, 0, row + #template_content, 1 })
end end
end, end,
}) })

View File

@@ -91,12 +91,15 @@ end
--- ---
--- - to configure a language, just add your configurations in the `languages` table. --- - to configure a language, just add your configurations in the `languages` table.
--- For example, for the `lua` lang: --- For example, for the `lua` lang:
--- > --- >
--- languages = { --- languages = {
--- lua = { -- Configuration here } --- 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 ---@toc_entry Configure the setup
---@tag neogen-configuration ---@tag neogen-configuration
@@ -109,6 +112,9 @@ neogen.configuration = {
-- Configuration for default languages -- Configuration for default languages
languages = {}, languages = {},
-- Use a snippet engine to generate annotations.
snippet_engine = nil,
} }
--minidoc_afterlines_end --minidoc_afterlines_end
@@ -120,9 +126,15 @@ neogen.configuration = {
--- For example, if you are inside a function, and called `generate({ type = "func" })`, --- 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. --- Neogen will go until the start of the function and start annotating for you.
--- ---
---@param opts table Options to change default behaviour of generation. ---@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. --- - {opts.type} `(string, default: "func")` Which type we are trying to use for generating annotations.
--- Currently supported: `func`, `class`, `type`, `file` --- 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 ---@toc_entry Generate annotations
neogen.generate = function(opts) neogen.generate = function(opts)
if not conf or not conf.enabled then if not conf or not conf.enabled then
@@ -130,7 +142,7 @@ neogen.generate = function(opts)
return return
end 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 end
-- Expose more API ============================================================ -- Expose more API ============================================================
@@ -204,6 +216,10 @@ end
--- ---
--- Note: We will only document `major` and `minor` versions, not `patch` ones. --- 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~ --- ## 2.2.0~
--- ### Python~ --- ### Python~
--- - Add support for `*args` and `**kwargs` --- - 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

View File

@@ -4,4 +4,4 @@ if _G.MiniDoc == nil then
minidoc.setup() minidoc.setup()
end end
minidoc.generate({ "lua/neogen/init.lua", "lua/neogen/template.lua" }, nil, nil) minidoc.generate({ "lua/neogen/init.lua", "lua/neogen/snippet.lua", "lua/neogen/template.lua" }, nil, nil)