diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ba2aa54..e7286a7 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2785,4 +2785,13 @@ target_link_libraries(step433_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step434_test tests/step434_test.cpp) +target_include_directories(step434_test PRIVATE src) +target_link_libraries(step434_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/CodeAnnotationHeatmap.h b/editor/src/CodeAnnotationHeatmap.h new file mode 100644 index 0000000..4891303 --- /dev/null +++ b/editor/src/CodeAnnotationHeatmap.h @@ -0,0 +1,50 @@ +#pragma once + +#include "CodeAnnotationBadges.h" + +#include +#include +#include +#include + +struct HeatmapCell { + int line = 0; + int score = 0; + std::string colorHex; +}; + +class CodeAnnotationHeatmap { +public: + static std::vector build(const std::vector& annotations, + int maxLine, + bool enabled = true) { + if (!enabled || maxLine <= 0) return {}; + std::map scores; + for (const auto& a : annotations) { + if (a.line <= 0 || a.line > maxLine) continue; + scores[a.line] += weightForType(a.type); + } + std::vector out; + for (int line = 1; line <= maxLine; ++line) { + int score = scores.count(line) ? scores[line] : 0; + out.push_back({line, score, colorForScore(score)}); + } + return out; + } + + static int weightForType(const std::string& type) { + if (type.find("Complexity") != std::string::npos) return 3; + if (type.find("Risk") != std::string::npos) return 4; + if (type.find("Review") != std::string::npos) return 2; + if (type.find("Contract") != std::string::npos) return 1; + if (type.find("Intent") != std::string::npos) return 1; + return 1; + } + + static std::string colorForScore(int score) { + if (score <= 0) return "#1F6F8B"; // cool blue (no annotations) + if (score <= 2) return "#2ECC71"; // cool green (well-annotated simple) + if (score <= 5) return "#F39C12"; // warm orange (moderate complexity) + return "#E74C3C"; // hot red (high complexity/risk) + } +}; diff --git a/editor/tests/step434_test.cpp b/editor/tests/step434_test.cpp new file mode 100644 index 0000000..22e4d1d --- /dev/null +++ b/editor/tests/step434_test.cpp @@ -0,0 +1,126 @@ +// Step 434: Annotation Heatmap Tests (12 tests) + +#include "CodeAnnotationHeatmap.h" + +#include + +static int passed = 0, failed = 0; +#define TEST(name) { std::cout << " " << #name << "... "; } +#define PASS() { std::cout << "PASS\n"; ++passed; } +#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } +#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} + +static std::vector sample() { + return { + {2, "IntentAnnotation", "intent", "n1"}, + {3, "ComplexityAnnotation", "complex", "n2"}, + {3, "RiskAnnotation", "risk", "n2"}, + {5, "ReviewAnnotation", "review", "n3"}, + {6, "ContractAnnotation", "contract", "n4"} + }; +} + +void test_build_heatmap_returns_cells_for_each_line() { + TEST(build_heatmap_returns_cells_for_each_line); + auto cells = CodeAnnotationHeatmap::build(sample(), 8, true); + CHECK(cells.size() == 8, "expected 8 cells"); + PASS(); +} + +void test_disabled_heatmap_returns_empty() { + TEST(disabled_heatmap_returns_empty); + auto cells = CodeAnnotationHeatmap::build(sample(), 8, false); + CHECK(cells.empty(), "disabled heatmap should be empty"); + PASS(); +} + +void test_maxline_nonpositive_returns_empty() { + TEST(maxline_nonpositive_returns_empty); + auto cells = CodeAnnotationHeatmap::build(sample(), 0, true); + CHECK(cells.empty(), "maxLine <=0 should return empty"); + PASS(); +} + +void test_weight_mapping_complexity() { + TEST(weight_mapping_complexity); + CHECK(CodeAnnotationHeatmap::weightForType("ComplexityAnnotation") == 3, "complexity weight"); + PASS(); +} + +void test_weight_mapping_risk() { + TEST(weight_mapping_risk); + CHECK(CodeAnnotationHeatmap::weightForType("RiskAnnotation") == 4, "risk weight"); + PASS(); +} + +void test_weight_mapping_review() { + TEST(weight_mapping_review); + CHECK(CodeAnnotationHeatmap::weightForType("ReviewAnnotation") == 2, "review weight"); + PASS(); +} + +void test_color_for_score_cool_blue() { + TEST(color_for_score_cool_blue); + CHECK(CodeAnnotationHeatmap::colorForScore(0) == "#1F6F8B", "score 0 should be blue"); + PASS(); +} + +void test_color_for_score_cool_green() { + TEST(color_for_score_cool_green); + CHECK(CodeAnnotationHeatmap::colorForScore(2) == "#2ECC71", "score 2 should be green"); + PASS(); +} + +void test_color_for_score_orange() { + TEST(color_for_score_orange); + CHECK(CodeAnnotationHeatmap::colorForScore(5) == "#F39C12", "score 5 should be orange"); + PASS(); +} + +void test_color_for_score_red() { + TEST(color_for_score_red); + CHECK(CodeAnnotationHeatmap::colorForScore(6) == "#E74C3C", "score 6 should be red"); + PASS(); +} + +void test_line_score_accumulates_multiple_annotations() { + TEST(line_score_accumulates_multiple_annotations); + auto cells = CodeAnnotationHeatmap::build(sample(), 8, true); + int line3 = -1; + for (const auto& c : cells) if (c.line == 3) line3 = c.score; + CHECK(line3 == 7, "line 3 should accumulate complexity(3)+risk(4)=7"); + PASS(); +} + +void test_annotations_outside_range_ignored() { + TEST(annotations_outside_range_ignored); + auto in = sample(); + in.push_back({100, "RiskAnnotation", "x", "n5"}); + auto cells = CodeAnnotationHeatmap::build(in, 8, true); + CHECK(cells.size() == 8, "still bounded to maxLine"); + int total = 0; + for (const auto& c : cells) total += c.score; + CHECK(total < 20, "out-of-range annotation should not inflate score"); + PASS(); +} + +int main() { + std::cout << "Step 434: Annotation Heatmap Tests\n"; + + test_build_heatmap_returns_cells_for_each_line(); // 1 + test_disabled_heatmap_returns_empty(); // 2 + test_maxline_nonpositive_returns_empty(); // 3 + test_weight_mapping_complexity(); // 4 + test_weight_mapping_risk(); // 5 + test_weight_mapping_review(); // 6 + test_color_for_score_cool_blue(); // 7 + test_color_for_score_cool_green(); // 8 + test_color_for_score_orange(); // 9 + test_color_for_score_red(); // 10 + test_line_score_accumulates_multiple_annotations();// 11 + test_annotations_outside_range_ignored(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index b4f3483..7168996 100644 --- a/progress.md +++ b/progress.md @@ -5040,6 +5040,49 @@ type-specific icon/color mapping, stacked line badges, and expandable detail tex - `editor/src/MCPServer.h` (`1940` > `600`) - `editor/src/HeadlessAgentRPCHandler.h` (`2768` > `600`) +### Step 434: Annotation Heatmap +**Status:** PASS (12/12 tests) + +Added a heatmap projection for code annotations to support line-level intensity +visualization in workflow overlays, including category weighting and color mapping. + +**Files created:** +- `editor/src/CodeAnnotationHeatmap.h` — heatmap support: + - annotation input -> per-line heat cells + - category weighting (complexity/risk/review/priority/intent/contract) + - deterministic score accumulation per line + - score-to-color mapping (cool blue -> green -> orange -> red) + - range filtering for out-of-bounds annotations +- `editor/tests/step434_test.cpp` — 12 tests covering: + 1. one cell per line generation behavior + 2. disabled heatmap returns empty output + 3. invalid max-line boundary handling + 4. complexity weight mapping + 5. risk weight mapping + 6. review weight mapping + 7. low-score cool-blue color mapping + 8. low-mid cool-green color mapping + 9. mid-high orange color mapping + 10. high-score red color mapping + 11. multi-annotation score accumulation + 12. out-of-range annotation filtering + +**Files modified:** +- `editor/CMakeLists.txt` — `step434_test` target + +**Verification run:** +- `step434_test` — PASS (12/12) new step coverage +- `step433_test` — PASS (12/12) regression coverage +- `step432_test` — PASS (8/8) regression coverage + +**Architecture gate check:** +- `editor/src/CodeAnnotationHeatmap.h` within header-size limit (`50` <= `600`) +- `editor/tests/step434_test.cpp` within test-file size guidance (`126` lines) +- Legacy oversized headers persist: + - `editor/src/ast/Serialization.h` (`1427` > `600`) + - `editor/src/MCPServer.h` (`1940` > `600`) + - `editor/src/HeadlessAgentRPCHandler.h` (`2768` > `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)