From d585d7f63cfd7ef8c0b8e4bf56bd98326771dd76 Mon Sep 17 00:00:00 2001 From: romamihalich Date: Tue, 6 Jan 2026 18:55:21 +0300 Subject: [PATCH 1/4] C# improvements 1. Fix "return" insertion for: - method returning void (not insert if void) - constructor (not insert at all) - indexer (change logic from search for return statement to checking if getter is present, because get might not contain return statement, but instead "=>", also might not contain body at all in interfaces) 2. Fix parameter generation when attribute is attached to it 3. Added support for primary constructors 4. Added support for records, record structs, structs (same logic as classes) 5. Change parameters and type parameters order for xmldoc (type parameters should go first) --- lua/neogen/configurations/cs.lua | 132 ++++++++++++++++++++----------- lua/neogen/templates/xmldoc.lua | 2 +- lua/neogen/utilities/nodes.lua | 5 +- 3 files changed, 92 insertions(+), 47 deletions(-) diff --git a/lua/neogen/configurations/cs.lua b/lua/neogen/configurations/cs.lua index 59709df5..b78b5490 100644 --- a/lua/neogen/configurations/cs.lua +++ b/lua/neogen/configurations/cs.lua @@ -3,6 +3,50 @@ local nodes_utils = require("neogen.utilities.nodes") local template = require("neogen.template") local i = require("neogen.types.template").item +local is_void = function(return_statement) + if not return_statement then + return + end + for _, value in ipairs(return_statement) do + if value == "void" then + return true + end + end + return false +end + +local get_parameters_tree = function() + return { + retrieve = "first", + node_type = "parameter_list", + subtree = { + { + retrieve = "all", + node_type = "parameter", + subtree = { + { position = -1, extract = true, as = i.Parameter }, + }, + }, + }, + } +end + +local get_type_parameters_tree = function() + return { + retrieve = "first", + node_type = "type_parameter_list", + subtree = { + { + retrieve = "all", + node_type = "type_parameter", + subtree = { + { position = 1, extract = true, as = i.Tparam }, + }, + }, + }, + } +end + return { parent = { func = { @@ -12,7 +56,7 @@ return { "delegate_declaration", "conversion_operator_declaration", }, - class = { "class_declaration", "interface_declaration" }, + class = { "interface_declaration", "class_declaration", "record_declaration", "struct_declaration" }, type = { "field_declaration", "property_declaration", "event_field_declaration", "indexer_declaration" }, }, data = { @@ -21,42 +65,23 @@ return { ["0"] = { extract = function(node) local tree = { + get_type_parameters_tree(), + get_parameters_tree(), { - retrieve = "first", - node_type = "parameter_list", - subtree = { - { - retrieve = "all", - node_type = "parameter", - subtree = { - { position = 2, extract = true, as = i.Parameter }, - }, - }, - }, - }, - { - retrieve = "first", - node_type = "type_parameter_list", - subtree = { - { - retrieve = "all", - node_type = "type_parameter", - subtree = { - { position = 1, extract = true, as = i.Tparam }, - }, - }, - }, - }, - { - position = 1, + retrieve = "all", extract = true, as = i.Return, }, } local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) - if res.return_statement[1] == "void" then + + local has_no_return = node:type() == "constructor_declaration" + or is_void(res.return_statement); + if has_no_return then res.return_statement = nil + else + res.return_statement = { res.return_statement[1] } end res.identifier = res["_"] return res @@ -65,23 +90,25 @@ return { }, }, class = { - ["class_declaration|interface_declaration"] = { + ["interface_declaration"] = { ["0"] = { extract = function(node) local tree = { - { - retrieve = "first", - node_type = "type_parameter_list", - subtree = { - { - retrieve = "all", - node_type = "type_parameter", - subtree = { - { position = 1, extract = true, as = i.Tparam }, - }, - }, - }, - }, + get_type_parameters_tree() + } + local nodes = nodes_utils:matching_nodes_from(node, tree) + local res = extractors:extract_from_matched(nodes) + res.identifier = res["_"] + return res + end, + }, + }, + ["class_declaration|record_declaration|struct_declaration"] = { + ["0"] = { + extract = function(node) + local tree = { + get_type_parameters_tree(), + get_parameters_tree(), } local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) @@ -127,9 +154,8 @@ return { subtree = { { retrieve = "all", - node_type = "return_statement", + node_type = "accessor_declaration", as = i.Return, - recursive = true, extract = true, }, }, @@ -137,6 +163,22 @@ return { } local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) + + local has_getter = false; + if res.return_statement then + for _, value in ipairs(res.return_statement) do + if vim.startswith(value, "get") then + has_getter = true + break + end + end + end + if has_getter then + res.return_statement = { "get" } + else + res.return_statement = nil + end + return res end, }, diff --git a/lua/neogen/templates/xmldoc.lua b/lua/neogen/templates/xmldoc.lua index 41f45657..3fdd52f0 100644 --- a/lua/neogen/templates/xmldoc.lua +++ b/lua/neogen/templates/xmldoc.lua @@ -8,7 +8,7 @@ return { { nil, "/// ", {} }, { nil, "/// $1", {} }, { nil, "/// ", {} }, - { i.Parameter, '/// $1', { type = { "func", "type" } } }, { i.Tparam, '/// $1', { type = { "func", "class" } } }, + { i.Parameter, '/// $1', { type = { "func", "type", "class" } } }, { i.Return, "/// $1", { type = { "func", "type" } } }, } diff --git a/lua/neogen/utilities/nodes.lua b/lua/neogen/utilities/nodes.lua index 97dd7fc3..909b51fa 100644 --- a/lua/neogen/utilities/nodes.lua +++ b/lua/neogen/utilities/nodes.lua @@ -56,7 +56,7 @@ return { --- @param parent TSNode the parent node --- @param tree table a nested table : { retrieve = "all|first", node_type = node_name, subtree = tree, recursive = true } --- If you want to extract the node, do not specify the subtree and instead: extract = true - --- Optional: you can specify position = number instead of retrieve, and it will fetch the child node at position number + --- Optional: you can specify position = number instead of retrieve, and it will fetch the child node at position number, if position == -1, then last child is taken --- @param result? table the table of results --- @return table result a table of k,v where k are node_types and v all matched nodes matching_nodes_from = function(self, parent, tree, result) @@ -74,6 +74,9 @@ return { -- Only keep the node with custom position if not subtree.retrieve then assert(type(subtree.position) == "number", "please require position if retrieve is nil") + if subtree.position == -1 then + subtree.position = #matched + end matched = { matched[subtree.position] } end From 0e71c27753192e48e6877aacd4c52ef8c62a5691 Mon Sep 17 00:00:00 2001 From: romamihalich Date: Tue, 6 Jan 2026 19:39:49 +0300 Subject: [PATCH 2/4] fix state corruption bug --- lua/neogen/utilities/nodes.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/neogen/utilities/nodes.lua b/lua/neogen/utilities/nodes.lua index 909b51fa..6118849a 100644 --- a/lua/neogen/utilities/nodes.lua +++ b/lua/neogen/utilities/nodes.lua @@ -74,10 +74,11 @@ return { -- Only keep the node with custom position if not subtree.retrieve then assert(type(subtree.position) == "number", "please require position if retrieve is nil") - if subtree.position == -1 then - subtree.position = #matched + local position = subtree.position + if position == -1 then + position = #matched end - matched = { matched[subtree.position] } + matched = { matched[position] } end if subtree.recursive then From 4133b88e4f84107be346af4f9c687452524bcabb Mon Sep 17 00:00:00 2001 From: romamihalich Date: Tue, 6 Jan 2026 20:16:11 +0300 Subject: [PATCH 3/4] run stylua on file "lua/neogen/configurations/cs.lua" --- lua/neogen/configurations/cs.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lua/neogen/configurations/cs.lua b/lua/neogen/configurations/cs.lua index b78b5490..cfd7ca58 100644 --- a/lua/neogen/configurations/cs.lua +++ b/lua/neogen/configurations/cs.lua @@ -76,8 +76,7 @@ return { local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) - local has_no_return = node:type() == "constructor_declaration" - or is_void(res.return_statement); + local has_no_return = node:type() == "constructor_declaration" or is_void(res.return_statement) if has_no_return then res.return_statement = nil else @@ -94,7 +93,7 @@ return { ["0"] = { extract = function(node) local tree = { - get_type_parameters_tree() + get_type_parameters_tree(), } local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) @@ -164,7 +163,7 @@ return { local nodes = nodes_utils:matching_nodes_from(node, tree) local res = extractors:extract_from_matched(nodes) - local has_getter = false; + local has_getter = false if res.return_statement then for _, value in ipairs(res.return_statement) do if vim.startswith(value, "get") then From 08df93cba32425420364aa8511504cb921cde826 Mon Sep 17 00:00:00 2001 From: romamihalich Date: Mon, 26 Jan 2026 18:23:19 +0300 Subject: [PATCH 4/4] Add enums for C# --- lua/neogen/configurations/cs.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lua/neogen/configurations/cs.lua b/lua/neogen/configurations/cs.lua index cfd7ca58..34893a86 100644 --- a/lua/neogen/configurations/cs.lua +++ b/lua/neogen/configurations/cs.lua @@ -56,8 +56,8 @@ return { "delegate_declaration", "conversion_operator_declaration", }, - class = { "interface_declaration", "class_declaration", "record_declaration", "struct_declaration" }, - type = { "field_declaration", "property_declaration", "event_field_declaration", "indexer_declaration" }, + class = { "interface_declaration", "class_declaration", "record_declaration", "struct_declaration", "enum_declaration" }, + type = { "field_declaration", "property_declaration", "event_field_declaration", "indexer_declaration", "enum_member_declaration" }, }, data = { func = { @@ -116,9 +116,16 @@ return { end, }, }, + ["enum_declaration"] = { + ["0"] = { + extract = function() + return {} + end, + } + }, }, type = { - ["field_declaration|property_declaration|event_field_declaration"] = { + ["field_declaration|property_declaration|event_field_declaration|enum_member_declaration"] = { ["0"] = { extract = function() return {}