diff --git a/PROGRESS.md b/PROGRESS.md index deb6436..50772fb 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -202,6 +202,7 @@ All 38 steps implemented and passing. Each step has a corresponding test (`step1 - [x] Step 99: **IMPLEMENTED** — Annotation context menu for add/edit/remove via ASTMutationAPI (2/2 tests pass) - [x] Step 100: **IMPLEMENTED** — Memory strategy suggestions with lightbulb gutter icons and apply popup (1/1 tests pass) - [x] Step 101: **IMPLEMENTED** — Annotation conflict highlighting with gutter linkage and quick-fix actions (1/1 tests pass) +- [x] Step 102: **IMPLEMENTED** — Memory strategy dashboard panel with counts and JSON export (1/1 tests pass) --- @@ -283,6 +284,7 @@ vcpkg's imgui 1.91.9 removed the `sdl2-binding` feature (only `sdl3-binding` exi **Step 99:** Compile and pass (2/2) **Step 100:** Compile and pass (1/1) **Step 101:** Compile and pass (1/1) +**Step 102:** Compile and pass (1/1) --- @@ -393,3 +395,4 @@ Sprint 4 in progress. Step 76 (LayoutManager) done. Next: Step 77 (custom code e | 2026-02-09 | Codex | Step 99: Annotation context menu (add/edit/remove) via ASTMutationAPI. 2/2 tests pass. | | 2026-02-09 | Codex | Step 100: Memory strategy suggestions with lightbulb icons and apply popup. 1/1 tests pass. | | 2026-02-09 | Codex | Step 101: Annotation conflict highlighting with quick-fix actions. 1/1 tests pass. | +| 2026-02-09 | Codex | Step 102: Memory strategy dashboard panel with counts and JSON export. 1/1 tests pass. | diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 41680eb..c0a248f 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -554,6 +554,10 @@ add_executable(step101_test tests/step101_test.cpp) target_include_directories(step101_test PRIVATE src) target_link_libraries(step101_test PRIVATE nlohmann_json::nlohmann_json) +add_executable(step102_test tests/step102_test.cpp) +target_include_directories(step102_test PRIVATE src) +target_link_libraries(step102_test PRIVATE nlohmann_json::nlohmann_json) + find_package(SDL2 CONFIG REQUIRED) find_package(OpenGL REQUIRED) find_package(glad CONFIG REQUIRED) diff --git a/editor/src/StrategyDashboard.h b/editor/src/StrategyDashboard.h new file mode 100644 index 0000000..68ffcc2 --- /dev/null +++ b/editor/src/StrategyDashboard.h @@ -0,0 +1,88 @@ +#pragma once +// Step 102: Memory strategy dashboard helpers + +#include +#include +#include +#include +#include "ast/ASTNode.h" +#include "ast/Annotation.h" +#include "ast/Function.h" +#include "ast/Module.h" + +struct AnnotationEntry { + std::string nodeId; + std::string nodeName; + std::string label; + int line = -1; +}; + +inline bool isMemoryAnnotation(const ASTNode* anno) { + if (!anno) return false; + const auto& ct = anno->conceptType; + return ct == "DeallocateAnnotation" || ct == "LifetimeAnnotation" || + ct == "ReclaimAnnotation" || ct == "OwnerAnnotation" || + ct == "AllocateAnnotation"; +} + +inline std::string annotationLabelFor(const ASTNode* anno) { + if (!anno) return ""; + if (anno->conceptType == "ReclaimAnnotation") { + auto* a = static_cast(anno); + return "@Reclaim(" + a->strategy + ")"; + } + if (anno->conceptType == "OwnerAnnotation") { + auto* a = static_cast(anno); + return "@Owner(" + a->strategy + ")"; + } + if (anno->conceptType == "DeallocateAnnotation") { + auto* a = static_cast(anno); + return "@Deallocate(" + a->strategy + ")"; + } + if (anno->conceptType == "LifetimeAnnotation") { + auto* a = static_cast(anno); + return "@Lifetime(" + a->strategy + ")"; + } + if (anno->conceptType == "AllocateAnnotation") { + auto* a = static_cast(anno); + return "@Allocate(" + a->strategy + ")"; + } + return anno->conceptType; +} + +inline std::string nodeNameFor(const ASTNode* node) { + if (!node) return ""; + if (node->conceptType == "Function") return static_cast(node)->name; + if (node->conceptType == "Module") return static_cast(node)->name; + return node->conceptType; +} + +inline void collectAnnotationEntries(const ASTNode* node, std::vector& out) { + if (!node) return; + for (auto* anno : node->getChildren("annotations")) { + if (!isMemoryAnnotation(anno)) continue; + AnnotationEntry e; + e.nodeId = node->id; + e.nodeName = nodeNameFor(node); + e.label = annotationLabelFor(anno); + if (node->hasSpan()) e.line = node->spanStartLine; + out.push_back(std::move(e)); + } + for (auto* child : node->allChildren()) { + collectAnnotationEntries(child, out); + } +} + +inline nlohmann::json buildAnnotationSummaryJson(const std::vector& entries) { + nlohmann::json j; + j["annotations"] = nlohmann::json::array(); + for (const auto& e : entries) { + j["annotations"].push_back({ + {"nodeId", e.nodeId}, + {"nodeName", e.nodeName}, + {"label", e.label}, + {"line", e.line} + }); + } + return j; +} diff --git a/editor/src/main.cpp b/editor/src/main.cpp index 58e234f..10e86f3 100644 --- a/editor/src/main.cpp +++ b/editor/src/main.cpp @@ -32,6 +32,7 @@ #include "ASTMutationAPI.h" #include "MemoryStrategyInference.h" #include "AnnotationConflict.h" +#include "StrategyDashboard.h" #include "ast/Generator.h" #include "ast/Annotation.h" @@ -1826,6 +1827,55 @@ int main(int, char**) { ImGui::End(); + // --------------------------------------------------------------- + // Memory Strategies dashboard + // --------------------------------------------------------------- + ImGui::Begin("Memory Strategies"); + ImGui::PushFont(uiFont); + Module* ast = state.active() ? state.active()->sync.getAST() : nullptr; + if (!ast) { + ImGui::TextDisabled("(no AST)"); + } else { + std::vector entries; + collectAnnotationEntries(ast, entries); + std::map counts; + for (const auto& e : entries) counts[e.label]++; + + ImGui::Text("Annotations"); + ImGui::Separator(); + for (const auto& [label, count] : counts) { + ImGui::Text("%s: %d", label.c_str(), count); + } + ImGui::Spacing(); + + ImGui::Text("Details"); + ImGui::Separator(); + for (const auto& e : entries) { + std::string lineInfo = e.line >= 0 ? ("L" + std::to_string(e.line + 1)) : "-"; + std::string row = e.label + " — " + e.nodeName + " (" + lineInfo + ")"; + if (ImGui::Selectable(row.c_str())) { + if (state.active()) state.jumpTo(state.active(), e.line, 0); + } + } + + ImGui::Spacing(); + ImGui::Text("Suggestions"); + ImGui::Separator(); + for (const auto& s : state.suggestions) { + if (s.confidence < 0.5) continue; + std::string row = s.annotationType + "(" + s.strategy + ") — " + + s.nodeId + " (" + std::to_string(s.confidence) + ")"; + ImGui::TextUnformatted(row.c_str()); + } + + if (ImGui::Button("Export JSON")) { + auto j = buildAnnotationSummaryJson(entries); + state.outputLog += "Annotation summary:\\n" + j.dump(2) + "\\n"; + } + } + ImGui::PopFont(); + ImGui::End(); + // --------------------------------------------------------------- // Status bar // --------------------------------------------------------------- diff --git a/editor/tests/step102_test.cpp b/editor/tests/step102_test.cpp new file mode 100644 index 0000000..9bd9ee5 --- /dev/null +++ b/editor/tests/step102_test.cpp @@ -0,0 +1,37 @@ +// Step 102 TDD Test: Memory strategy dashboard helpers +// +// Tests: +// 1. Collect annotation entries and build JSON + +#include +#include +#include "StrategyDashboard.h" +#include "ast/Module.h" +#include "ast/Function.h" +#include "ast/Annotation.h" + +int main() { + int passed = 0; + int failed = 0; + + Module* mod = new Module("m1", "mod", "cpp"); + mod->setSpan(0, 0, 5, 0); + auto* fn = new Function("f1", "foo"); + fn->setSpan(1, 0, 3, 0); + mod->addChild("functions", fn); + + auto* anno = new LifetimeAnnotation("a1", "RAII"); + fn->addChild("annotations", anno); + + std::vector entries; + collectAnnotationEntries(mod, entries); + assert(entries.size() == 1); + auto j = buildAnnotationSummaryJson(entries); + assert(j.contains("annotations")); + assert(j["annotations"].size() == 1); + std::cout << "Test 1 PASS: dashboard helpers" << std::endl; + ++passed; + + std::cout << "\n=== Step 102 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}