Sprints 46-58 implement the cross-language porting foundation: language-to-IR adapters, equivalence checking, gate validation, legacy ingestion, managed/dynamic families, low-level/logic-actor semantics, debug workflow tooling, AST-native family tools, Rust/CPP raising tools, system-level orchestration, query family, and porting gates. Sprint 59 adds the governance layer (policy packs, review boards, waiver packets, ambiguity triage, decision ledger) with the whetstone_review_porting_decision MCP tool. Also includes: sprint plans 46-130, MCP taskitem pipeline scripts, CLAUDE.md, docs, and full test matrix (steps 689-828). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
145 lines
5.8 KiB
C++
145 lines
5.8 KiB
C++
|
|
// ---------------------------------------------------------------------------
|
|
// Syntax highlight color map
|
|
// ---------------------------------------------------------------------------
|
|
static ImVec4 tokenColor(TokenCategory cat) {
|
|
switch (cat) {
|
|
case TokenCategory::Keyword: return ImVec4(0.77f, 0.49f, 0.86f, 1.0f); // purple
|
|
case TokenCategory::String: return ImVec4(0.81f, 0.54f, 0.37f, 1.0f); // orange
|
|
case TokenCategory::Comment: return ImVec4(0.42f, 0.52f, 0.35f, 1.0f); // green
|
|
case TokenCategory::Number: return ImVec4(0.71f, 0.80f, 0.55f, 1.0f); // light green
|
|
case TokenCategory::Function: return ImVec4(0.86f, 0.86f, 0.55f, 1.0f); // yellow
|
|
case TokenCategory::Parameter: return ImVec4(0.60f, 0.78f, 0.90f, 1.0f); // light blue
|
|
case TokenCategory::Type: return ImVec4(0.30f, 0.70f, 0.68f, 1.0f); // teal
|
|
case TokenCategory::Operator: return ImVec4(0.86f, 0.86f, 0.86f, 1.0f); // white
|
|
case TokenCategory::Punctuation: return ImVec4(0.60f, 0.60f, 0.60f, 1.0f); // gray
|
|
case TokenCategory::Builtin: return ImVec4(0.30f, 0.70f, 0.90f, 1.0f); // blue
|
|
case TokenCategory::Identifier: return ImVec4(0.60f, 0.78f, 0.90f, 1.0f); // light blue
|
|
default: return ImVec4(0.86f, 0.86f, 0.86f, 1.0f); // white
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Render syntax-highlighted text (read-only preview)
|
|
// ---------------------------------------------------------------------------
|
|
static void RenderHighlightedText(const std::string& text,
|
|
const std::vector<HighlightSpan>& spans) {
|
|
if (text.empty()) return;
|
|
|
|
// Build a color for each byte position
|
|
// Default = plain text color
|
|
std::vector<TokenCategory> charCats(text.size(), TokenCategory::Plain);
|
|
for (const auto& span : spans) {
|
|
for (uint32_t i = span.start; i < span.end && i < charCats.size(); ++i) {
|
|
charCats[i] = span.category;
|
|
}
|
|
}
|
|
|
|
// Render line by line, span by span
|
|
size_t pos = 0;
|
|
int lineNum = 1;
|
|
while (pos < text.size()) {
|
|
// Find end of line
|
|
size_t eol = text.find('\n', pos);
|
|
if (eol == std::string::npos) eol = text.size();
|
|
|
|
// Line number
|
|
ImGui::TextColored(ImVec4(0.45f, 0.45f, 0.45f, 1.0f), "%4d ", lineNum);
|
|
ImGui::SameLine(0.0f, 0.0f);
|
|
|
|
// Render spans within this line
|
|
size_t linePos = pos;
|
|
while (linePos < eol) {
|
|
// Find the extent of the current color
|
|
TokenCategory cat = charCats[linePos];
|
|
size_t spanEnd = linePos + 1;
|
|
while (spanEnd < eol && charCats[spanEnd] == cat) ++spanEnd;
|
|
|
|
std::string chunk = text.substr(linePos, spanEnd - linePos);
|
|
ImGui::TextColored(tokenColor(cat), "%s", chunk.c_str());
|
|
if (spanEnd < eol) ImGui::SameLine(0.0f, 0.0f);
|
|
|
|
linePos = spanEnd;
|
|
}
|
|
|
|
if (linePos == pos) {
|
|
// Empty line
|
|
ImGui::TextUnformatted("");
|
|
}
|
|
|
|
pos = eol + 1;
|
|
++lineNum;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// File tree rendering
|
|
// ---------------------------------------------------------------------------
|
|
static void RenderFileTree(const FileNode& node, EditorState& state) {
|
|
if (!node.isDir) {
|
|
if (ImGui::Selectable(node.name.c_str())) {
|
|
state.doOpen(node.path, state.defaultBufferMode());
|
|
}
|
|
return;
|
|
}
|
|
|
|
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
|
if (!state.workspaceRoot.empty() && node.path == state.workspaceRoot) {
|
|
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
|
}
|
|
if (ImGui::TreeNodeEx(node.name.c_str(), flags)) {
|
|
for (const auto& child : node.children) {
|
|
RenderFileTree(child, state);
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Welcome screen rendering
|
|
// ---------------------------------------------------------------------------
|
|
static void RenderWelcome(WelcomeScreen& welcome, EditorState& state, std::string& lastDialogPath) {
|
|
ImGui::Text("%s", welcome.getTitle().c_str());
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "%s", welcome.getSubtitle().c_str());
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Quick Actions");
|
|
if (ImGui::Button("New File")) {
|
|
std::string lang = state.active() ? state.active()->language : "python";
|
|
state.createBuffer(state.makeUntitledName(), "", lang, state.defaultBufferMode());
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Open File")) {
|
|
auto path = FileDialog::openFile({"Open File", {"*.py","*.cpp","*.h","*.el","*.js","*.ts","*.java","*.rs","*.go","*.org","*.md","*.txt","*.docx","*.*"}, lastDialogPath});
|
|
if (!path.empty()) {
|
|
lastDialogPath = path;
|
|
state.doOpen(path, state.defaultBufferMode());
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Open Folder")) {
|
|
auto path = FileDialog::openFolder({"Open Folder", state.workspaceRoot});
|
|
if (!path.empty()) {
|
|
std::string error;
|
|
if (state.setWorkspaceRoot(path, &error)) {
|
|
lastDialogPath = path;
|
|
} else {
|
|
state.notify(NotificationLevel::Error,
|
|
"Failed to open folder: " + error);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Recent Files");
|
|
for (const auto& rf : welcome.getRecentFiles()) {
|
|
if (ImGui::Selectable(rf.displayName.c_str())) {
|
|
state.doOpen(rf.path, bufferModeFromString(rf.mode));
|
|
}
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Tip");
|
|
ImGui::TextWrapped("%s", welcome.getTip(0).c_str());
|
|
}
|