From e115314c3b5c5068755bf7a1336769aa19f8420a Mon Sep 17 00:00:00 2001 From: Sasha Gurevich Date: Tue, 14 Jul 2026 23:37:30 +0300 Subject: [PATCH 1/3] feat(__get_outer_namespaces): added method stub and tests --- lua/hopcsharp/hop/providers/utils.lua | 6 ++++++ test/hop/providers/utils_spec.lua | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lua/hopcsharp/hop/providers/utils.lua b/lua/hopcsharp/hop/providers/utils.lua index 1d05d01..4526403 100644 --- a/lua/hopcsharp/hop/providers/utils.lua +++ b/lua/hopcsharp/hop/providers/utils.lua @@ -123,4 +123,10 @@ M.__get_node_modifiers = function(node) return modifiers end +---@param namespace string Namespace name +---@return string[] List of outer namespaces +M.__get_outer_namespaces = function(namespace) + -- TODO +end + return M diff --git a/test/hop/providers/utils_spec.lua b/test/hop/providers/utils_spec.lua index 307689b..e801065 100644 --- a/test/hop/providers/utils_spec.lua +++ b/test/hop/providers/utils_spec.lua @@ -137,4 +137,27 @@ describe('hop.providers.utils', function() local modifiers = utils.__get_node_modifiers(vim.treesitter.get_node()) assert(#modifiers == 0) end) + + it('__get_outer_namespaces - outmost namespace', function() + local namespaces = utils.__get_outer_namespaces('NoNesting') + assert(#namespaces == 0) + end) + + it('__get_outer_namespaces - happy', function() + local namespaces = utils.__get_outer_namespaces('This.Is.My.Namespace') + assert(#namespaces == 3) + assert(namespaces[1] == 'This') + assert(namespaces[2] == 'This.Is') + assert(namespaces[3] == 'This.Is.My') + end) + + it('__get_outer_namespaces - nil', function() + local namespaces = utils.__get_outer_namespaces(nil) + assert(#namespaces == 0) + end) + + it('__get_outer_namespaces - empty', function() + local namespaces = utils.__get_outer_namespaces('') + assert(#namespaces == 0) + end) end) From 012365d881b430a4f2fb27c8eba54ce72e40fb21 Mon Sep 17 00:00:00 2001 From: Sasha Gurevich Date: Thu, 16 Jul 2026 14:36:45 +0300 Subject: [PATCH 2/3] feat: added get outer namespace method --- lua/hopcsharp/hop/providers/reference.lua | 2 +- lua/hopcsharp/hop/providers/utils.lua | 27 ++++++++++++++++++++++- test/hop/providers/utils_spec.lua | 13 +++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lua/hopcsharp/hop/providers/reference.lua b/lua/hopcsharp/hop/providers/reference.lua index 07cd8ae..c1b79b6 100644 --- a/lua/hopcsharp/hop/providers/reference.lua +++ b/lua/hopcsharp/hop/providers/reference.lua @@ -63,7 +63,7 @@ M.__by_name_and_current_namespace = function(current_word, node) end local db = database.__get_db() - return db:eval(query.get_reference_by_name_and_current_namespace(name, namespace)) + return db:eval(query.get_reference_by_name_and_current_namespace(name, namespace)), dbutils.get_reference_type_name end, } end diff --git a/lua/hopcsharp/hop/providers/utils.lua b/lua/hopcsharp/hop/providers/utils.lua index 4526403..39b3d05 100644 --- a/lua/hopcsharp/hop/providers/utils.lua +++ b/lua/hopcsharp/hop/providers/utils.lua @@ -126,7 +126,32 @@ end ---@param namespace string Namespace name ---@return string[] List of outer namespaces M.__get_outer_namespaces = function(namespace) - -- TODO + local namespaces = {} + + if namespace == nil then + return namespaces + end + + if namespace == '' then + return namespaces + end + + local parts = {} + for part in string.gmatch(namespace, "([^%.]+)") do + table.insert(parts, part) + end + + local i = 1 + while i < #parts do + local n = {} + for j = 1, i, 1 do + table.insert(n, parts[j]) + end + i = i + 1 + table.insert(namespaces, table.concat(n, '.')) + end + + return namespaces end return M diff --git a/test/hop/providers/utils_spec.lua b/test/hop/providers/utils_spec.lua index e801065..a1164ef 100644 --- a/test/hop/providers/utils_spec.lua +++ b/test/hop/providers/utils_spec.lua @@ -143,6 +143,19 @@ describe('hop.providers.utils', function() assert(#namespaces == 0) end) + it('__get_outer_namespaces - one outer namespace', function() + local namespaces = utils.__get_outer_namespaces('One.Two') + assert(#namespaces == 1) + assert(namespaces[1] == 'One') + end) + + it('__get_outer_namespaces - two outer namespace', function() + local namespaces = utils.__get_outer_namespaces('One.Two.Three') + assert(#namespaces == 2) + assert(namespaces[1] == 'One') + assert(namespaces[2] == 'One.Two') + end) + it('__get_outer_namespaces - happy', function() local namespaces = utils.__get_outer_namespaces('This.Is.My.Namespace') assert(#namespaces == 3) From baef31ca57326c9a8af02a4425a03bf36026731d Mon Sep 17 00:00:00 2001 From: Sasha Gurevich Date: Thu, 16 Jul 2026 15:31:40 +0300 Subject: [PATCH 3/3] feat: implmented nested namespace lookup --- lua/hopcsharp/database/query.lua | 10 ++++++++-- lua/hopcsharp/hop/providers/definition.lua | 7 +++++++ lua/hopcsharp/hop/providers/reference.lua | 3 ++- lua/hopcsharp/hop/providers/utils.lua | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lua/hopcsharp/database/query.lua b/lua/hopcsharp/database/query.lua index ff10d8b..e448e72 100644 --- a/lua/hopcsharp/database/query.lua +++ b/lua/hopcsharp/database/query.lua @@ -273,20 +273,26 @@ M.get_reference_by_name_and_current_namespace = function(name, namespaces) WHERE (r.name = '%s' OR r.name GLOB '%s' OR r.name || 'Attribute' = '%s') -- or we have using with that namespace -- or we are in the same namespace - AND ((u.namespace IN (%s)) OR (n.name IN (%s))) + -- or there are references in a nested namespaces + AND ((u.namespace IN (%s)) OR (n.name IN (%s)) OR (n.name GLOB %s)) ORDER BY r.name ASC, n.name ASC, f.path ASC ]] + -- TODO check if GLOB didn't introduce table scan + -- TODO cover in tests + -- TODO comment and explain why we are doing it + local namespace_glob = "'" .. namespaces[1] .. "*'" + for i, namespace in ipairs(namespaces) do namespaces[i] = '"' .. namespace .. '"' end local namespace_string = table.concat(namespaces, ',') - return string.format(query, name, name .. '<*>', name, namespace_string, namespace_string) + return string.format(query, name, name .. '<*>', name, namespace_string, namespace_string, namespace_glob) end M.get_reference_by_name_and_type = function(name, type) diff --git a/lua/hopcsharp/hop/providers/definition.lua b/lua/hopcsharp/hop/providers/definition.lua index 6f2b5d3..955f011 100644 --- a/lua/hopcsharp/hop/providers/definition.lua +++ b/lua/hopcsharp/hop/providers/definition.lua @@ -91,6 +91,13 @@ M.__by_name_type_and_current_namespace = function(current_word, node) table.insert(namespace, vim.treesitter.get_node_text(nn, 0, nil)) end + -- TODO cover in tests to make sure it is needed + -- insert outer namespace that are not included by usings + -- because namespace has access to all types defined in outer namespaces + for _, n in ipairs(utils.__get_outer_namespaces(namespace[1] or '')) do + table.insert(namespace, n) + end + local db = database.__get_db() return db:eval(query.get_definition_by_name_type_and_namespace(name, node_type, namespace)) end, diff --git a/lua/hopcsharp/hop/providers/reference.lua b/lua/hopcsharp/hop/providers/reference.lua index c1b79b6..8009408 100644 --- a/lua/hopcsharp/hop/providers/reference.lua +++ b/lua/hopcsharp/hop/providers/reference.lua @@ -63,7 +63,8 @@ M.__by_name_and_current_namespace = function(current_word, node) end local db = database.__get_db() - return db:eval(query.get_reference_by_name_and_current_namespace(name, namespace)), dbutils.get_reference_type_name + return db:eval(query.get_reference_by_name_and_current_namespace(name, namespace)), + dbutils.get_reference_type_name end, } end diff --git a/lua/hopcsharp/hop/providers/utils.lua b/lua/hopcsharp/hop/providers/utils.lua index 39b3d05..a4b8407 100644 --- a/lua/hopcsharp/hop/providers/utils.lua +++ b/lua/hopcsharp/hop/providers/utils.lua @@ -137,7 +137,7 @@ M.__get_outer_namespaces = function(namespace) end local parts = {} - for part in string.gmatch(namespace, "([^%.]+)") do + for part in string.gmatch(namespace, '([^%.]+)') do table.insert(parts, part) end