Files
whetstone_DSL/editor/src/panels/SidePanels.h

309 lines
12 KiB
C++

#pragma once
#include "../EditorState.h"
#include "../EditorUtils.h"
static void renderOutlinePanel(EditorState& state) {
if (!state.ui.showOutline) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Outline", &state.ui.showOutline);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
queueFeatureHint(state.featureHints,
"hint.outline",
"Tip: Use the Outline to jump between symbols quickly.");
ImGui::PushFont(state.uiFont);
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputText("Filter", state.outlineFilter, sizeof(state.outlineFilter));
ImGui::Separator();
std::string filter = toLowerCopy(state.outlineFilter);
if (!state.active()) {
ImGui::TextDisabled("(no file)");
} else {
std::vector<LSPClient::DocumentSymbol> lspSymbols =
state.lsp ? state.lsp->getDocumentSymbols() : std::vector<LSPClient::DocumentSymbol>{};
if (!lspSymbols.empty()) {
int outlineId = 0;
std::function<void(const LSPClient::DocumentSymbol&)> renderLsp;
renderLsp = [&](const LSPClient::DocumentSymbol& sym) {
if (!lspSymbolMatchesFilter(sym, filter)) return;
ImGui::PushID(outlineId++);
std::string label = std::string(lspSymbolKindLabel(sym.kind)) + ": " + sym.name;
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth;
if (sym.children.empty()) flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
bool open = ImGui::TreeNodeEx(label.c_str(), flags);
if (ImGui::IsItemClicked()) {
state.jumpTo(state.active(), sym.selectionRange.start.line,
sym.selectionRange.start.character);
}
if (!sym.children.empty() && open) {
for (const auto& child : sym.children) {
renderLsp(child);
}
ImGui::TreePop();
}
ImGui::PopID();
};
for (const auto& sym : lspSymbols) renderLsp(sym);
} else if (state.isStructured()) {
std::vector<OutlineItem> outline;
collectOutlineFromAST(state.activeAST(), outline);
if (outline.empty()) {
ImGui::TextDisabled("(no symbols)");
} else {
int outlineId = 0;
std::function<void(const OutlineItem&)> renderItem;
renderItem = [&](const OutlineItem& item) {
if (!outlineMatchesFilter(item, filter)) return;
ImGui::PushID(outlineId++);
std::string label = item.kind + ": " + item.name;
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth;
if (item.children.empty()) flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
bool open = ImGui::TreeNodeEx(label.c_str(), flags);
if (ImGui::IsItemClicked() && item.line >= 0) {
state.jumpTo(state.active(), item.line, item.col);
}
if (!item.children.empty() && open) {
for (const auto& child : item.children) {
renderItem(child);
}
ImGui::TreePop();
}
ImGui::PopID();
};
for (const auto& item : outline) renderItem(item);
}
} else {
ImGui::TextDisabled("(no symbols)");
}
}
ImGui::PopFont();
ImGui::End();
}
static void renderDependenciesPanel(EditorState& state) {
if (!state.library.showDependencyPanel) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Dependencies", &state.library.showDependencyPanel);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
renderDependencyPanel(state.library.dependencyPanel,
state.workspaceRoot,
state.notifications,
state.library.vulnDb);
ImGui::PopFont();
ImGui::End();
state.library.primitives.setVulnerableLibraries(
state.library.dependencyPanel.vulnerableLibraries);
if (state.library.dependencyPanel.needsIndex) {
state.requestLibraryIndex();
state.library.dependencyPanel.needsIndex = false;
}
}
static void renderLibrariesPanel(EditorState& state) {
if (!state.library.showLibraryBrowserPanel) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Libraries", &state.library.showLibraryBrowserPanel);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
std::string insertText;
std::string insertLibrary;
if (renderLibraryBrowser(state.library.libraryBrowser,
state.activeAST(),
state.library.libraryIndex,
insertText,
insertLibrary,
state.notifications)) {
state.ensureImportForSymbol(insertLibrary, insertText);
state.insertTextAtCursor(insertText);
}
ImGui::PopFont();
ImGui::End();
}
static void renderCompositionPanel(EditorState& state) {
if (!state.library.showCompositionPanel) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Compose", &state.library.showCompositionPanel);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
std::string nodeId;
if (state.activeAST()) {
ASTNode* scopeNode = findNodeAtPosition(state.activeAST(),
std::max(0, state.active()->cursorLine - 1),
std::max(0, state.active()->cursorCol - 1));
if (scopeNode) nodeId = scopeNode->id;
}
std::vector<PrimitiveSymbol> primitives;
auto funcs = state.library.primitives.getAvailableFunctions(nodeId);
primitives.insert(primitives.end(), funcs.begin(), funcs.end());
std::string code;
if (renderCompositionPanel(state.library.compositionPanel, primitives, code, state.notifications)) {
state.insertTextAtCursor(code);
}
ImGui::PopFont();
ImGui::End();
}
static void renderEmacsPackagesPanel(EditorState& state) {
if (!state.emacsState.showEmacsPackagesPanel) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Emacs Packages", &state.emacsState.showEmacsPackagesPanel);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
if (renderEmacsPackageBrowser(state.emacsState.emacsPackages,
state.emacsState.emacs,
state.notifications)) {
state.emacsState.emacsFunctionIndexDirty = true;
}
ImGui::PopFont();
ImGui::End();
}
static void renderEmacsBridgePanel(EditorState& state) {
if (!state.emacsState.showEmacsBridgePanel) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Emacs Bridge", &state.emacsState.showEmacsBridgePanel);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
if (state.active()) {
ImGui::Text("Active file: %s", state.active()->path.c_str());
} else {
ImGui::TextDisabled("(no active buffer)");
}
ImGui::Separator();
if (ImGui::Button("Open Emacs Frame")) {
state.openInEmacsFrame();
}
ImGui::SameLine();
if (ImGui::Button("Pull From Emacs")) {
state.pullFromEmacs();
}
ImGui::SameLine();
if (ImGui::Button("Push To Emacs")) {
state.pushToEmacs();
}
ImGui::PopFont();
ImGui::End();
}
static void renderMinibuffer(EditorState& state) {
if (!(state.ui.layoutPreset == LayoutPreset::Emacs && state.emacsState.emacsKeys.minibufferActive))
return;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGuiWindowFlags mbFlags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoScrollbar;
float mbHeight = ImGui::GetFrameHeight() + 10;
ImVec2 mbPos(viewport->WorkPos.x,
viewport->WorkPos.y + viewport->WorkSize.y - mbHeight - 24.0f);
ImVec2 mbSize(viewport->WorkSize.x, mbHeight);
ImGui::SetNextWindowPos(mbPos);
ImGui::SetNextWindowSize(mbSize);
ImGui::Begin("##Minibuffer", nullptr, mbFlags);
ImGui::PushFont(state.uiFont);
ImGui::TextUnformatted(state.emacsState.emacsKeys.minibufferPrompt.c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(-1.0f);
if (ImGui::InputText("##minibuffer",
state.emacsState.emacsKeys.minibufferBuf,
sizeof(state.emacsState.emacsKeys.minibufferBuf),
ImGuiInputTextFlags_EnterReturnsTrue)) {
emacsExecuteMinibuffer(state.emacsState.emacsKeys,
state.emacsState.emacs,
state.notifications);
}
ImGui::PopFont();
ImGui::End();
}
static void renderMemoryStrategiesPanel(EditorState& state) {
if (!state.ui.showMemoryStrategies) return;
if (state.ui.focusTarget == FocusRegion::Side) {
ImGui::SetNextWindowFocus();
state.ui.focusTarget = FocusRegion::None;
}
ImGui::Begin("Memory Strategies", &state.ui.showMemoryStrategies);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
state.ui.focusedRegion = FocusRegion::Side;
}
ImGui::PushFont(state.uiFont);
Module* ast = state.active() ? state.active()->sync.getAST() : nullptr;
if (state.active() && state.active()->bufferMode == BufferManager::BufferMode::Text) {
ImGui::TextDisabled("(disabled in Text mode)");
} else if (!ast) {
ImGui::TextDisabled("(no AST)");
} else {
std::vector<AnnotationEntry> entries;
collectAnnotationEntries(ast, entries);
std::map<std::string, int> 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.notify(NotificationLevel::Info,
"Annotation summary:\\n" + j.dump(2));
}
}
ImGui::PopFont();
ImGui::End();
}