273 lines
12 KiB
C++
273 lines
12 KiB
C++
#pragma once
|
|
#include "../EditorState.h"
|
|
#include "../EditorUtils.h"
|
|
#include "../ThemeEngine.h"
|
|
|
|
static void renderMenuBar(EditorState& state) {
|
|
if (!ImGui::BeginMainMenuBar()) return;
|
|
|
|
if (ImGui::BeginMenu("File")) {
|
|
if (ImGui::MenuItem("New", state.keys.getBinding("file.new").toString().c_str()))
|
|
{
|
|
std::string lang = state.active() ? state.active()->language : "python";
|
|
state.createBuffer(state.makeUntitledName(), "", lang, state.defaultBufferMode());
|
|
}
|
|
if (ImGui::MenuItem("Open...", state.keys.getBinding("file.open").toString().c_str()))
|
|
{
|
|
auto path = FileDialog::openFile({"Open File", {"*.py","*.cpp","*.h","*.el","*.js","*.ts","*.java","*.rs","*.go","*.org","*.md","*.txt","*.docx","*.*"}, state.lastDialogPath});
|
|
if (!path.empty()) {
|
|
state.lastDialogPath = path;
|
|
state.doOpen(path, state.defaultBufferMode());
|
|
}
|
|
}
|
|
if (ImGui::MenuItem("Open Project...")) {
|
|
auto path = FileDialog::openFile({"Open Project", {"*.whetstone"}, state.lastDialogPath});
|
|
if (!path.empty()) {
|
|
if (!state.loadProject(path)) {
|
|
state.notify(NotificationLevel::Error,
|
|
"Failed to open project: " + path);
|
|
} else {
|
|
state.lastDialogPath = path;
|
|
}
|
|
}
|
|
}
|
|
if (ImGui::MenuItem("Open Folder...")) {
|
|
auto path = FileDialog::openFolder({"Open Folder", state.workspaceRoot});
|
|
if (!path.empty()) {
|
|
std::string error;
|
|
if (!state.setWorkspaceRoot(path, &error)) {
|
|
state.notify(NotificationLevel::Error,
|
|
"Failed to open folder: " + error);
|
|
} else {
|
|
state.lastDialogPath = path;
|
|
}
|
|
}
|
|
}
|
|
if (ImGui::MenuItem("Save", state.keys.getBinding("file.save").toString().c_str()))
|
|
{
|
|
if (!state.active()) {
|
|
// no-op
|
|
} else if (state.active()->path.rfind("(untitled", 0) == 0) {
|
|
auto path = FileDialog::saveFile({"Save File", {"*.*"}, state.lastDialogPath});
|
|
if (!path.empty()) {
|
|
state.active()->path = path;
|
|
state.lastDialogPath = path;
|
|
state.doSave();
|
|
}
|
|
} else {
|
|
state.doSave();
|
|
}
|
|
}
|
|
if (ImGui::MenuItem("Save Project...")) {
|
|
auto path = FileDialog::saveFile({"Save Project", {"*.whetstone"}, state.lastDialogPath});
|
|
if (!path.empty()) {
|
|
if (!state.saveProject(path)) {
|
|
state.notify(NotificationLevel::Error,
|
|
"Failed to save project: " + path);
|
|
} else {
|
|
state.lastDialogPath = path;
|
|
}
|
|
}
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Exit")) state.exitRequested = true;
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Edit")) {
|
|
if (ImGui::MenuItem("Undo", state.keys.getBinding("edit.undo").toString().c_str(),
|
|
false, state.active() ? state.active()->orchestrator.getUndoDepth() > 0 : false))
|
|
state.doUndo();
|
|
if (ImGui::MenuItem("Redo", state.keys.getBinding("edit.redo").toString().c_str(),
|
|
false, state.active() ? state.active()->orchestrator.getRedoDepth() > 0 : false))
|
|
state.doRedo();
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Find/Replace", state.keys.getBinding("search.find").toString().c_str())) {
|
|
state.search.showFind = true;
|
|
state.search.showReplace = true;
|
|
}
|
|
if (ImGui::MenuItem("Find in Files", state.keys.getBinding("search.findInFiles").toString().c_str()))
|
|
state.search.showProjectSearch = !state.search.showProjectSearch;
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("View")) {
|
|
ImGui::MenuItem("Show Whitespace", nullptr, &state.ui.showWhitespace);
|
|
ImGui::MenuItem("Show Minimap", nullptr, &state.ui.showMinimap);
|
|
ImGui::MenuItem("Inline Completion Helper", nullptr, &state.ui.showCompletionHelper);
|
|
ImGui::MenuItem("Show Annotations", nullptr, &state.ui.showAnnotations);
|
|
ImGui::MenuItem("Show Outline", nullptr, &state.ui.showOutline);
|
|
ImGui::MenuItem("Terminal", state.keys.getBinding("view.toggleTerminal").toString().c_str(),
|
|
&state.build.showTerminalPanel);
|
|
ImGui::MenuItem("Dependencies", nullptr, &state.library.showDependencyPanel);
|
|
ImGui::MenuItem("Libraries", nullptr, &state.library.showLibraryBrowserPanel);
|
|
ImGui::MenuItem("Compose", nullptr, &state.library.showCompositionPanel);
|
|
ImGui::MenuItem("Memory Strategies", nullptr, &state.ui.showMemoryStrategies);
|
|
ImGui::MenuItem("Agent Chat", nullptr, &state.ui.showAgentChatPanel);
|
|
ImGui::MenuItem("Emacs Packages", nullptr, &state.emacsState.showEmacsPackagesPanel);
|
|
ImGui::MenuItem("Emacs Bridge", nullptr, &state.emacsState.showEmacsBridgePanel);
|
|
ImGui::MenuItem("Settings", nullptr, &state.ui.showSettingsPanel);
|
|
ImGui::MenuItem("Keyboard Shortcuts", nullptr, &state.ui.showShortcutReference);
|
|
ImGui::MenuItem("LSP Servers...", nullptr, &state.ui.showLspSettings);
|
|
if (state.active()) {
|
|
bool textMode = state.active()->bufferMode == BufferManager::BufferMode::Text;
|
|
if (ImGui::MenuItem("Text-Editor Mode", nullptr, textMode)) {
|
|
state.active()->bufferMode = textMode ?
|
|
BufferManager::BufferMode::Structured :
|
|
BufferManager::BufferMode::Text;
|
|
state.buffers.setBufferMode(state.active()->path, state.active()->bufferMode);
|
|
if (state.active()->bufferMode == BufferManager::BufferMode::Text) {
|
|
state.suggestions.clear();
|
|
state.whetstoneDiagnostics.clear();
|
|
state.analysisPending = false;
|
|
} else {
|
|
state.onTextChanged();
|
|
}
|
|
if (state.active()->path.rfind("(untitled", 0) != 0) {
|
|
state.welcome.addRecentFile(state.active()->path,
|
|
state.active()->language,
|
|
bufferModeToString(state.active()->bufferMode));
|
|
state.saveRecentFiles();
|
|
}
|
|
}
|
|
}
|
|
if (ImGui::BeginMenu("Layout")) {
|
|
if (ImGui::MenuItem("VSCode", nullptr, state.ui.layoutPreset == LayoutPreset::VSCode))
|
|
state.ui.layoutPreset = LayoutPreset::VSCode;
|
|
if (ImGui::MenuItem("Emacs", nullptr, state.ui.layoutPreset == LayoutPreset::Emacs))
|
|
state.ui.layoutPreset = LayoutPreset::Emacs;
|
|
if (ImGui::MenuItem("JetBrains", nullptr, state.ui.layoutPreset == LayoutPreset::JetBrains))
|
|
state.ui.layoutPreset = LayoutPreset::JetBrains;
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Reset Layout")) {
|
|
state.ui.requestLayoutReset = true;
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Navigate")) {
|
|
if (ImGui::MenuItem("Go to Line...", state.keys.getBinding("nav.goToLine").toString().c_str())) {
|
|
state.search.showGoToLine = true;
|
|
state.search.goToLineBuf[0] = '\0';
|
|
state.search.goToLineError = false;
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Build")) {
|
|
if (ImGui::MenuItem("Run", state.keys.getBinding("build.run").toString().c_str())) {
|
|
state.runActiveFile(false);
|
|
}
|
|
if (ImGui::MenuItem("Build", state.keys.getBinding("build.build").toString().c_str())) {
|
|
state.runActiveFile(true);
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Language")) {
|
|
if (ImGui::MenuItem("Python", nullptr, state.active() && state.active()->language == "python"))
|
|
state.setLanguage("python");
|
|
if (ImGui::MenuItem("C++", nullptr, state.active() && state.active()->language == "cpp"))
|
|
state.setLanguage("cpp");
|
|
if (ImGui::MenuItem("Elisp", nullptr, state.active() && state.active()->language == "elisp"))
|
|
state.setLanguage("elisp");
|
|
if (ImGui::MenuItem("JavaScript", nullptr, state.active() && state.active()->language == "javascript"))
|
|
state.setLanguage("javascript");
|
|
if (ImGui::MenuItem("TypeScript", nullptr, state.active() && state.active()->language == "typescript"))
|
|
state.setLanguage("typescript");
|
|
if (ImGui::MenuItem("Java", nullptr, state.active() && state.active()->language == "java"))
|
|
state.setLanguage("java");
|
|
if (ImGui::MenuItem("Rust", nullptr, state.active() && state.active()->language == "rust"))
|
|
state.setLanguage("rust");
|
|
if (ImGui::MenuItem("Go", nullptr, state.active() && state.active()->language == "go"))
|
|
state.setLanguage("go");
|
|
if (ImGui::MenuItem("Org", nullptr, state.active() && state.active()->language == "org"))
|
|
state.setLanguage("org");
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Refactor")) {
|
|
bool canRefactor = state.isStructured() && state.active() && !state.active()->readOnly;
|
|
if (!canRefactor) ImGui::BeginDisabled();
|
|
if (ImGui::MenuItem("Rename Variable")) {
|
|
state.refactorAction = 1;
|
|
state.showRefactorPopup = true;
|
|
state.refactorNameA[0] = '\0';
|
|
state.refactorNameB[0] = '\0';
|
|
ImGui::OpenPopup("RefactorPopup");
|
|
}
|
|
if (ImGui::MenuItem("Extract Function")) {
|
|
state.refactorAction = 2;
|
|
state.showRefactorPopup = true;
|
|
state.refactorNameA[0] = '\0';
|
|
state.refactorNameB[0] = '\0';
|
|
ImGui::OpenPopup("RefactorPopup");
|
|
}
|
|
if (ImGui::MenuItem("Inline Variable")) {
|
|
state.refactorAction = 3;
|
|
state.showRefactorPopup = true;
|
|
state.refactorNameA[0] = '\0';
|
|
state.refactorNameB[0] = '\0';
|
|
ImGui::OpenPopup("RefactorPopup");
|
|
}
|
|
if (!canRefactor) ImGui::EndDisabled();
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Workflow")) {
|
|
bool hasAst = state.activeAST() != nullptr;
|
|
if (!hasAst) ImGui::BeginDisabled();
|
|
if (ImGui::MenuItem("Annotate File Wizard")) {
|
|
state.ui.showAnnotateWizard = true;
|
|
}
|
|
if (ImGui::MenuItem("Cross-Language Project Wizard")) {
|
|
state.ui.showProjectWizard = true;
|
|
}
|
|
if (!hasAst) ImGui::EndDisabled();
|
|
if (ImGui::MenuItem("Connect Agent Wizard")) {
|
|
state.ui.showAgentWizard = true;
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Keybindings")) {
|
|
for (auto p : KeybindingManager::availableProfiles()) {
|
|
if (ImGui::MenuItem(KeybindingManager::profileName(p), nullptr,
|
|
state.keys.getProfile() == p)) {
|
|
state.keys.setProfile(p);
|
|
state.registerCommands();
|
|
}
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Help")) {
|
|
ImGui::MenuItem("Documentation", nullptr, &state.ui.showHelpPanel);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
const bool canProject = state.activeAST() != nullptr;
|
|
ImGui::SameLine();
|
|
ImGui::Dummy(ImVec2(12.0f, 0.0f));
|
|
ImGui::SameLine();
|
|
if (!canProject) ImGui::BeginDisabled();
|
|
if (ImGui::Button("Project to...")) {
|
|
ImGui::OpenPopup("ProjectToPopup");
|
|
}
|
|
if (ImGui::BeginPopup("ProjectToPopup")) {
|
|
if (ImGui::MenuItem("Python")) state.projectToLanguage("python");
|
|
if (ImGui::MenuItem("C++")) state.projectToLanguage("cpp");
|
|
if (ImGui::MenuItem("Elisp")) state.projectToLanguage("elisp");
|
|
ImGui::EndPopup();
|
|
}
|
|
if (!canProject) ImGui::EndDisabled();
|
|
|
|
ImGui::SameLine();
|
|
ImGui::Dummy(ImVec2(12.0f, 0.0f));
|
|
ImGui::SameLine();
|
|
bool canRunFile = state.active() && !state.active()->readOnly;
|
|
if (!canRunFile) ImGui::BeginDisabled();
|
|
if (ImGui::Button("Run >")) {
|
|
state.runActiveFile(false);
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Build")) {
|
|
state.runActiveFile(true);
|
|
}
|
|
if (!canRunFile) ImGui::EndDisabled();
|
|
ImGui::EndMainMenuBar();
|
|
}
|