Skip to content

Commit 24f6d27

Browse files
committed
Added support for generating sections of documentation
1 parent e932ba9 commit 24f6d27

7 files changed

Lines changed: 294 additions & 11 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ local opts = { noremap = true, silent = true }
105105
vim.api.nvim_set_keymap("n", "<Leader>nc", ":lua require('neogen').generate({ type = 'class' })<CR>", opts)
106106
```
107107

108+
If you'd like to generate only part of a docstring, add `sections`.
109+
110+
```lua
111+
require('neogen').generate({
112+
type = "func" -- the annotation type to generate. Currently supported: func, class, type, file
113+
sections = {"parameter", "return"}
114+
})
115+
```
116+
117+
Example sections:
118+
```
119+
parameter
120+
return
121+
throw
122+
yield
123+
```
124+
108125
### Snippet support
109126

110127
We added snippet support, and we provide defaults for some snippet engines.

doc/neogen.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,15 @@ Feel free to submit a PR, I will be happy to help you !
182182
We use semantic versioning ! (https://semver.org)
183183
Here is the current Neogen version:
184184
>lua
185-
neogen.version = "2.19.4"
185+
186+
neogen.version = "2.20.0"
186187
<
187188
# Changelog~
188189

189190
Note: We will only document `major` and `minor` versions, not `patch` ones. (only X and Y in X.Y.z)
190191

192+
## 2.20.0~
193+
- Added `sections` support for docstring generation (#185)
191194
## 2.19.0~
192195
- Add support for julia (`julia`) ! (#185)
193196
## 2.18.0~
@@ -464,4 +467,4 @@ If not specified, will use this line for all types.
464467
{required} `(string)` If specified, is used in if the first field of the table is a `table` (example above)
465468

466469

467-
vim:tw=78:ts=8:noet:ft=help:norl:
470+
vim:tw=78:ts=8:noet:ft=help:norl:

lua/neogen/configurations/python.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,8 @@ return {
460460
},
461461
},
462462
},
463+
locator = require("neogen.locators.python"),
463464
-- Use default granulator and generator
464-
locator = nil,
465465
granulator = nil,
466466
generator = nil,
467467
template = template

lua/neogen/generator.lua

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,13 @@ end
152152
---@param template table a template from the configuration
153153
---@param required_type string
154154
---@param annotation_convention string
155+
---@param sections string[] | string the parts of a docstring to create
155156
---@return table { line, content }, with line being the line to append the content
156-
local function generate_content(parent, data, template, required_type, annotation_convention)
157+
local function generate_content(parent, data, template, required_type, annotation_convention, partial)
158+
if partial == nil then
159+
partial = false
160+
end
161+
157162
local row, col = get_place_pos(parent, template.position, template.append, required_type)
158163

159164
local commentstring = vim.trim(vim.bo.commentstring:format(""))
@@ -188,7 +193,7 @@ local function generate_content(parent, data, template, required_type, annotatio
188193
end
189194

190195
local ins_type = type(inserted_type)
191-
if ins_type == "nil" then
196+
if not partial and ins_type == "nil" then
192197
local no_data = vim.tbl_isempty(data)
193198
if opts.no_results then
194199
if no_data then
@@ -231,11 +236,110 @@ local function generate_content(parent, data, template, required_type, annotatio
231236
end
232237
end
233238

239+
if partial then
240+
local index = 1
241+
242+
while result[index] == "" do
243+
table.remove(result, index)
244+
end
245+
end
246+
234247
return row, result, default_text
235248
end
236249

250+
--- Interpret all `sections` into Neogen-compatible section names.
251+
--- Each section name is a partial match. e.g. "parameter" will match
252+
--- "has_parameter" and "parameters".
253+
---@param sections string[] | string A user's desired parts of a docstring to create.
254+
---@return string[] # The resolved section names.
255+
---
256+
local function expand_sections(sections)
257+
local function has_match(expression, options)
258+
for _, option in ipairs(options) do
259+
if option:match(expression) then
260+
return true
261+
end
262+
end
263+
264+
return false
265+
end
266+
267+
local i = require("neogen.types.template").item
268+
269+
if type(sections) == "string" then
270+
sections = {sections}
271+
end
272+
273+
local has_parameter = false
274+
local parameters = {
275+
i.ArbitraryArgs,
276+
i.HasParameter,
277+
i.Kwargs,
278+
i.Parameter,
279+
i.Tparam,
280+
i.Vararg,
281+
}
282+
283+
local has_return = false
284+
local returns = {
285+
i.HasReturn,
286+
i.Return,
287+
i.ReturnAnonym,
288+
i.ReturnTypeHint,
289+
}
290+
291+
local has_throw = false
292+
local throws = { i.HasThrow, i.Throw }
293+
294+
local has_yield = false
295+
local yields = { i.HasYield, i.Yield }
296+
297+
local output = {}
298+
299+
for _, section in ipairs(sections) do
300+
if not has_parameter and has_match(section, parameters) then
301+
vim.list_extend(output, parameters)
302+
has_parameter = true
303+
end
304+
305+
if not has_return and has_match(section, returns) then
306+
vim.list_extend(output, returns)
307+
has_return = true
308+
end
309+
310+
if not has_throw and has_match(section, throws) then
311+
vim.list_extend(output, throws)
312+
has_throw = true
313+
end
314+
315+
if not has_yield and has_match(section, yields) then
316+
vim.list_extend(output, yields)
317+
has_yield = true
318+
end
319+
end
320+
321+
return output
322+
end
323+
324+
--- Remove `data` that is not present in `sections`.
325+
---@param data table the data from configurations[lang].data
326+
---@param sections string[] Any part of `data` not found in `sections` will be omitted.
327+
---@return table # The filtered output of `data`.
328+
local function filter_by_sections(data, sections)
329+
local output = {}
330+
331+
for key, value in pairs(data) do
332+
if vim.tbl_contains(sections, key) then
333+
output[key] = value
334+
end
335+
end
336+
337+
return output
338+
end
339+
340+
237341
return setmetatable({}, {
238-
__call = function(_, filetype, node_type, return_snippet, annotation_convention)
342+
__call = function(_, filetype, node_type, return_snippet, annotation_convention, sections)
239343
if filetype == "" then
240344
notify("No filetype detected", vim.log.levels.WARN)
241345
return
@@ -279,9 +383,21 @@ return setmetatable({}, {
279383

280384
local data = granulator(parent_node, language.data[node_type])
281385

386+
local partial = false
387+
388+
if sections ~= nil then
389+
partial = true
390+
sections = expand_sections(sections)
391+
data = filter_by_sections(data, sections)
392+
end
393+
282394
-- Will try to generate the documentation from a template and the data found from the granulator
283395
local row, template_content, default_text =
284-
generate_content(parent_node, data, template, node_type, annotation_convention[filetype])
396+
generate_content(parent_node, data, template, node_type, annotation_convention[filetype], partial)
397+
398+
if partial then
399+
row = vim.api.nvim_win_get_cursor(0)[1]
400+
end
285401

286402
local content = {}
287403
local marks_pos = {}

lua/neogen/init.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ neogen.configuration = {
154154
--- This is language specific. For example, `generate({ annotation_convention = { python = 'numpydoc' } })`
155155
--- If no convention is specified for a specific language, it'll use the default annotation convention for the language.
156156
--- - {opts.return_snippet} `boolean` if true, will return 3 values from the function call.
157+
--- - {opts.sections} `string | string[]` if provided, only these parts of a docstring are created.
157158
--- This option is useful if you want to get the snippet to use with a unsupported snippet engine
158159
--- Below are the returned values:
159160
--- - 1: (type: `string[]`) the resulting lsp snippet
@@ -167,7 +168,7 @@ neogen.generate = function(opts)
167168
end
168169

169170
opts = opts or {}
170-
return require("neogen.generator")(vim.bo.filetype, opts.type, opts.return_snippet, opts.annotation_convention)
171+
return require("neogen.generator")(vim.bo.filetype, opts.type, opts.return_snippet, opts.annotation_convention, opts.sections)
171172
end
172173

173174
-- Expose more API ============================================================
@@ -309,7 +310,7 @@ end
309310
--- with multiple annotation conventions.
310311
---@tag neogen-changelog
311312
---@toc_entry Changes in neogen plugin
312-
neogen.version = "2.19.4"
313+
neogen.version = "2.20.0"
313314
--minidoc_afterlines_end
314315

315316
return neogen

lua/neogen/locators/python.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---@param node_info Neogen.node_info
2+
---@param nodes_to_match TSNode[]
3+
---@return TSNode?
4+
return function(node_info, nodes_to_match)
5+
local current = node_info.current
6+
local parents = { "class_definition", "function_definition", "module" }
7+
8+
while current do
9+
if vim.tbl_contains(parents, current:type()) then
10+
return current
11+
end
12+
13+
current = current:parent()
14+
end
15+
16+
return nil
17+
end

0 commit comments

Comments
 (0)