refactor: refactor code base
This commit is contained in:
@@ -99,7 +99,7 @@ Neogen provides those defaults, and you can change them to suit your needs
|
|||||||
jump_text = "$1",
|
jump_text = "$1",
|
||||||
|
|
||||||
-- Configuration for default languages
|
-- Configuration for default languages
|
||||||
languages = {},
|
languages = {}
|
||||||
}
|
}
|
||||||
<
|
<
|
||||||
# Notes~
|
# Notes~
|
||||||
|
|||||||
23
lua/neogen/config.lua
Normal file
23
lua/neogen/config.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
local config = {_data = {}}
|
||||||
|
|
||||||
|
config.get = function()
|
||||||
|
return config._data
|
||||||
|
end
|
||||||
|
|
||||||
|
config.setup = function(default, user)
|
||||||
|
local data = vim.tbl_deep_extend("keep", user or {}, default)
|
||||||
|
setmetatable(data.languages, {
|
||||||
|
__index = function(langs, ft)
|
||||||
|
local ok, ft_config = pcall(require, "neogen.configurations." .. ft)
|
||||||
|
if not ok then
|
||||||
|
ft_config = nil
|
||||||
|
end
|
||||||
|
rawset(langs, ft, ft_config)
|
||||||
|
return ft_config
|
||||||
|
end
|
||||||
|
})
|
||||||
|
config._data = data
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
return config
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local default_locator = require("neogen.locators.default")
|
local default_locator = require("neogen.locators.default")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
|
|
||||||
local c_params = {
|
local c_params = {
|
||||||
@@ -149,14 +149,14 @@ local c_config = {
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if node_info.current == nil then
|
if not node_info.current then
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if the function happens to be a function template we want to place
|
-- if the function happens to be a function template we want to place
|
||||||
-- the annotation before the template statement and extract the
|
-- the annotation before the template statement and extract the
|
||||||
-- template parameters names as well
|
-- template parameters names as well
|
||||||
if node_info.current:parent() == nil then
|
if not node_info.current:parent() then
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
if node_info.current:parent():type() == "template_declaration" then
|
if node_info.current:parent():type() == "template_declaration" then
|
||||||
@@ -165,10 +165,6 @@ local c_config = {
|
|||||||
return result
|
return result
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Use default granulator and generator
|
|
||||||
granulator = nil,
|
|
||||||
generator = nil,
|
|
||||||
|
|
||||||
template = template:add_default_annotation("doxygen"),
|
template = template:add_default_annotation("doxygen"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
parent = {
|
parent = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
local function_tree = {
|
local function_tree = {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
local function_tree = {
|
local function_tree = {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
local function_extractor = function(node, type)
|
local function_extractor = function(node, type)
|
||||||
if not vim.tbl_contains({ "local", "function" }, type) then
|
if not vim.tbl_contains({ "local", "function" }, type) then
|
||||||
@@ -145,9 +145,5 @@ return {
|
|||||||
-- Custom lua locator that escapes from comments
|
-- Custom lua locator that escapes from comments
|
||||||
locator = require("neogen.locators.lua"),
|
locator = require("neogen.locators.lua"),
|
||||||
|
|
||||||
-- Use default granulator and generator
|
|
||||||
granulator = nil,
|
|
||||||
generator = nil,
|
|
||||||
|
|
||||||
template = template:config({ use_default_comment = true }):add_default_annotation("emmylua"):add_annotation("ldoc"),
|
template = template:config({ use_default_comment = true }):add_default_annotation("emmylua"):add_annotation("ldoc"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local ts_utils = require("nvim-treesitter.ts_utils")
|
|||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local locator = require("neogen.locators.default")
|
local locator = require("neogen.locators.default")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
|
|
||||||
local parent = {
|
local parent = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
parent = {
|
parent = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local extractors = require("neogen.utilities.extractors")
|
local extractors = require("neogen.utilities.extractors")
|
||||||
local nodes_utils = require("neogen.utilities.nodes")
|
local nodes_utils = require("neogen.utilities.nodes")
|
||||||
local i = require("neogen.types.template").item
|
local i = require("neogen.types.template").item
|
||||||
local template = require("neogen.utilities.template")
|
local template = require("neogen.template")
|
||||||
|
|
||||||
local construct_type_annotation = function(parameters)
|
local construct_type_annotation = function(parameters)
|
||||||
local results = parameters and {} or nil
|
local results = parameters and {} or nil
|
||||||
|
|||||||
229
lua/neogen/generator.lua
Normal file
229
lua/neogen/generator.lua
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
local ok, ts_utils = pcall(require, "nvim-treesitter.ts_utils")
|
||||||
|
assert(ok, "neogen requires nvim-treesitter to operate :(")
|
||||||
|
|
||||||
|
local conf = require("neogen.config").get()
|
||||||
|
local granulator = require("neogen.granulator")
|
||||||
|
local helpers = require("neogen.utilities.helpers")
|
||||||
|
local notify = helpers.notify
|
||||||
|
|
||||||
|
local mark = require("neogen.mark")
|
||||||
|
local nodes = require("neogen.utilities.nodes")
|
||||||
|
local default_locator = require("neogen.locators.default")
|
||||||
|
|
||||||
|
local function get_parent_node(filetype, typ, language)
|
||||||
|
local parser = vim.treesitter.get_parser(0, filetype)
|
||||||
|
local tstree = parser:parse()[1]
|
||||||
|
local tree = tstree:root()
|
||||||
|
|
||||||
|
language.locator = language.locator or default_locator
|
||||||
|
-- Use the language locator to locate one of the required parent nodes above the cursor
|
||||||
|
return language.locator({root = tree, current = ts_utils.get_node_at_cursor(0)}, language.parent[typ])
|
||||||
|
end
|
||||||
|
|
||||||
|
---Generates the prefix according to `template` options.
|
||||||
|
---Prefix is generated with an offset (currently spaces) repetition of `n` times.
|
||||||
|
---If `template.use_default_comment` is not set to false, the `commentstring` is added
|
||||||
|
---@param template table
|
||||||
|
---@param commentstring string
|
||||||
|
---@param n integer
|
||||||
|
---@return string
|
||||||
|
local function prefix_generator(template, commentstring, n)
|
||||||
|
local prefix = (" "):rep(n)
|
||||||
|
|
||||||
|
-- Do not append the comment string if not wanted
|
||||||
|
if template.use_default_comment ~= false then
|
||||||
|
prefix = prefix .. commentstring
|
||||||
|
end
|
||||||
|
return prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_place_pos(parent, position, append, typ)
|
||||||
|
local row, col
|
||||||
|
-- You can use a custom position placement
|
||||||
|
if position and type(position) == "function" then
|
||||||
|
row, col = position(parent, typ)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If the custom placement does not return the correct row and cols, default to the node range
|
||||||
|
-- Same if there is no custom placement
|
||||||
|
if not row and not col then
|
||||||
|
-- Because the file type is always at top
|
||||||
|
if typ == "file" then
|
||||||
|
row, col = 0, 0
|
||||||
|
else
|
||||||
|
row, col = ts_utils.get_node_range(parent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append = append or {}
|
||||||
|
|
||||||
|
if append.position == "after" and not vim.tbl_contains(append.disabled or {}, typ) then
|
||||||
|
local child_node = nodes:matching_child_nodes(parent, append.child_name)[1]
|
||||||
|
if not child_node and append.fallback then
|
||||||
|
local fallback = nodes:matching_child_nodes(parent, append.fallback)[1]
|
||||||
|
if fallback then
|
||||||
|
row, col = fallback:range()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
row, col = child_node:range()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return row, col
|
||||||
|
end
|
||||||
|
|
||||||
|
---Uses the provided template to format the annotations with data found by the granulator
|
||||||
|
---@param parent userdata the node used to generate the annotations
|
||||||
|
---@param data table the data from the granulator, which is a set of [type] = results
|
||||||
|
---@param template table a template from the configuration
|
||||||
|
---@param jump_text string jump_text from the default or language configuration
|
||||||
|
---@param required_type string
|
||||||
|
---@return table { line, content }, with line being the line to append the content
|
||||||
|
local function generate_content(parent, data, template, required_type, jump_text)
|
||||||
|
assert(template, "Need a template table")
|
||||||
|
local row, col = get_place_pos(parent, template.position, template.append, required_type)
|
||||||
|
|
||||||
|
local commentstring = vim.trim(vim.bo.commentstring:format(""))
|
||||||
|
local generated_template
|
||||||
|
if not template.annotation_convention then
|
||||||
|
-- Default template
|
||||||
|
generated_template = {{nil, ""}, {"name", " @Summary "}, {"parameters", " @Param "}, {"return", " @Return "}}
|
||||||
|
else
|
||||||
|
generated_template = template[template.annotation_convention]
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local prefix = prefix_generator(template, commentstring, col)
|
||||||
|
|
||||||
|
local function append_str(str)
|
||||||
|
table.insert(result, str == "" and str or prefix .. str)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, values in ipairs(generated_template) do
|
||||||
|
local inserted_type, formatted_str, opts = unpack(values)
|
||||||
|
opts = opts or {}
|
||||||
|
if type(opts.type) ~= "table" or vim.tbl_contains(opts.type, required_type) then
|
||||||
|
-- Will append the item before all their nodes
|
||||||
|
if type(opts.before_first_item) == "table" and data[inserted_type] then
|
||||||
|
for _, s in ipairs(opts.before_first_item) do
|
||||||
|
append_str(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ins_type = type(inserted_type)
|
||||||
|
if ins_type == "nil" then
|
||||||
|
local no_data = vim.tbl_isempty(data)
|
||||||
|
if opts.no_results then
|
||||||
|
if no_data then
|
||||||
|
append_str(formatted_str)
|
||||||
|
end
|
||||||
|
elseif not no_data then
|
||||||
|
append_str(formatted_str:format(""))
|
||||||
|
end
|
||||||
|
elseif ins_type == "string" and type(data[inserted_type]) == "table" then
|
||||||
|
-- Format the output with the corresponding data
|
||||||
|
for _, s in ipairs(data[inserted_type]) do
|
||||||
|
append_str(formatted_str:format(s))
|
||||||
|
if opts.after_each then
|
||||||
|
append_str(opts.after_each:format(s))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif ins_type == "table" and #inserted_type > 0 and type(data[opts.required]) == "table" then
|
||||||
|
-- First item in the template item can be a table.
|
||||||
|
-- in this case, the template will use provided types to generate the line.
|
||||||
|
-- e.g {{ "type", "parameter"}, "* @type {%s} %s"}
|
||||||
|
-- will replace every %s with the provided data from those types
|
||||||
|
|
||||||
|
-- If one item is missing, it'll use the required option to iterate
|
||||||
|
-- and will replace the missing item with default jump_text
|
||||||
|
for _, extracted in pairs(data[opts.required]) do
|
||||||
|
local fmt_args = {}
|
||||||
|
for _, typ in ipairs(inserted_type) do
|
||||||
|
if extracted[typ] then
|
||||||
|
table.insert(fmt_args, extracted[typ][1])
|
||||||
|
else
|
||||||
|
table.insert(fmt_args, jump_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
append_str(formatted_str:format(unpack(fmt_args)))
|
||||||
|
if opts.after_each then
|
||||||
|
append_str(opts.after_each:format(unpack(fmt_args)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return row, result
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, {
|
||||||
|
__call = function(_, filetype, typ)
|
||||||
|
if filetype == "" then
|
||||||
|
notify("No filetype detected", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local language = conf.languages[filetype]
|
||||||
|
if not language then
|
||||||
|
notify("Language " .. filetype .. " not supported.", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
typ = (type(typ) ~= "string" or typ == "") and "func" or typ -- Default type
|
||||||
|
if not language.parent[typ] or not language.data[typ] then
|
||||||
|
notify("Type `" .. typ .. "` not supported", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local parent_node = get_parent_node(filetype, typ, language)
|
||||||
|
if not parent_node then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = granulator(parent_node, language.data[typ])
|
||||||
|
|
||||||
|
local jump_text = language.jump_text or conf.jump_text
|
||||||
|
|
||||||
|
-- Will try to generate the documentation from a template and the data found from the granulator
|
||||||
|
local row, template_content = generate_content(parent_node, data, language.template, typ, jump_text)
|
||||||
|
|
||||||
|
local content = {}
|
||||||
|
local marks_pos = {}
|
||||||
|
|
||||||
|
local input_after_comment = conf.input_after_comment
|
||||||
|
|
||||||
|
local pattern = jump_text .. (input_after_comment and "[|%w]*" or "|?")
|
||||||
|
for r, line in ipairs(template_content) do
|
||||||
|
local last_col = 0
|
||||||
|
local len = 1
|
||||||
|
local sects = {}
|
||||||
|
while true do
|
||||||
|
local s, e = line:find(pattern, last_col + 1)
|
||||||
|
if not s then
|
||||||
|
table.insert(sects, line:sub(last_col + 1, -1))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(sects, line:sub(last_col + 1, s - 1))
|
||||||
|
if input_after_comment then
|
||||||
|
len = len + s - last_col - 1
|
||||||
|
table.insert(marks_pos, {row + r - 1, len - 1})
|
||||||
|
end
|
||||||
|
last_col = e
|
||||||
|
end
|
||||||
|
table.insert(content, table.concat(sects, ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 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()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
local ts_utils = require("nvim-treesitter.ts_utils")
|
|
||||||
local nodes = require("neogen.utilities.nodes")
|
|
||||||
|
|
||||||
--- Generates the prefix according to `template` options.
|
|
||||||
--- Prefix is generated with an offset (currently spaces) repetition of `n` times.
|
|
||||||
--- If `template.use_default_comment` is not set to false, the `commentstring` is added
|
|
||||||
--- @param template table
|
|
||||||
--- @param commentstring string
|
|
||||||
--- @param n integer
|
|
||||||
--- @return string
|
|
||||||
local function prefix_generator(template, commentstring, n)
|
|
||||||
local prefix = (" "):rep(n)
|
|
||||||
|
|
||||||
-- Do not append the comment string if not wanted
|
|
||||||
if template.use_default_comment ~= false then
|
|
||||||
prefix = prefix .. commentstring
|
|
||||||
end
|
|
||||||
return prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Does some checks within the `value` and adds the `prefix` before it if required
|
|
||||||
--- @param prefix string
|
|
||||||
--- @param value string
|
|
||||||
--- @return string
|
|
||||||
local function conditional_prefix_inserter(prefix, value)
|
|
||||||
if value == "" then
|
|
||||||
return value
|
|
||||||
else
|
|
||||||
return prefix .. value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Insert values from `items` in `result` and returns it
|
|
||||||
--- @param result table
|
|
||||||
--- @param items table
|
|
||||||
--- @param prefix string
|
|
||||||
--- @return table result
|
|
||||||
local function add_values_to_result(result, items, prefix)
|
|
||||||
for _, value in ipairs(items) do
|
|
||||||
local inserted = conditional_prefix_inserter(prefix, value)
|
|
||||||
table.insert(result, inserted)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
---Default Generator:
|
|
||||||
---Uses the provided template to format the annotations with data found by the granulator
|
|
||||||
--- @param parent userdata the node used to generate the annotations
|
|
||||||
--- @param data table the data from the granulator, which is a set of [type] = results
|
|
||||||
--- @param template table a template from the configuration
|
|
||||||
--- @param required_type string
|
|
||||||
--- @return table { line, content, opts }, with line being the line to append the content
|
|
||||||
return function(parent, data, template, required_type)
|
|
||||||
local row_to_place, col_to_place
|
|
||||||
-- You can use a custom position placement
|
|
||||||
if template.position and type(template.position) == "function" then
|
|
||||||
row_to_place, col_to_place = template.position(parent, required_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If the custom placement does not return the correct row and cols, default to the node range
|
|
||||||
-- Same if there is no custom placement
|
|
||||||
if not row_to_place and not col_to_place then
|
|
||||||
-- Because the file type is always at top
|
|
||||||
if required_type == "file" then
|
|
||||||
row_to_place = 0
|
|
||||||
col_to_place = 0
|
|
||||||
else
|
|
||||||
row_to_place, col_to_place, _, _ = ts_utils.get_node_range(parent)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local commentstring, generated_template = vim.trim(vim.api.nvim_buf_get_option(0, "commentstring"):format(""))
|
|
||||||
|
|
||||||
local append = template.append or {}
|
|
||||||
|
|
||||||
if append.position == "after" and not vim.tbl_contains(append.disabled or {}, required_type) then
|
|
||||||
local child_node = nodes:matching_child_nodes(parent, append.child_name)[1]
|
|
||||||
if not child_node and append.fallback then
|
|
||||||
local fallback = nodes:matching_child_nodes(parent, append.fallback)[1]
|
|
||||||
if fallback then
|
|
||||||
row_to_place, col_to_place, _, _ = fallback:range()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
row_to_place, col_to_place, _, _ = child_node:range()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not template or not template.annotation_convention then
|
|
||||||
-- Default template
|
|
||||||
generated_template = {
|
|
||||||
{ nil, "" },
|
|
||||||
{ "name", " @Summary " },
|
|
||||||
{ "parameters", " @Param " },
|
|
||||||
{ "return", " @Return " },
|
|
||||||
}
|
|
||||||
elseif type(template) == "function" then
|
|
||||||
-- You can also pass a function as a template
|
|
||||||
generated_template = template(parent, commentstring, data)
|
|
||||||
else
|
|
||||||
generated_template = template[template.annotation_convention]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parse_generated_template()
|
|
||||||
local result = {}
|
|
||||||
local prefix = prefix_generator(template, commentstring, col_to_place)
|
|
||||||
|
|
||||||
for _, values in ipairs(generated_template) do
|
|
||||||
local inserted_type = values[1]
|
|
||||||
local formatted_string = values[2]
|
|
||||||
local opts = vim.deepcopy(values[3]) or {}
|
|
||||||
|
|
||||||
opts.type = opts.type or { required_type }
|
|
||||||
|
|
||||||
if opts.type and vim.tbl_contains(opts.type, required_type) then
|
|
||||||
-- Will append the item before all their nodes
|
|
||||||
if opts.before_first_item and data[inserted_type] then
|
|
||||||
result = add_values_to_result(result, opts.before_first_item, prefix)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If there is no data returned, will append the string with opts.no_results
|
|
||||||
if opts.no_results == true and vim.tbl_isempty(data) then
|
|
||||||
local inserted = conditional_prefix_inserter(prefix, formatted_string)
|
|
||||||
table.insert(result, inserted)
|
|
||||||
else
|
|
||||||
-- append the output as is
|
|
||||||
if inserted_type == nil and opts.no_results ~= true and not vim.tbl_isempty(data) then
|
|
||||||
local inserted = conditional_prefix_inserter(prefix, formatted_string:format(""))
|
|
||||||
table.insert(result, inserted)
|
|
||||||
elseif inserted_type then
|
|
||||||
-- Format the output with the corresponding data
|
|
||||||
if type(inserted_type) == "string" and data[inserted_type] then
|
|
||||||
for _, value in ipairs(data[inserted_type]) do
|
|
||||||
local inserted = conditional_prefix_inserter(prefix, formatted_string:format(value))
|
|
||||||
table.insert(result, inserted)
|
|
||||||
if opts.after_each then
|
|
||||||
table.insert(
|
|
||||||
result,
|
|
||||||
conditional_prefix_inserter(prefix, opts.after_each):format(value)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif type(inserted_type) == "table" and data[opts.required] then
|
|
||||||
-- First item in the template item can be a table.
|
|
||||||
-- in this case, the template will use provided types to generate the line.
|
|
||||||
-- e.g {{ "type", "parameter"}, "* @type {%s} %s"}
|
|
||||||
-- will replace every %s with the provided data from those types
|
|
||||||
|
|
||||||
-- If one item is missing, it'll use the required option to iterate
|
|
||||||
-- and will replace the missing item with default jump_text
|
|
||||||
for _, tbl in pairs(data[opts.required]) do
|
|
||||||
local _values = {}
|
|
||||||
for _, v in ipairs(inserted_type) do
|
|
||||||
if tbl[v] then
|
|
||||||
table.insert(_values, tbl[v][1])
|
|
||||||
else
|
|
||||||
local jump_text = neogen.configuration.jump_text
|
|
||||||
table.insert(_values, jump_text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not vim.tbl_isempty(_values) then
|
|
||||||
local inserted = conditional_prefix_inserter(
|
|
||||||
prefix,
|
|
||||||
formatted_string:format(unpack(_values))
|
|
||||||
)
|
|
||||||
table.insert(result, inserted)
|
|
||||||
if opts.after_each then
|
|
||||||
if type(opts.after_each) == "table" then
|
|
||||||
local _v = {}
|
|
||||||
local index_types = opts.after_each["index_types"]
|
|
||||||
for _, i in ipairs(index_types) do
|
|
||||||
table.insert(_v, _values[i])
|
|
||||||
end
|
|
||||||
_values = _v
|
|
||||||
table.insert(
|
|
||||||
result,
|
|
||||||
conditional_prefix_inserter(
|
|
||||||
prefix,
|
|
||||||
opts.after_each[1]:format(unpack(_values))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
table.insert(
|
|
||||||
result,
|
|
||||||
conditional_prefix_inserter(
|
|
||||||
prefix,
|
|
||||||
opts.after_each:format(unpack(_values))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
return row_to_place, col_to_place, parse_generated_template()
|
|
||||||
end
|
|
||||||
45
lua/neogen/granulator.lua
Normal file
45
lua/neogen/granulator.lua
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
local ts_utils = require("nvim-treesitter.ts_utils")
|
||||||
|
local helpers = require("neogen.utilities.helpers")
|
||||||
|
|
||||||
|
---Tries to use the configuration to find all required content nodes from the parent node
|
||||||
|
---@param parent_node userdata the node found by the locator
|
||||||
|
---@param node_data table the data from configurations[lang].data
|
||||||
|
return function(parent_node, node_data)
|
||||||
|
local result = {}
|
||||||
|
if not parent_node then
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
for parent_type, child_data in pairs(node_data) do
|
||||||
|
local matches = helpers.split(parent_type, "|", true)
|
||||||
|
|
||||||
|
-- Look if the parent node is one of the matches
|
||||||
|
if vim.tbl_contains(matches, parent_node:type()) then
|
||||||
|
-- For each child_data in the matched parent node
|
||||||
|
for i, data in pairs(child_data) do
|
||||||
|
local index = tonumber(i)
|
||||||
|
assert(index, "Need a valid index")
|
||||||
|
|
||||||
|
local child_node = index == 0 and parent_node or parent_node:named_child(index - 1)
|
||||||
|
|
||||||
|
if not child_node then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not data.match or child_node:type() == data.match then
|
||||||
|
if type(data.extract) == "function" then
|
||||||
|
-- Extract content from it { [type] = { data } }
|
||||||
|
for type, extracted_data in pairs(data.extract(child_node)) do
|
||||||
|
result[type] = extracted_data
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- if not extract function, get the text from the node (required: data.type)
|
||||||
|
result[data.type] = ts_utils.get_node_text(child_node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
local ts_utils = require("nvim-treesitter.ts_utils")
|
|
||||||
|
|
||||||
--- Tries to use the configuration to find all required content nodes from the parent node
|
|
||||||
--- @param parent_node userdata the node found by the locator
|
|
||||||
--- @param node_data table the data from configurations[lang].data
|
|
||||||
return function(parent_node, node_data)
|
|
||||||
local result = {}
|
|
||||||
|
|
||||||
for parent_type, child_data in pairs(node_data) do
|
|
||||||
local matches = vim.split(parent_type, "|", true)
|
|
||||||
|
|
||||||
-- Look if the parent node is one of the matches
|
|
||||||
if vim.tbl_contains(matches, parent_node:type()) then
|
|
||||||
-- For each child_data in the matched parent node
|
|
||||||
for i, data in pairs(child_data) do
|
|
||||||
local child_node
|
|
||||||
|
|
||||||
if tonumber(i) == 0 then
|
|
||||||
child_node = parent_node
|
|
||||||
else
|
|
||||||
child_node = parent_node:named_child(tonumber(i) - 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not child_node then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if child_node:type() == data.match or not data.match then
|
|
||||||
local extract = {}
|
|
||||||
|
|
||||||
if data.extract then
|
|
||||||
-- Extract content from it { [type] = { data } }
|
|
||||||
extract = data.extract(child_node)
|
|
||||||
|
|
||||||
-- All extracted values are created added in result, like so: [data.type] = { extract }
|
|
||||||
if data.type then
|
|
||||||
-- Extract information into a one-dimensional array
|
|
||||||
local one_dimensional_arr = {}
|
|
||||||
|
|
||||||
for _, values in pairs(extract) do
|
|
||||||
table.insert(one_dimensional_arr, values)
|
|
||||||
end
|
|
||||||
|
|
||||||
result[data.type] = one_dimensional_arr
|
|
||||||
else
|
|
||||||
for type, extracted_data in pairs(extract) do
|
|
||||||
result[type] = extracted_data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- if not extract function, get the text from the node (required: data.type)
|
|
||||||
extract = ts_utils.get_node_text(child_node)
|
|
||||||
result[data.type] = extract
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
local ok, ts_utils = pcall(require, "nvim-treesitter.ts_utils")
|
|
||||||
assert(ok, "neogen requires nvim-treesitter to operate :(")
|
|
||||||
|
|
||||||
--- Table of contents:
|
--- Table of contents:
|
||||||
---@toc
|
---@toc
|
||||||
---@text
|
---@text
|
||||||
@@ -22,22 +19,15 @@ assert(ok, "neogen requires nvim-treesitter to operate :(")
|
|||||||
--- - Written in lua (and uses Tree-sitter)
|
--- - Written in lua (and uses Tree-sitter)
|
||||||
---@tag neogen
|
---@tag neogen
|
||||||
---@toc_entry Neogen's purpose
|
---@toc_entry Neogen's purpose
|
||||||
|
|
||||||
-- Requires ===================================================================
|
-- Requires ===================================================================
|
||||||
|
|
||||||
local neogen = {}
|
local neogen = {}
|
||||||
|
|
||||||
|
local conf
|
||||||
|
local config = require("neogen.config")
|
||||||
local helpers = require("neogen.utilities.helpers")
|
local helpers = require("neogen.utilities.helpers")
|
||||||
|
local mark = require("neogen.mark")
|
||||||
local notify = helpers.notify
|
local notify = helpers.notify
|
||||||
|
|
||||||
local cursor = require("neogen.utilities.cursor")
|
|
||||||
|
|
||||||
local default_locator = require("neogen.locators.default")
|
|
||||||
local default_granulator = require("neogen.granulators.default")
|
|
||||||
local default_generator = require("neogen.generators.default")
|
|
||||||
|
|
||||||
local autocmd = require("neogen.utilities.autocmd")
|
|
||||||
|
|
||||||
-- Module definition ==========================================================
|
-- Module definition ==========================================================
|
||||||
|
|
||||||
--- Module setup
|
--- Module setup
|
||||||
@@ -47,20 +37,10 @@ local autocmd = require("neogen.utilities.autocmd")
|
|||||||
---@usage `require('neogen').setup({})` (replace `{}` with your `config` table)
|
---@usage `require('neogen').setup({})` (replace `{}` with your `config` table)
|
||||||
---@toc_entry The setup function
|
---@toc_entry The setup function
|
||||||
neogen.setup = function(opts)
|
neogen.setup = function(opts)
|
||||||
-- Stores the user configuration globally so that we keep his configs when switching languages
|
conf = config.setup(neogen.configuration, opts)
|
||||||
neogen.user_configuration = opts or {}
|
if conf.enabled then
|
||||||
|
|
||||||
neogen.configuration = vim.tbl_deep_extend("keep", neogen.user_configuration, neogen.configuration)
|
|
||||||
|
|
||||||
if neogen.configuration.enabled == true then
|
|
||||||
neogen.generate_command()
|
neogen.generate_command()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Export module
|
|
||||||
_G.neogen = neogen
|
|
||||||
|
|
||||||
-- Force configuring current language again when doing `setup` call.
|
|
||||||
helpers.switch_language()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Neogen Usage
|
--- Neogen Usage
|
||||||
@@ -132,7 +112,7 @@ neogen.configuration = {
|
|||||||
jump_text = "$1",
|
jump_text = "$1",
|
||||||
|
|
||||||
-- Configuration for default languages
|
-- Configuration for default languages
|
||||||
languages = {},
|
languages = {}
|
||||||
}
|
}
|
||||||
--minidoc_afterlines_end
|
--minidoc_afterlines_end
|
||||||
|
|
||||||
@@ -149,128 +129,12 @@ neogen.configuration = {
|
|||||||
--- Currently supported: `func`, `class`, `type`, `file`
|
--- Currently supported: `func`, `class`, `type`, `file`
|
||||||
---@toc_entry Generate annotations
|
---@toc_entry Generate annotations
|
||||||
neogen.generate = function(opts)
|
neogen.generate = function(opts)
|
||||||
opts = opts or {}
|
if not conf.enabled then
|
||||||
opts.type = (opts.type == nil or opts.type == "") and "func" or opts.type -- Default type
|
|
||||||
|
|
||||||
if not neogen.configuration.enabled then
|
|
||||||
notify("Neogen not enabled. Please enable it.", vim.log.levels.WARN)
|
notify("Neogen not enabled. Please enable it.", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if vim.bo.filetype == "" then
|
require("neogen.generator")(vim.bo.filetype, opts and opts.type)
|
||||||
notify("No filetype detected", vim.log.levels.WARN)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local parser = vim.treesitter.get_parser(0, vim.bo.filetype)
|
|
||||||
local tstree = parser:parse()[1]
|
|
||||||
local tree = tstree:root()
|
|
||||||
|
|
||||||
local language = neogen.configuration.languages[vim.bo.filetype]
|
|
||||||
|
|
||||||
if not language then
|
|
||||||
notify("Language " .. vim.bo.filetype .. " not supported.", vim.log.levels.WARN)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
language.locator = language.locator or default_locator
|
|
||||||
language.granulator = language.granulator or default_granulator
|
|
||||||
language.generator = language.generator or default_generator
|
|
||||||
|
|
||||||
if not language.parent[opts.type] or not language.data[opts.type] then
|
|
||||||
notify("Type `" .. opts.type .. "` not supported", vim.log.levels.WARN)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Use the language locator to locate one of the required parent nodes above the cursor
|
|
||||||
local located_parent_node = language.locator({
|
|
||||||
root = tree,
|
|
||||||
current = ts_utils.get_node_at_cursor(0),
|
|
||||||
}, language.parent[opts.type])
|
|
||||||
|
|
||||||
if not located_parent_node then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Use the language granulator to get the required content inside the node found with the locator
|
|
||||||
local data = language.granulator(located_parent_node, language.data[opts.type])
|
|
||||||
|
|
||||||
if data then
|
|
||||||
-- Will try to generate the documentation from a template and the data found from the granulator
|
|
||||||
local to_place, start_column, content = language.generator(
|
|
||||||
located_parent_node,
|
|
||||||
data,
|
|
||||||
language.template,
|
|
||||||
opts.type
|
|
||||||
)
|
|
||||||
|
|
||||||
if #content ~= 0 then
|
|
||||||
cursor.del_extmarks() -- Delete previous extmarks before setting any new ones
|
|
||||||
|
|
||||||
local jump_text = language.jump_text or neogen.configuration.jump_text
|
|
||||||
|
|
||||||
--- Removes jump_text marks and keep the second part of jump_text|other_text if there is one (which is other_text)
|
|
||||||
local delete_marks = function(v)
|
|
||||||
local pattern = jump_text .. "[|%w]+"
|
|
||||||
local matched = string.match(v, pattern)
|
|
||||||
|
|
||||||
if matched then
|
|
||||||
local split = vim.split(matched, "|", true)
|
|
||||||
if #split == 2 and neogen.configuration.input_after_comment == false then
|
|
||||||
return string.gsub(v, jump_text .. "|", "")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return string.gsub(v, jump_text, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
return string.gsub(v, pattern, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
local content_with_marks = vim.deepcopy(content)
|
|
||||||
|
|
||||||
-- delete all jump_text marks
|
|
||||||
content = vim.tbl_map(delete_marks, content)
|
|
||||||
|
|
||||||
-- Append the annotation in required place
|
|
||||||
vim.fn.append(to_place, content)
|
|
||||||
|
|
||||||
-- Place cursor after annotations and start editing
|
|
||||||
-- First and last extmarks are needed to know the range of inserted content
|
|
||||||
if neogen.configuration.input_after_comment == true then
|
|
||||||
-- Creates extmark for the beggining of the content
|
|
||||||
cursor.create(to_place + 1, start_column)
|
|
||||||
-- Creates extmarks for the content
|
|
||||||
for i, value in pairs(content_with_marks) do
|
|
||||||
local start = 0
|
|
||||||
local count = 0
|
|
||||||
while true do
|
|
||||||
start = string.find(value, jump_text, start + 1)
|
|
||||||
if not start then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
cursor.create(to_place + i, start - count * #jump_text)
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create extmark to jump back to current location
|
|
||||||
local pos = vim.api.nvim_win_get_cursor(0)
|
|
||||||
local col = pos[2] + 2
|
|
||||||
|
|
||||||
-- If the line we are in is empty, it will throw an error out of bounds.
|
|
||||||
if vim.api.nvim_get_current_line() == "" then
|
|
||||||
col = pos[2] + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
cursor.create(pos[1], col)
|
|
||||||
|
|
||||||
-- Creates extmark for the end of the content
|
|
||||||
cursor.create(to_place + #content + 1, 0)
|
|
||||||
|
|
||||||
cursor.jump({ first_time = true })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Expose more API ============================================================
|
-- Expose more API ============================================================
|
||||||
@@ -282,15 +146,12 @@ neogen.match_commands = helpers.match_commands
|
|||||||
---@param filetype string
|
---@param filetype string
|
||||||
---@return neogen.TemplateConfig|nil
|
---@return neogen.TemplateConfig|nil
|
||||||
neogen.get_template = function(filetype)
|
neogen.get_template = function(filetype)
|
||||||
if not neogen.configuration.languages[filetype] then
|
local template
|
||||||
return
|
local ft_conf = conf.languages[filetype]
|
||||||
|
if ft_conf and ft_conf.template then
|
||||||
|
template = ft_conf.template
|
||||||
end
|
end
|
||||||
|
return template
|
||||||
if not neogen.configuration.languages[filetype].template then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
return neogen.configuration.languages[filetype].template
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Required for use with completion engine =====================================
|
-- Required for use with completion engine =====================================
|
||||||
@@ -298,24 +159,20 @@ end
|
|||||||
--- Jumps to the next cursor template position
|
--- Jumps to the next cursor template position
|
||||||
---@private
|
---@private
|
||||||
function neogen.jump_next()
|
function neogen.jump_next()
|
||||||
if neogen.jumpable() then
|
mark:jump()
|
||||||
cursor.jump()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Jumps to the next cursor template position
|
--- Jumps to the next cursor template position
|
||||||
---@private
|
---@private
|
||||||
function neogen.jump_prev()
|
function neogen.jump_prev()
|
||||||
if cursor.jumpable(-1) then
|
mark:jump(true)
|
||||||
cursor.jump_prev()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Checks if the cursor can jump backwards or forwards
|
--- Checks if the cursor can jump backwards or forwards
|
||||||
--- @param reverse number? if `-1`, will try to see if can be jumped backwards
|
--- @param reverse number? if `-1` or true, will try to see if can be jumped backwards
|
||||||
---@private
|
---@private
|
||||||
function neogen.jumpable(reverse)
|
function neogen.jumpable(reverse)
|
||||||
return cursor.jumpable(reverse)
|
return mark:jumpable(reverse == -1 or reverse == true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Command and autocommands ====================================================
|
-- Command and autocommands ====================================================
|
||||||
@@ -323,21 +180,10 @@ end
|
|||||||
--- Generates the `:Neogen` command, which calls `neogen.generate()`
|
--- Generates the `:Neogen` command, which calls `neogen.generate()`
|
||||||
---@private
|
---@private
|
||||||
function neogen.generate_command()
|
function neogen.generate_command()
|
||||||
vim.api.nvim_command(
|
|
||||||
'command! -nargs=? -complete=customlist,v:lua.neogen.match_commands -range -bar Neogen lua require("neogen").generate({ type = <q-args>})'
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
autocmd.subscribe("BufEnter", function()
|
|
||||||
helpers.switch_language()
|
|
||||||
end)
|
|
||||||
|
|
||||||
vim.cmd([[
|
vim.cmd([[
|
||||||
augroup ___neogen___
|
command! -nargs=? -complete=customlist,v:lua.require'neogen'.match_commands -range -bar Neogen lua require("neogen").generate({ type = <q-args>})
|
||||||
autocmd!
|
|
||||||
autocmd BufEnter * lua require'neogen.utilities.autocmd'.emit('BufEnter')
|
|
||||||
augroup END
|
|
||||||
]])
|
]])
|
||||||
|
end
|
||||||
|
|
||||||
--- Contribute to Neogen
|
--- Contribute to Neogen
|
||||||
---
|
---
|
||||||
|
|||||||
82
lua/neogen/mark.lua
Normal file
82
lua/neogen/mark.lua
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
local mark = {}
|
||||||
|
|
||||||
|
local api = vim.api
|
||||||
|
local ns = api.nvim_create_namespace("neogen")
|
||||||
|
|
||||||
|
mark.start = function(self)
|
||||||
|
if self.started then
|
||||||
|
mark:stop()
|
||||||
|
end
|
||||||
|
self.bufnr = api.nvim_get_current_buf()
|
||||||
|
self.winid = api.nvim_get_current_win()
|
||||||
|
local pos = api.nvim_win_get_cursor(self.winid)
|
||||||
|
local row, col = unpack(pos)
|
||||||
|
self.last_jump = api.nvim_buf_set_extmark(self.bufnr, ns, row - 1, col, {})
|
||||||
|
self.index = 0
|
||||||
|
self.ids = {}
|
||||||
|
self.started = true
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.get_mark = function(self, i)
|
||||||
|
local id = self.ids[i]
|
||||||
|
return api.nvim_buf_get_extmark_by_id(self.bufnr, ns, id, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.add_mark = function(self, pos)
|
||||||
|
local line, col = unpack(pos)
|
||||||
|
local id = api.nvim_buf_set_extmark(self.bufnr, ns, line, col, {})
|
||||||
|
table.insert(mark.ids, id)
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.mark_len = function(self)
|
||||||
|
return #self.ids
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.jumpable = function(self, reverse)
|
||||||
|
local ret = true
|
||||||
|
if reverse then
|
||||||
|
ret = self.index ~= 1
|
||||||
|
else
|
||||||
|
ret = #self.ids ~= self.index
|
||||||
|
end
|
||||||
|
if ret then
|
||||||
|
ret = self.bufnr == api.nvim_get_current_buf() and self.winid == api.nvim_get_current_win()
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.jump = function(self, reverse)
|
||||||
|
if self.index == 0 or mark:jumpable(reverse) then
|
||||||
|
self.index = reverse and self.index - 1 or self.index + 1
|
||||||
|
local line, row = unpack(self:get_mark(self.index))
|
||||||
|
api.nvim_win_set_cursor(self.winid, {line + 1, row})
|
||||||
|
else
|
||||||
|
self:stop(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mark.stop = function(self, should_jump)
|
||||||
|
local bufnr = self.bufnr
|
||||||
|
if bufnr and bufnr > 0 and api.nvim_buf_is_valid(bufnr) then
|
||||||
|
for _, id in ipairs(self.ids) do
|
||||||
|
api.nvim_buf_del_extmark(bufnr, ns, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local winid = self.winid
|
||||||
|
if should_jump and winid and winid > 0 and api.nvim_win_is_valid(winid) then
|
||||||
|
local pos = api.nvim_buf_get_extmark_by_id(self.bufnr, ns, self.last_jump, {})
|
||||||
|
local line, col = unpack(pos)
|
||||||
|
api.nvim_win_set_cursor(winid, {line + 1, col})
|
||||||
|
end
|
||||||
|
api.nvim_buf_del_extmark(bufnr, ns, self.last_jump)
|
||||||
|
end
|
||||||
|
self.bufnr = nil
|
||||||
|
self.winid = nil
|
||||||
|
self.index = nil
|
||||||
|
self.last_jump = nil
|
||||||
|
self.started = false
|
||||||
|
self.ids = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return mark
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
cursor = {}
|
|
||||||
|
|
||||||
local neogen_ns = vim.api.nvim_create_namespace("neogen")
|
|
||||||
local neogen_virt_text_ns = vim.api.nvim_create_namespace("neogen_virt_text")
|
|
||||||
local current_position = 1
|
|
||||||
|
|
||||||
--- Wrapper around set_extmark with 1-based numbering for `line` and `col`, and returns the id of the created extmark
|
|
||||||
--- @param line number
|
|
||||||
--- @param col number
|
|
||||||
--- @return number
|
|
||||||
cursor.create = function(line, col)
|
|
||||||
current_position = 1
|
|
||||||
local new_col = col == 0 and 0 or col - 1
|
|
||||||
return vim.api.nvim_buf_set_extmark(0, neogen_ns, line - 1, new_col, {})
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Find next created extmark and goes to it.
|
|
||||||
--- It removes the extmark afterwards.
|
|
||||||
--- First jumpable extmark is the one after the extmarks responsible of start/end of annotation
|
|
||||||
cursor.go_next_extmark = function()
|
|
||||||
local extm_list = vim.api.nvim_buf_get_extmarks(0, neogen_ns, 0, -1, {})
|
|
||||||
local position = current_position + 1
|
|
||||||
table.sort(extm_list, function(a, b)
|
|
||||||
return a[1] < b[1]
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #extm_list ~= 2 then
|
|
||||||
local pos = { extm_list[position][2] + 1, extm_list[position][3] }
|
|
||||||
vim.api.nvim_win_set_cursor(0, pos)
|
|
||||||
current_position = current_position + 1
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Goes to next extmark and start insert mode.
|
|
||||||
--- If `opts.first_time` is supplied, will try to go to normal mode before going to extmark
|
|
||||||
--- @param opts table
|
|
||||||
cursor.jump = function(opts)
|
|
||||||
opts = opts or {}
|
|
||||||
|
|
||||||
-- This is weird, the first time nvim goes to insert is not the same as when i'm already on insert mode
|
|
||||||
-- that's why i put a first_time flag
|
|
||||||
if opts.first_time then
|
|
||||||
vim.api.nvim_command("startinsert")
|
|
||||||
end
|
|
||||||
|
|
||||||
if cursor.go_next_extmark() then
|
|
||||||
vim.api.nvim_command("startinsert")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
cursor.jump_prev = function()
|
|
||||||
local marks = vim.api.nvim_buf_get_extmarks(0, neogen_ns, 0, -1, {})
|
|
||||||
table.sort(marks, function(a, b)
|
|
||||||
return a[1] < b[1]
|
|
||||||
end)
|
|
||||||
|
|
||||||
if #marks == 2 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local position = current_position - 1
|
|
||||||
local pos = { marks[position][2] + 1, marks[position][3] }
|
|
||||||
vim.api.nvim_win_set_cursor(0, pos)
|
|
||||||
current_position = current_position - 1
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Delete all active extmarks
|
|
||||||
cursor.del_extmarks = function()
|
|
||||||
local extmarks = vim.api.nvim_buf_get_extmarks(0, neogen_ns, 0, -1, {})
|
|
||||||
for _, v in pairs(extmarks) do
|
|
||||||
vim.api.nvim_buf_del_extmark(0, neogen_ns, v[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Checks if there are still possible jump positions to perform
|
|
||||||
--- Verifies if the cursor is in the last annotated part
|
|
||||||
cursor.jumpable = function(reverse)
|
|
||||||
local extm_list = vim.api.nvim_buf_get_extmarks(0, neogen_ns, 0, -1, {})
|
|
||||||
if #extm_list == 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
|
||||||
if cursor[1] > extm_list[#extm_list][2] or cursor[1] < extm_list[1][2] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- We arrive at the end, we can't jump anymore
|
|
||||||
if current_position == #extm_list then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if reverse == -1 then
|
|
||||||
-- Check first boundaries
|
|
||||||
if current_position == 2 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #extm_list > 2 then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return cursor
|
|
||||||
@@ -16,7 +16,7 @@ return {
|
|||||||
local get_text = function(node)
|
local get_text = function(node)
|
||||||
return ts_utils.get_node_text(node)[1]
|
return ts_utils.get_node_text(node)[1]
|
||||||
end
|
end
|
||||||
if opts.type == true then
|
if opts.type then
|
||||||
result[k] = vim.tbl_map(get_type, v)
|
result[k] = vim.tbl_map(get_type, v)
|
||||||
else
|
else
|
||||||
result[k] = vim.tbl_map(get_text, v)
|
result[k] = vim.tbl_map(get_text, v)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
local config = require("neogen.config")
|
||||||
return {
|
return {
|
||||||
notify = function(msg, log_level)
|
notify = function(msg, log_level)
|
||||||
vim.notify(msg, log_level, {title = "Neogen"})
|
vim.notify(msg, log_level, {title = "Neogen"})
|
||||||
@@ -10,7 +11,7 @@ return {
|
|||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local language = neogen.configuration.languages[vim.bo.filetype]
|
local language = config.get().languages[vim.bo.filetype]
|
||||||
|
|
||||||
if not language or not language.parent then
|
if not language or not language.parent then
|
||||||
return {}
|
return {}
|
||||||
@@ -18,19 +19,8 @@ return {
|
|||||||
|
|
||||||
return vim.tbl_keys(language.parent)
|
return vim.tbl_keys(language.parent)
|
||||||
end,
|
end,
|
||||||
|
split = function(s, sep, plain)
|
||||||
switch_language = function()
|
return vim.fn.has("nvim-0.6") == 1 and vim.split(s, sep, {plain = plain}) or
|
||||||
local filetype = vim.bo.filetype
|
vim.split(s, sep, plain)
|
||||||
local ok, ft_configuration = pcall(require, "neogen.configurations." .. filetype)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
neogen.configuration.languages[filetype] = vim.tbl_deep_extend(
|
|
||||||
"keep",
|
|
||||||
neogen.user_configuration.languages and neogen.user_configuration.languages[filetype] or {},
|
|
||||||
ft_configuration
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
|
local helpers = require("neogen.utilities.helpers")
|
||||||
return {
|
return {
|
||||||
--- Get a list of child nodes that match the provided node name
|
--- Get a list of child nodes that match the provided node name
|
||||||
--- @param _ any
|
--- @param _ any
|
||||||
--- @param parent userdata the parent's node
|
--- @param parent userdata the parent's node
|
||||||
--- @param node_name string|nil the node type to search for (if multiple childrens, separate each one with "|")
|
--- @param node_type string|nil the node type to search for (if multiple childrens, separate each one with "|")
|
||||||
--- @return table a table of nodes that matched the name
|
--- @return table a table of nodes that matched the name
|
||||||
matching_child_nodes = function(_, parent, node_name)
|
matching_child_nodes = function(_, parent, node_type)
|
||||||
local results = {}
|
local results = {}
|
||||||
-- Return all nodes if there is no node name
|
-- Return all nodes if there is no node name
|
||||||
if node_name == nil then
|
if not node_type then
|
||||||
for child in parent:iter_children() do
|
for child in parent:iter_children() do
|
||||||
if child:named() then
|
if child:named() then
|
||||||
table.insert(results, child)
|
table.insert(results, child)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local split = vim.split(node_name, "|", true)
|
local types = helpers.split(node_type, "|", true)
|
||||||
for child in parent:iter_children() do
|
for child in parent:iter_children() do
|
||||||
if vim.tbl_contains(split, child:type()) then
|
if vim.tbl_contains(types, child:type()) then
|
||||||
table.insert(results, child)
|
table.insert(results, child)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -45,8 +46,7 @@ return {
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
local found = self:recursive_find(child, node_name, { results = results })
|
self:recursive_find(child, node_name, {results = results})
|
||||||
vim.tbl_deep_extend("keep", results, found)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
@@ -63,21 +63,17 @@ return {
|
|||||||
result = result or {}
|
result = result or {}
|
||||||
|
|
||||||
for _, subtree in pairs(tree) do
|
for _, subtree in pairs(tree) do
|
||||||
if subtree.retrieve and not vim.tbl_contains({ "all", "first" }, subtree.retrieve) then
|
assert(not subtree.retrieve or vim.tbl_contains({"all", "first"}, subtree.retrieve),
|
||||||
assert(false, "Supported nodes matching: all|first")
|
"Supported nodes matching: all|first")
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Match all child nodes of the parent node
|
-- Match all child nodes of the parent node
|
||||||
local matched = self:matching_child_nodes(parent, subtree.node_type)
|
local matched = self:matching_child_nodes(parent, subtree.node_type)
|
||||||
|
|
||||||
-- Only keep the node with custom position
|
-- Only keep the node with custom position
|
||||||
if subtree.retrieve == nil then
|
if not subtree.retrieve then
|
||||||
if type(subtree.position) == "number" then
|
assert(type(subtree.position) == "number",
|
||||||
|
"please require position if retrieve is nil")
|
||||||
matched = {matched[subtree.position]}
|
matched = {matched[subtree.position]}
|
||||||
else
|
|
||||||
assert(false, "please require position if retrieve is nil")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if subtree.recursive then
|
if subtree.recursive then
|
||||||
@@ -86,18 +82,17 @@ return {
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _, child in pairs(matched) do
|
for _, child in pairs(matched) do
|
||||||
if subtree.extract == true then
|
if subtree.extract then
|
||||||
local name = subtree.as and subtree.as or (subtree.node_type or "_")
|
local name = subtree.as and subtree.as or (subtree.node_type or "_")
|
||||||
if result[name] == nil then
|
if not result[name] then
|
||||||
result[name] = {}
|
result[name] = {}
|
||||||
end
|
end
|
||||||
table.insert(result[name], child)
|
table.insert(result[name], child)
|
||||||
else
|
else
|
||||||
local nodes = self:matching_nodes_from(child, subtree.subtree, result)
|
self:matching_nodes_from(child, subtree.subtree, result)
|
||||||
result = vim.tbl_deep_extend("keep", result, nodes)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end,
|
end
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ if _G.MiniDoc == nil then
|
|||||||
minidoc.setup()
|
minidoc.setup()
|
||||||
end
|
end
|
||||||
|
|
||||||
minidoc.generate({ "lua/neogen/init.lua", "lua/neogen/utilities/template.lua" }, nil, nil)
|
minidoc.generate({ "lua/neogen/init.lua", "lua/neogen/template.lua" }, nil, nil)
|
||||||
|
|||||||
Reference in New Issue
Block a user