diff --git a/conductor/tracks/ts_cpp_tree_sitter_20260308/plan.md b/conductor/tracks/ts_cpp_tree_sitter_20260308/plan.md index 69a3ead..efc152f 100644 --- a/conductor/tracks/ts_cpp_tree_sitter_20260308/plan.md +++ b/conductor/tracks/ts_cpp_tree_sitter_20260308/plan.md @@ -62,7 +62,7 @@ Focus: Implement definitions, signatures, and update tools ## Phase 6: Robust Testing with gencpp Focus: Verify against real-world C++ components -- [ ] Task 6.1: Define test corpus from gencpp samples +- [~] Task 6.1: Define test corpus from gencpp samples - WHERE: tests/assets/gencpp_samples/ - WHAT: Collect complex C++ headers and implementations (templates, nested namespaces, multi-line macros) - HOW: Copy selected files from gencpp repo into test assets diff --git a/config.toml b/config.toml index 0867c44..c104c4e 100644 --- a/config.toml +++ b/config.toml @@ -23,7 +23,7 @@ separate_tool_calls_panel = false bg_shader_enabled = false crt_filter_enabled = false separate_task_dag = false -separate_usage_analytics = true +separate_usage_analytics = false separate_tier1 = false separate_tier2 = false separate_tier3 = false @@ -36,7 +36,7 @@ separate_external_tools = false "AI Settings" = true "MMA Dashboard" = true "Task DAG" = false -"Usage Analytics" = true +"Usage Analytics" = false "Tier 1" = false "Tier 2" = false "Tier 3" = false diff --git a/manualslop_layout.ini b/manualslop_layout.ini index c71b975..2b6c409 100644 --- a/manualslop_layout.ini +++ b/manualslop_layout.ini @@ -44,13 +44,13 @@ Collapsed=0 DockId=0x00000005,0 [Window][Message] -Pos=1756,543 +Pos=475,163 Size=327,652 Collapsed=0 [Window][Response] -Pos=1771,288 -Size=757,1001 +Pos=447,143 +Size=1442,1129 Collapsed=0 [Window][Tool Calls] @@ -102,26 +102,26 @@ Collapsed=0 DockId=0x0000000D,0 [Window][Discussion Hub] -Pos=756,24 -Size=924,1176 +Pos=1537,24 +Size=1023,1416 Collapsed=0 DockId=0x00000006,0 [Window][Operations Hub] Pos=0,24 -Size=754,1176 +Size=1535,1416 Collapsed=0 DockId=0x00000005,2 [Window][Files & Media] -Pos=756,24 -Size=924,1176 +Pos=1537,24 +Size=1023,1416 Collapsed=0 DockId=0x00000006,1 [Window][AI Settings] Pos=0,24 -Size=754,1176 +Size=1535,1416 Collapsed=0 DockId=0x00000005,0 @@ -131,14 +131,14 @@ Size=416,325 Collapsed=0 [Window][MMA Dashboard] -Pos=756,24 -Size=924,1176 +Pos=1537,24 +Size=1023,1416 Collapsed=0 DockId=0x00000006,2 [Window][Log Management] -Pos=756,24 -Size=924,1176 +Pos=1537,24 +Size=1023,1416 Collapsed=0 DockId=0x00000006,3 @@ -407,7 +407,7 @@ DockId=0x00000006,1 [Window][Project Settings] Pos=0,24 -Size=754,1176 +Size=1535,1416 Collapsed=0 DockId=0x00000005,1 @@ -417,6 +417,16 @@ Size=924,1176 Collapsed=0 DockId=0x00000006,4 +[Window][Text Viewer - ts_cpp_get_skeleton] +Pos=60,58 +Size=1422,1259 +Collapsed=0 + +[Window][Text Viewer - ts_cpp_get_code_outline] +Pos=60,60 +Size=900,700 +Collapsed=0 + [Table][0xFB6E3870,4] RefScale=13 Column 0 Width=80 @@ -541,12 +551,12 @@ Column 2 Width=150 DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=1680,1176 Split=X +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=2560,1416 Split=X DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2175,1183 Split=X DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 DockNode ID=0x00000007 Parent=0x0000000B SizeRef=1512,858 Split=X Selected=0x8CA2375C - DockNode ID=0x00000005 Parent=0x00000007 SizeRef=754,1681 CentralNode=1 Selected=0x418C7449 - DockNode ID=0x00000006 Parent=0x00000007 SizeRef=924,1681 Selected=0x6F2B5B04 + DockNode ID=0x00000005 Parent=0x00000007 SizeRef=655,1681 CentralNode=1 Selected=0x418C7449 + DockNode ID=0x00000006 Parent=0x00000007 SizeRef=1023,1681 Selected=0x6F2B5B04 DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1162,1183 Split=X Selected=0x3AEC3498 diff --git a/src/file_cache.py b/src/file_cache.py index 43b6d2b..e6c7c47 100644 --- a/src/file_cache.py +++ b/src/file_cache.py @@ -94,6 +94,39 @@ class ASTParser: _ast_cache[path] = (mtime, tree) return tree + def _get_name(self, node: tree_sitter.Node, code: str) -> str: + name_node = node.child_by_field_name("name") + if name_node: + return code[name_node.start_byte:name_node.end_byte] + + if node.type in ("function_definition", "field_declaration"): + def find_id(n: tree_sitter.Node) -> str: + if n.type in ("identifier", "field_identifier", "qualified_identifier", "destructor_name"): + return code[n.start_byte:n.end_byte] + # Try field name 'declarator' first + d = n.child_by_field_name("declarator") + if d: + res = find_id(d) + if res: return res + # Fallback to all children + for child in n.children: + if child.type == "compound_statement": continue # Don't look in body + res = find_id(child) + if res: return res + return "" + return find_id(node) + + if node.type == "template_declaration": + for child in node.children: + if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "field_declaration"): + return self._get_name(child, code) + + if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"): + for child in node.children: + if child.type in ("type_identifier", "identifier", "namespace_identifier"): + return code[child.start_byte:child.end_byte] + return "" + def get_skeleton(self, code: str, path: Optional[str] = None) -> str: """ Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). @@ -381,36 +414,6 @@ class ASTParser: """ tree = self.get_cached_tree(path, code) - def get_name(node: tree_sitter.Node) -> str: - name_node = node.child_by_field_name("name") - if name_node: - return code[name_node.start_byte:name_node.end_byte] - - if node.type == "function_definition": - decl = node.child_by_field_name("declarator") - while decl: - if decl.type in ("identifier", "field_identifier"): - return code[decl.start_byte:decl.end_byte] - next_decl = decl.child_by_field_name("declarator") - if not next_decl and decl.child_count > 0: - for child in decl.children: - if child.type in ("identifier", "field_identifier"): - return code[child.start_byte:child.end_byte] - decl = decl.children[0] - else: - decl = next_decl - - if node.type == "template_declaration": - for child in node.children: - if child.type in ("function_definition", "class_definition"): - return get_name(child) - - if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"): - for child in node.children: - if child.type in ("type_identifier", "identifier", "namespace_identifier"): - return code[child.start_byte:child.end_byte] - return "" - parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: @@ -418,42 +421,54 @@ class ASTParser: return None target = target_parts[0] for child in node.children: - if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - if get_name(child) == target: + # If it's a field_declaration, it might wrap a class/struct/enum definition + check_node = child + if child.type == "field_declaration": + for sub in child.children: + if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"): + check_node = sub + break + + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration") + if is_interesting: + node_name = self._get_name(check_node, code) + if node_name == target: if len(target_parts) == 1: - return child - body = child.child_by_field_name("body") - if not body and child.type == "template_declaration": - for sub in child.children: - if sub.type in ("function_definition", "class_definition"): - body = sub.child_by_field_name("body") - break - if body: - found = walk(body, target_parts[1:]) - if found: return found - for sub in child.children: - if sub.type in ("field_declaration_list", "class_body", "declaration_list"): - found = walk(sub, target_parts[1:]) - if found: return found - # Recurse for top-level or namespaces - if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"): - for child in node.children: - if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - found = walk(child, target_parts) + return check_node if child.type != "field_declaration" else child + next_parts = target_parts[1:] + else: + next_parts = target_parts + + body = check_node.child_by_field_name("body") + if not body and check_node.type == "template_declaration": + for sub in check_node.children: + if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"): + body = sub.child_by_field_name("body") + break + if body: + found = walk(body, next_parts) if found: return found + for sub in check_node.children: + if sub.type in ("field_declaration_list", "class_body", "declaration_list"): + found = walk(sub, next_parts) + if found: return found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + found = walk(child, target_parts) + if found: return found + return None + + def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: + if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): + if self._get_name(node, code) == target: + return node + for child in node.children: + res = deep_search(child, target) + if res: return res return None found_node = walk(tree.root_node, parts) - if not found_node and len(parts) == 1: - def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - if get_name(node) == target: - return node - for child in node.children: - res = deep_search(child, target) - if res: return res - return None - found_node = deep_search(tree.root_node, parts[0]) + if not found_node: + found_node = deep_search(tree.root_node, name) if found_node: return code[found_node.start_byte:found_node.end_byte] @@ -466,36 +481,6 @@ class ASTParser: """ tree = self.get_cached_tree(path, code) - def get_name(node: tree_sitter.Node) -> str: - name_node = node.child_by_field_name("name") - if name_node: - return code[name_node.start_byte:name_node.end_byte] - - if node.type == "function_definition": - decl = node.child_by_field_name("declarator") - while decl: - if decl.type in ("identifier", "field_identifier"): - return code[decl.start_byte:decl.end_byte] - next_decl = decl.child_by_field_name("declarator") - if not next_decl and decl.child_count > 0: - for child in decl.children: - if child.type in ("identifier", "field_identifier"): - return code[child.start_byte:child.end_byte] - decl = decl.children[0] - else: - decl = next_decl - - if node.type == "template_declaration": - for child in node.children: - if child.type in ("function_definition", "class_definition"): - return get_name(child) - - if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"): - for child in node.children: - if child.type in ("type_identifier", "identifier", "namespace_identifier"): - return code[child.start_byte:child.end_byte] - return "" - parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: @@ -503,47 +488,60 @@ class ASTParser: return None target = target_parts[0] for child in node.children: - if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - if get_name(child) == target: + # If it's a field_declaration, it might wrap a class/struct/enum definition + check_node = child + if child.type == "field_declaration": + for sub in child.children: + if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"): + check_node = sub + break + + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration") + if is_interesting: + node_name = self._get_name(check_node, code) + if node_name == target: if len(target_parts) == 1: - return child - body = child.child_by_field_name("body") - if not body and child.type == "template_declaration": - for sub in child.children: - if sub.type in ("function_definition", "class_definition"): - body = sub.child_by_field_name("body") - break - if body: - found = walk(body, target_parts[1:]) - if found: return found - for sub in child.children: - if sub.type in ("field_declaration_list", "class_body", "declaration_list"): - found = walk(sub, target_parts[1:]) - if found: return found - if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"): - for child in node.children: - if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - found = walk(child, target_parts) + return check_node if child.type != "field_declaration" else child + next_parts = target_parts[1:] + else: + next_parts = target_parts + + body = check_node.child_by_field_name("body") + if not body and check_node.type == "template_declaration": + for sub in check_node.children: + if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"): + body = sub.child_by_field_name("body") + break + if body: + found = walk(body, next_parts) if found: return found + for sub in check_node.children: + if sub.type in ("field_declaration_list", "class_body", "declaration_list"): + found = walk(sub, next_parts) + if found: return found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + found = walk(child, target_parts) + if found: return found + return None + + def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: + if node.type in ("function_definition", "template_declaration"): + if self._get_name(node, code) == target: + return node + for child in node.children: + res = deep_search(child, target) + if res: return res return None found_node = walk(tree.root_node, parts) - if not found_node and len(parts) == 1: - def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "template_declaration"): - if get_name(node) == target: - return node - for child in node.children: - res = deep_search(child, target) - if res: return res - return None - found_node = deep_search(tree.root_node, parts[0]) + if not found_node: + found_node = deep_search(tree.root_node, name) if found_node: target_node = found_node if found_node.type == "template_declaration": for child in found_node.children: - if child.type in ("function_definition", "class_definition"): + if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"): target_node = child break @@ -561,31 +559,6 @@ class ASTParser: tree = self.get_cached_tree(path, code) output = [] - def get_name(node: tree_sitter.Node) -> str: - name_node = node.child_by_field_name("name") - if name_node: - return code[name_node.start_byte:name_node.end_byte] - - if node.type == "function_definition": - decl = node.child_by_field_name("declarator") - while decl: - if decl.type in ("identifier", "field_identifier"): - return code[decl.start_byte:decl.end_byte] - next_decl = decl.child_by_field_name("declarator") - if not next_decl and decl.child_count > 0: - for child in decl.children: - if child.type in ("identifier", "field_identifier"): - return code[child.start_byte:child.end_byte] - decl = decl.children[0] - else: - decl = next_decl - - if node.type in ("struct_specifier", "class_specifier"): - for child in node.children: - if child.type in ("type_identifier", "identifier"): - return code[child.start_byte:child.end_byte] - return "" - def walk(node: tree_sitter.Node, indent: int = 0) -> None: ntype = node.type label = "" @@ -597,7 +570,7 @@ class ASTParser: label = "[Method]" if indent > 0 else "[Func]" if label: - name = get_name(node) + name = self._get_name(node, code) if name: start = node.start_point.row + 1 end = node.end_point.row + 1 @@ -620,36 +593,6 @@ class ASTParser: """ tree = self.get_cached_tree(path, code) - def get_name(node: tree_sitter.Node) -> str: - name_node = node.child_by_field_name("name") - if name_node: - return code[name_node.start_byte:name_node.end_byte] - - if node.type == "function_definition": - decl = node.child_by_field_name("declarator") - while decl: - if decl.type in ("identifier", "field_identifier"): - return code[decl.start_byte:decl.end_byte] - next_decl = decl.child_by_field_name("declarator") - if not next_decl and decl.child_count > 0: - for child in decl.children: - if child.type in ("identifier", "field_identifier"): - return code[child.start_byte:child.end_byte] - decl = decl.children[0] - else: - decl = next_decl - - if node.type == "template_declaration": - for child in node.children: - if child.type in ("function_definition", "class_definition"): - return get_name(child) - - if node.type in ("struct_specifier", "class_specifier", "class_definition", "namespace_definition"): - for child in node.children: - if child.type in ("type_identifier", "identifier", "namespace_identifier"): - return code[child.start_byte:child.end_byte] - return "" - parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: @@ -657,42 +600,54 @@ class ASTParser: return None target = target_parts[0] for child in node.children: - if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - if get_name(child) == target: + # If it's a field_declaration, it might wrap a class/struct/enum definition + check_node = child + if child.type == "field_declaration": + for sub in child.children: + if sub.type in ("class_specifier", "struct_specifier", "enum_specifier"): + check_node = sub + break + + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "field_declaration") + if is_interesting: + node_name = self._get_name(check_node, code) + if node_name == target: if len(target_parts) == 1: - return child - body = child.child_by_field_name("body") - if not body and child.type == "template_declaration": - for sub in child.children: - if sub.type in ("function_definition", "class_definition"): - body = sub.child_by_field_name("body") - break - if body: - found = walk(body, target_parts[1:]) - if found: return found - for sub in child.children: - if sub.type in ("field_declaration_list", "class_body", "declaration_list"): - found = walk(sub, target_parts[1:]) - if found: return found - # Recurse for top-level or namespaces - if node.type in ("module", "translation_unit", "namespace_definition", "declaration_list"): - for child in node.children: - if child.type not in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - found = walk(child, target_parts) + return check_node if child.type != "field_declaration" else child + next_parts = target_parts[1:] + else: + next_parts = target_parts + + body = check_node.child_by_field_name("body") + if not body and check_node.type == "template_declaration": + for sub in check_node.children: + if sub.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier"): + body = sub.child_by_field_name("body") + break + if body: + found = walk(body, next_parts) if found: return found + for sub in check_node.children: + if sub.type in ("field_declaration_list", "class_body", "declaration_list"): + found = walk(sub, next_parts) + if found: return found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + found = walk(child, target_parts) + if found: return found + return None + + def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: + if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): + if self._get_name(node, code) == target: + return node + for child in node.children: + res = deep_search(child, target) + if res: return res return None found_node = walk(tree.root_node, parts) - if not found_node and len(parts) == 1: - def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "namespace_definition", "template_declaration"): - if get_name(node) == target: - return node - for child in node.children: - res = deep_search(child, target) - if res: return res - return None - found_node = deep_search(tree.root_node, parts[0]) + if not found_node: + found_node = deep_search(tree.root_node, name) if found_node: code_bytes = bytearray(code, "utf8") diff --git a/tests/assets/gencpp_samples/base_component.h b/tests/assets/gencpp_samples/base_component.h new file mode 100644 index 0000000..a70df1a --- /dev/null +++ b/tests/assets/gencpp_samples/base_component.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +namespace gencpp { +namespace core { + +/** + * @brief Base class for all components in the system. + */ +template +class BaseComponent { +public: + virtual ~BaseComponent() = default; + + virtual void Initialize() = 0; + virtual void Shutdown() = 0; + + virtual const std::string& GetName() const = 0; + + struct Config { + std::string name; + int priority; + bool enabled; + + class Metadata { + public: + std::string author; + std::string version; + }; + Metadata metadata; + }; + +protected: + BaseComponent(const Config& config) : m_config(config) {} + Config m_config; +}; + +} // namespace core +} // namespace gencpp diff --git a/tests/assets/gencpp_samples/complex_template.h b/tests/assets/gencpp_samples/complex_template.h new file mode 100644 index 0000000..2d43dfc --- /dev/null +++ b/tests/assets/gencpp_samples/complex_template.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +namespace gencpp { +namespace util { + +/** + * @brief A complex template class demonstrating variadic templates. + */ +template +class MultiBuffer { +public: + void SetData(Args... args) { + m_data = std::make_tuple(args...); + } + + template + auto Get() const -> const typename std::tuple_element>::type& { + return std::get(m_data); + } + +private: + std::tuple m_data; +}; + +/** + * @brief Template specialization example. + */ +template +struct TypeTraits { + static constexpr bool IsPointer = false; +}; + +template +struct TypeTraits { + static constexpr bool IsPointer = true; +}; + +/** + * @brief Nested template class. + */ +template +struct Container { + template + struct Wrapper { + Inner value; + Outer context; + }; +}; + +} // namespace util +} // namespace gencpp diff --git a/tests/assets/gencpp_samples/component_registry.cpp b/tests/assets/gencpp_samples/component_registry.cpp new file mode 100644 index 0000000..98cbe9f --- /dev/null +++ b/tests/assets/gencpp_samples/component_registry.cpp @@ -0,0 +1,26 @@ +#include "component_registry.h" +#include + +namespace gencpp { +namespace registry { + +ComponentRegistry& ComponentRegistry::Instance() { + static ComponentRegistry instance; + return instance; +} + +void ComponentRegistry::Register(const std::string& type, ComponentCreator creator) { + std::cout << "Registering component type: " << type << std::endl; + m_creators[type] = creator; +} + +std::unique_ptr> ComponentRegistry::Create(const std::string& type) { + auto it = m_creators.find(type); + if (it != m_creators.end()) { + return it->second(); + } + return nullptr; +} + +} // namespace registry +} // namespace gencpp diff --git a/tests/assets/gencpp_samples/component_registry.h b/tests/assets/gencpp_samples/component_registry.h new file mode 100644 index 0000000..5fba5d7 --- /dev/null +++ b/tests/assets/gencpp_samples/component_registry.h @@ -0,0 +1,43 @@ +#pragma once + +#include "base_component.h" +#include +#include +#include + +namespace gencpp { +namespace registry { + +class ComponentRegistry { +public: + using ComponentCreator = std::function>()>; + + static ComponentRegistry& Instance(); + + void Register(const std::string& type, ComponentCreator creator); + std::unique_ptr> Create(const std::string& type); + + class Iterator { + public: + using MapIterator = std::map::iterator; + + Iterator(MapIterator it) : m_it(it) {} + + bool operator!=(const Iterator& other) const { return m_it != other.m_it; } + void operator++() { ++m_it; } + const std::string& GetType() const { return m_it->first; } + + private: + MapIterator m_it; + }; + + Iterator Begin() { return Iterator(m_creators.begin()); } + Iterator End() { return Iterator(m_creators.end()); } + +private: + ComponentRegistry() = default; + std::map m_creators; +}; + +} // namespace registry +} // namespace gencpp diff --git a/tests/assets/gencpp_samples/verify_samples.py b/tests/assets/gencpp_samples/verify_samples.py new file mode 100644 index 0000000..3078aa0 --- /dev/null +++ b/tests/assets/gencpp_samples/verify_samples.py @@ -0,0 +1,20 @@ +import os + +def verify_files(): + files = [ + "base_component.h", + "component_registry.h", + "component_registry.cpp", + "complex_template.h" + ] + base_path = "tests/assets/gencpp_samples" + for f in files: + p = os.path.join(base_path, f) + if os.path.exists(p): + print(f"Verified: {f}") + else: + print(f"Missing: {f}") + exit(1) + +if __name__ == "__main__": + verify_files() diff --git a/tests/test_ts_cpp_tools.py b/tests/test_ts_cpp_tools.py index edd9ae9..459c9a8 100644 --- a/tests/test_ts_cpp_tools.py +++ b/tests/test_ts_cpp_tools.py @@ -6,7 +6,7 @@ import sys # Add project root to sys.path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from src.mcp_client import ts_cpp_get_skeleton, ts_cpp_get_code_outline +from src.mcp_client import ts_cpp_get_skeleton, ts_cpp_get_code_outline, ts_cpp_get_definition, ts_cpp_update_definition def test_ts_cpp_get_skeleton(tmp_path): cpp_code = """#include @@ -69,3 +69,91 @@ void templateFunc(T t) { assert "[Func] templateFunc (Lines 8-9)" in outline finally: mcp_client._resolve_and_check = original_resolve + +def test_exhaustive_gencpp_corpus(): + base_dir = Path(__file__).parent / "assets" / "gencpp_samples" + files_to_test = { + "base_component.h": [ + "BaseComponent", + "BaseComponent::Config", + "BaseComponent::Config::Metadata" + ], + "complex_template.h": [ + "MultiBuffer", + "MultiBuffer::SetData", + "MultiBuffer::Get", + "TypeTraits", + "Container::Wrapper" + ], + "component_registry.h": [ + "ComponentRegistry", + "ComponentRegistry::Iterator", + "ComponentRegistry::Iterator::GetType", + "ComponentRegistry::Instance" + ], + "component_registry.cpp": [ + "ComponentRegistry::Instance", + "ComponentRegistry::Register", + "ComponentRegistry::Create" + ] + } + + from src import mcp_client + original_resolve = mcp_client._resolve_and_check + mcp_client._resolve_and_check = lambda path: (Path(path), None) + + try: + for filename, symbols in files_to_test.items(): + path = base_dir / filename + assert path.exists(), f"{path} does not exist" + + # 1. Verify skeleton executes without errors + skeleton = ts_cpp_get_skeleton(str(path)) + assert skeleton and "ERROR" not in skeleton + + # 2. Verify code outline executes without errors + outline = ts_cpp_get_code_outline(str(path)) + assert outline and "ERROR" not in outline + + # 3. Verify specific complex symbols can be retrieved via ts_cpp_get_definition + for symbol in symbols: + definition = ts_cpp_get_definition(str(path), symbol) + assert definition and "ERROR" not in definition, f"Failed to get definition for {symbol} in {filename}: {definition}" + # Basic check that the symbol name is in the definition (allowing for namespaces) + simple_name = symbol.split("::")[-1] + assert simple_name in definition + finally: + mcp_client._resolve_and_check = original_resolve + +def test_ts_cpp_update_definition(tmp_path): + asset_path = Path(__file__).parent / "assets" / "gencpp_samples" / "component_registry.cpp" + cpp_content = asset_path.read_text(encoding="utf-8") + cpp_file = tmp_path / "component_registry.cpp" + cpp_file.write_text(cpp_content, encoding="utf-8") + + from src import mcp_client + original_resolve = mcp_client._resolve_and_check + mcp_client._resolve_and_check = lambda path: (Path(path), None) + + try: + new_register_body = """void ComponentRegistry::Register(const std::string& type, ComponentCreator creator) { + // Updated body + std::cout << "DEBUG: Registering " << type << std::endl; + m_creators[type] = creator; + if (type == "secret") { + std::cout << "Special component" << std::endl; + } +}""" + result = ts_cpp_update_definition(str(cpp_file), "ComponentRegistry::Register", new_register_body) + assert "Successfully updated" in result + + updated_content = cpp_file.read_text(encoding="utf-8") + assert "// Updated body" in updated_content + assert "DEBUG: Registering" in updated_content + + # Verify it still parses by getting definition again + definition = ts_cpp_get_definition(str(cpp_file), "ComponentRegistry::Register") + assert "// Updated body" in definition + assert "DEBUG: Registering" in definition + finally: + mcp_client._resolve_and_check = original_resolve