Step 357: Status Bar (12/12 tests)
This commit is contained in:
@@ -2122,4 +2122,8 @@ add_executable(step356_test tests/step356_test.cpp)
|
|||||||
target_include_directories(step356_test PRIVATE src)
|
target_include_directories(step356_test PRIVATE src)
|
||||||
target_link_libraries(step356_test PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(step356_test PRIVATE nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
|
add_executable(step357_test tests/step357_test.cpp)
|
||||||
|
target_include_directories(step357_test PRIVATE src)
|
||||||
|
target_link_libraries(step357_test PRIVATE nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||||
|
|||||||
90
editor/src/panels/StatusBar.h
Normal file
90
editor/src/panels/StatusBar.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 357: Status bar model.
|
||||||
|
// Headless representation used for UI rendering/tests.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "../ThemeData.h"
|
||||||
|
|
||||||
|
struct StatusBarInput {
|
||||||
|
std::string language = "python";
|
||||||
|
std::string filePath;
|
||||||
|
int line = 1;
|
||||||
|
int column = 1;
|
||||||
|
std::string encoding = "UTF-8";
|
||||||
|
std::string lineEnding = "LF";
|
||||||
|
bool workflowActive = false;
|
||||||
|
int workflowCurrent = 0;
|
||||||
|
int workflowTotal = 0;
|
||||||
|
bool mcpConnected = false;
|
||||||
|
int errorCount = 0;
|
||||||
|
int warningCount = 0;
|
||||||
|
int infoCount = 0;
|
||||||
|
int hintCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StatusBarModel {
|
||||||
|
std::string languageBadge;
|
||||||
|
std::string leftText;
|
||||||
|
std::string centerText;
|
||||||
|
std::string rightText;
|
||||||
|
std::string workflowBadge;
|
||||||
|
std::string diagnosticsText;
|
||||||
|
std::string mcpText;
|
||||||
|
bool overflow = false;
|
||||||
|
|
||||||
|
Color4 bgColor;
|
||||||
|
Color4 textColor;
|
||||||
|
Color4 dimTextColor;
|
||||||
|
Color4 languageBadgeColor;
|
||||||
|
Color4 diagnosticsColor;
|
||||||
|
Color4 workflowColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Color4 statusLanguageColor(const std::string& language, const WhetstoneTheme& theme) {
|
||||||
|
if (language == "python") return theme.syntaxFunction;
|
||||||
|
if (language == "cpp") return theme.syntaxType;
|
||||||
|
if (language == "kotlin") return theme.syntaxAnnotation;
|
||||||
|
if (language == "rust") return theme.accent;
|
||||||
|
if (language == "go") return theme.syntaxKeyword;
|
||||||
|
return theme.textAccent;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline StatusBarModel buildStatusBar(const StatusBarInput& in,
|
||||||
|
const WhetstoneTheme& theme,
|
||||||
|
size_t maxChars = 140) {
|
||||||
|
StatusBarModel m;
|
||||||
|
m.bgColor = theme.bgPanel;
|
||||||
|
m.textColor = theme.text;
|
||||||
|
m.dimTextColor = theme.textDim;
|
||||||
|
m.languageBadgeColor = statusLanguageColor(in.language, theme);
|
||||||
|
m.workflowColor = theme.textAccent;
|
||||||
|
m.diagnosticsColor = (in.errorCount > 0) ? theme.diagError :
|
||||||
|
(in.warningCount > 0 ? theme.diagWarning : theme.diagInfo);
|
||||||
|
|
||||||
|
m.languageBadge = in.language;
|
||||||
|
m.leftText = in.language + " " + in.filePath;
|
||||||
|
m.centerText = "Ln " + std::to_string(in.line) + ", Col " + std::to_string(in.column) +
|
||||||
|
" | " + in.encoding + " | " + in.lineEnding;
|
||||||
|
m.diagnosticsText = std::to_string(in.errorCount) + " errors, " +
|
||||||
|
std::to_string(in.warningCount) + " warnings";
|
||||||
|
m.mcpText = in.mcpConnected ? "MCP: connected" : "MCP: offline";
|
||||||
|
|
||||||
|
if (in.workflowActive && in.workflowTotal > 0) {
|
||||||
|
m.workflowBadge = "Executing " + std::to_string(in.workflowCurrent) +
|
||||||
|
"/" + std::to_string(in.workflowTotal);
|
||||||
|
} else {
|
||||||
|
m.workflowBadge = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
m.rightText = m.diagnosticsText;
|
||||||
|
if (!m.workflowBadge.empty()) m.rightText += " | " + m.workflowBadge;
|
||||||
|
m.rightText += " | " + m.mcpText;
|
||||||
|
|
||||||
|
const size_t total = m.leftText.size() + m.centerText.size() + m.rightText.size();
|
||||||
|
m.overflow = total > maxChars;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string statusBarClickDiagnosticsTarget() {
|
||||||
|
return "diagnostics";
|
||||||
|
}
|
||||||
154
editor/tests/step357_test.cpp
Normal file
154
editor/tests/step357_test.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Step 357: Status Bar (12 tests)
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "panels/StatusBar.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int passed = 0;
|
||||||
|
WhetstoneTheme theme = getDefaultDarkTheme();
|
||||||
|
|
||||||
|
// Test 1: language badge shows current buffer language
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.language = "python";
|
||||||
|
in.filePath = "src/main.py";
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.languageBadge == "python");
|
||||||
|
std::cout << "Test 1 PASSED: language badge\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: cursor position updates
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
in.line = 42;
|
||||||
|
in.column = 8;
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.centerText.find("Ln 42, Col 8") != std::string::npos);
|
||||||
|
std::cout << "Test 2 PASSED: cursor position\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: diagnostic count accurate
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
in.errorCount = 3;
|
||||||
|
in.warningCount = 2;
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.diagnosticsText == "3 errors, 2 warnings");
|
||||||
|
std::cout << "Test 3 PASSED: diagnostics count\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: workflow badge shown when active
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
in.workflowActive = true;
|
||||||
|
in.workflowCurrent = 3;
|
||||||
|
in.workflowTotal = 10;
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.workflowBadge == "Executing 3/10");
|
||||||
|
std::cout << "Test 4 PASSED: workflow badge active\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: click diagnostics focuses diagnostics panel
|
||||||
|
{
|
||||||
|
assert(statusBarClickDiagnosticsTarget() == "diagnostics");
|
||||||
|
std::cout << "Test 5 PASSED: diagnostics click target\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: status bar uses theme colors
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.bgColor.toHex() == theme.bgPanel.toHex());
|
||||||
|
assert(model.textColor.toHex() == theme.text.toHex());
|
||||||
|
assert(model.dimTextColor.toHex() == theme.textDim.toHex());
|
||||||
|
std::cout << "Test 6 PASSED: theme colors\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: language changes when switching buffers
|
||||||
|
{
|
||||||
|
StatusBarInput py;
|
||||||
|
py.language = "python";
|
||||||
|
py.filePath = "a.py";
|
||||||
|
StatusBarInput cpp;
|
||||||
|
cpp.language = "cpp";
|
||||||
|
cpp.filePath = "a.cpp";
|
||||||
|
auto m1 = buildStatusBar(py, theme);
|
||||||
|
auto m2 = buildStatusBar(cpp, theme);
|
||||||
|
assert(m1.languageBadge != m2.languageBadge);
|
||||||
|
std::cout << "Test 7 PASSED: language switch reflected\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: encoding display
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
in.encoding = "UTF-16";
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.centerText.find("UTF-16") != std::string::npos);
|
||||||
|
std::cout << "Test 8 PASSED: encoding display\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 9: all sections fit without overflow for normal input
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "src/main.cpp";
|
||||||
|
auto model = buildStatusBar(in, theme, 200);
|
||||||
|
assert(!model.overflow);
|
||||||
|
std::cout << "Test 9 PASSED: no overflow normal case\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 10: overflow flagged for extreme long path
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = std::string(220, 'a');
|
||||||
|
auto model = buildStatusBar(in, theme, 80);
|
||||||
|
assert(model.overflow);
|
||||||
|
std::cout << "Test 10 PASSED: overflow flagged\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 11: line ending display
|
||||||
|
{
|
||||||
|
StatusBarInput in;
|
||||||
|
in.filePath = "file";
|
||||||
|
in.lineEnding = "CRLF";
|
||||||
|
auto model = buildStatusBar(in, theme);
|
||||||
|
assert(model.centerText.find("CRLF") != std::string::npos);
|
||||||
|
std::cout << "Test 11 PASSED: line ending display\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 12: MCP indicator states
|
||||||
|
{
|
||||||
|
StatusBarInput on;
|
||||||
|
on.filePath = "file";
|
||||||
|
on.mcpConnected = true;
|
||||||
|
StatusBarInput off = on;
|
||||||
|
off.mcpConnected = false;
|
||||||
|
auto a = buildStatusBar(on, theme);
|
||||||
|
auto b = buildStatusBar(off, theme);
|
||||||
|
assert(a.mcpText == "MCP: connected");
|
||||||
|
assert(b.mcpText == "MCP: offline");
|
||||||
|
std::cout << "Test 12 PASSED: MCP indicator states\n";
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\nResults: " << passed << "/12\n";
|
||||||
|
assert(passed == 12);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
36
progress.md
36
progress.md
@@ -1899,6 +1899,42 @@ theme-derived colors for consistent visual integration.
|
|||||||
- `step355_test` — PASS (8/8) regression coverage
|
- `step355_test` — PASS (8/8) regression coverage
|
||||||
- `step356_test` — PASS (12/12) new step coverage
|
- `step356_test` — PASS (12/12) new step coverage
|
||||||
|
|
||||||
|
### Step 357: Status Bar
|
||||||
|
**Status:** PASS (12/12 tests)
|
||||||
|
|
||||||
|
Added a status-bar data model that provides left/center/right sections for
|
||||||
|
language/file, cursor+encoding+line-ending, and diagnostics/workflow/MCP state.
|
||||||
|
The model includes theme-driven colors, overflow signaling, and a diagnostics
|
||||||
|
focus target for click handling.
|
||||||
|
|
||||||
|
**Files created:**
|
||||||
|
- `editor/src/panels/StatusBar.h` — headless status model:
|
||||||
|
- `StatusBarInput` + `StatusBarModel`
|
||||||
|
- `buildStatusBar(...)` section construction and overflow check
|
||||||
|
- language badge color mapping
|
||||||
|
- diagnostics click target helper
|
||||||
|
- `editor/tests/step357_test.cpp` — 12 tests covering:
|
||||||
|
1. language badge
|
||||||
|
2. cursor position updates
|
||||||
|
3. diagnostic summary counts
|
||||||
|
4. workflow badge visibility/content
|
||||||
|
5. diagnostics click target
|
||||||
|
6. theme color usage
|
||||||
|
7. language switch behavior
|
||||||
|
8. encoding display
|
||||||
|
9. normal-fit no-overflow case
|
||||||
|
10. overflow flag on extreme content
|
||||||
|
11. line-ending display
|
||||||
|
12. MCP connected/offline indicator
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
- `editor/CMakeLists.txt` — `step357_test` target
|
||||||
|
|
||||||
|
**Verification run:**
|
||||||
|
- `step355_test` — PASS (8/8) regression coverage
|
||||||
|
- `step356_test` — PASS (12/12) regression coverage
|
||||||
|
- `step357_test` — PASS (12/12) new step coverage
|
||||||
|
|
||||||
# Roadmap Planning — Sprints 12-25+
|
# Roadmap Planning — Sprints 12-25+
|
||||||
|
|
||||||
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user