|
|
@@ -1,567 +1,53 @@
|
|
|
#include "lumacs/command_system.hpp"
|
|
|
-#include "lumacs/editor_core.hpp"
|
|
|
-#include "lumacs/lua_api.hpp"
|
|
|
-#include <algorithm>
|
|
|
-#include <sstream>
|
|
|
-#include <regex>
|
|
|
-#include <iostream>
|
|
|
-#include <filesystem>
|
|
|
-#include <unordered_set>
|
|
|
-#include <iomanip>
|
|
|
+#include "lumacs/editor_core.hpp" // For EditorCore interaction (if needed by commands)
|
|
|
+#include <algorithm> // For std::sort
|
|
|
|
|
|
namespace lumacs {
|
|
|
|
|
|
-// FileSystemCompletionProvider Implementation
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> FileSystemCompletionProvider::complete_path(const std::string& input) const {
|
|
|
- std::vector<CompletionCandidate> candidates;
|
|
|
-
|
|
|
- try {
|
|
|
- std::string path_part = input;
|
|
|
- std::string dirname = ".";
|
|
|
- std::string basename = input;
|
|
|
-
|
|
|
- // Extract directory and filename parts
|
|
|
- auto last_slash = input.find_last_of("/\\");
|
|
|
- if (last_slash != std::string::npos) {
|
|
|
- dirname = input.substr(0, last_slash);
|
|
|
- basename = input.substr(last_slash + 1);
|
|
|
- if (dirname.empty()) dirname = "/";
|
|
|
- }
|
|
|
-
|
|
|
- // Expand ~ to home directory
|
|
|
- if (dirname.starts_with("~")) {
|
|
|
- const char* home = getenv("HOME");
|
|
|
- if (home) {
|
|
|
- dirname = std::string(home) + dirname.substr(1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!std::filesystem::exists(dirname)) {
|
|
|
- return candidates;
|
|
|
- }
|
|
|
-
|
|
|
- // Iterate through directory entries
|
|
|
- for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
|
|
|
- std::string filename = entry.path().filename().string();
|
|
|
-
|
|
|
- // Skip hidden files unless input starts with .
|
|
|
- if (filename.starts_with(".") && !basename.starts_with(".")) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // Check if filename matches input
|
|
|
- if (basename.empty() || filename.starts_with(basename)) {
|
|
|
- std::string full_path;
|
|
|
- if (dirname == ".") {
|
|
|
- full_path = filename;
|
|
|
- } else {
|
|
|
- full_path = (dirname == "/" ? "/" : dirname + "/") + filename;
|
|
|
- }
|
|
|
-
|
|
|
- // Add trailing slash for directories
|
|
|
- if (entry.is_directory()) {
|
|
|
- full_path += "/";
|
|
|
- }
|
|
|
-
|
|
|
- int score = calculate_path_score(filename, basename);
|
|
|
- std::string desc = entry.is_directory() ? "directory" : "file";
|
|
|
- candidates.emplace_back(full_path, score, desc);
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (const std::filesystem::filesystem_error& e) {
|
|
|
- // Ignore filesystem errors
|
|
|
- }
|
|
|
-
|
|
|
- std::sort(candidates.begin(), candidates.end());
|
|
|
- return candidates;
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> FileSystemCompletionProvider::complete_directory(const std::string& input) const {
|
|
|
- auto candidates = complete_path(input);
|
|
|
- // Filter to only directories
|
|
|
- candidates.erase(
|
|
|
- std::remove_if(candidates.begin(), candidates.end(),
|
|
|
- [](const CompletionCandidate& c) { return c.description != "directory"; }),
|
|
|
- candidates.end());
|
|
|
- return candidates;
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> FileSystemCompletionProvider::complete_file(const std::string& input, const std::vector<std::string>& extensions) const {
|
|
|
- auto candidates = complete_path(input);
|
|
|
-
|
|
|
- if (!extensions.empty()) {
|
|
|
- candidates.erase(
|
|
|
- std::remove_if(candidates.begin(), candidates.end(),
|
|
|
- [&extensions](const CompletionCandidate& c) {
|
|
|
- if (c.description == "directory") return false; // Keep directories
|
|
|
- for (const auto& ext : extensions) {
|
|
|
- if (c.text.ends_with(ext)) return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }),
|
|
|
- candidates.end());
|
|
|
- }
|
|
|
-
|
|
|
- return candidates;
|
|
|
+CommandSystem::CommandSystem(EditorCore* core) : core_(core) {
|
|
|
+ // Register core commands here, or ensure they are registered via Lua
|
|
|
}
|
|
|
|
|
|
-bool FileSystemCompletionProvider::is_absolute_path(const std::string& path) const {
|
|
|
- return !path.empty() && (path[0] == '/' || path[0] == '~');
|
|
|
-}
|
|
|
-
|
|
|
-std::string FileSystemCompletionProvider::normalize_path(const std::string& path) const {
|
|
|
- std::string normalized = path;
|
|
|
- if (normalized.starts_with("~")) {
|
|
|
- const char* home = getenv("HOME");
|
|
|
- if (home) {
|
|
|
- normalized = std::string(home) + normalized.substr(1);
|
|
|
- }
|
|
|
+void CommandSystem::register_command(const std::string& name, CommandFunction function,
|
|
|
+ const std::string& doc_string, bool interactive, std::string interactive_spec) {
|
|
|
+ // Check if command already exists
|
|
|
+ if (commands_.count(name)) {
|
|
|
+ // Log a warning or throw an error if overriding is not allowed
|
|
|
+ // For now, new registration simply overwrites old one
|
|
|
}
|
|
|
- return normalized;
|
|
|
+ commands_.emplace(name, Command(name, std::move(function), doc_string, interactive, std::move(interactive_spec)));
|
|
|
}
|
|
|
|
|
|
-int FileSystemCompletionProvider::calculate_path_score(const std::string& candidate, const std::string& input) const {
|
|
|
- if (input.empty()) return 50;
|
|
|
- if (candidate == input) return 100;
|
|
|
- if (candidate.starts_with(input)) return 90;
|
|
|
-
|
|
|
- // Fuzzy matching score
|
|
|
- return CommandSystem::fuzzy_match_score(candidate, input);
|
|
|
-}
|
|
|
-
|
|
|
-// CommandSystem Implementation
|
|
|
-
|
|
|
-CommandSystem::CommandSystem(EditorCore& core) : core_(core) {
|
|
|
- register_builtin_commands();
|
|
|
-}
|
|
|
-
|
|
|
-void CommandSystem::register_command(std::unique_ptr<Command> command) {
|
|
|
- if (!command) return;
|
|
|
-
|
|
|
- std::string name = command->name;
|
|
|
- auto shared_cmd = std::shared_ptr<Command>(command.release());
|
|
|
- commands_[name] = shared_cmd;
|
|
|
-
|
|
|
- // Register aliases
|
|
|
- for (const auto& alias : shared_cmd->aliases) {
|
|
|
- commands_[alias] = shared_cmd;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void CommandSystem::register_command(const std::string& name, const std::string& description,
|
|
|
- CommandFunction function, const std::vector<std::string>& aliases,
|
|
|
- bool interactive) {
|
|
|
- auto command = std::make_unique<Command>(name, description, std::move(function), aliases, interactive);
|
|
|
- register_command(std::move(command));
|
|
|
-}
|
|
|
-
|
|
|
-CommandResult CommandSystem::execute(const std::string& command_name, const std::vector<std::string>& args) {
|
|
|
- auto it = commands_.find(command_name);
|
|
|
- if (it == commands_.end()) {
|
|
|
- return CommandResult(false, "Command not found: " + command_name);
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- return it->second->function(args);
|
|
|
- } catch (const std::exception& e) {
|
|
|
- return CommandResult(false, "Command error: " + std::string(e.what()));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-CommandResult CommandSystem::execute_string(const std::string& command_string) {
|
|
|
- auto parts = parse_command_string(command_string);
|
|
|
- if (parts.empty()) {
|
|
|
- return CommandResult(false, "Empty command");
|
|
|
- }
|
|
|
-
|
|
|
- std::string command_name = parts[0];
|
|
|
- std::vector<std::string> args(parts.begin() + 1, parts.end());
|
|
|
-
|
|
|
- return execute(command_name, args);
|
|
|
-}
|
|
|
-
|
|
|
-bool CommandSystem::has_command(const std::string& name) const {
|
|
|
- return commands_.find(name) != commands_.end();
|
|
|
-}
|
|
|
-
|
|
|
-std::shared_ptr<Command> CommandSystem::get_command(const std::string& name) const {
|
|
|
+CommandResult CommandSystem::execute(const std::string& name, const std::vector<std::string>& args) {
|
|
|
auto it = commands_.find(name);
|
|
|
- return (it != commands_.end()) ? it->second : nullptr;
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<std::string> CommandSystem::get_all_command_names() const {
|
|
|
- std::vector<std::string> names;
|
|
|
- std::unordered_set<std::string> unique_names;
|
|
|
-
|
|
|
- for (const auto& [name, command] : commands_) {
|
|
|
- if (unique_names.insert(command->name).second) {
|
|
|
- names.push_back(command->name);
|
|
|
+ if (it != commands_.end()) {
|
|
|
+ try {
|
|
|
+ return it->second.function(args);
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ return {false, "Command '" + name + "' failed: " + e.what()};
|
|
|
+ } catch (...) {
|
|
|
+ return {false, "Command '" + name + "' failed with unknown error."};
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- std::sort(names.begin(), names.end());
|
|
|
- return names;
|
|
|
+ return {false, "Unknown command: " + name};
|
|
|
}
|
|
|
|
|
|
-std::vector<std::string> CommandSystem::get_interactive_command_names() const {
|
|
|
+std::vector<std::string> CommandSystem::get_command_names() const {
|
|
|
std::vector<std::string> names;
|
|
|
- std::unordered_set<std::string> unique_names;
|
|
|
-
|
|
|
- for (const auto& [name, command] : commands_) {
|
|
|
- if (command->interactive && unique_names.insert(command->name).second) {
|
|
|
- names.push_back(command->name);
|
|
|
- }
|
|
|
+ names.reserve(commands_.size());
|
|
|
+ for (const auto& pair : commands_) {
|
|
|
+ names.push_back(pair.first);
|
|
|
}
|
|
|
-
|
|
|
std::sort(names.begin(), names.end());
|
|
|
return names;
|
|
|
}
|
|
|
|
|
|
-std::vector<CompletionCandidate> CommandSystem::complete_command(const std::string& input) const {
|
|
|
- std::vector<CompletionCandidate> candidates;
|
|
|
- std::unordered_set<std::string> unique_names;
|
|
|
-
|
|
|
- for (const auto& [name, command] : commands_) {
|
|
|
- if (!command->interactive || !unique_names.insert(command->name).second) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- int score = fuzzy_match_score(command->name, input);
|
|
|
- if (score > 0) {
|
|
|
- candidates.emplace_back(command->name, score, command->description);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- std::sort(candidates.begin(), candidates.end());
|
|
|
- return candidates;
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> CommandSystem::complete_buffer_name(const std::string& input) const {
|
|
|
- std::vector<CompletionCandidate> candidates;
|
|
|
- auto buffer_names = core_.get_buffer_names();
|
|
|
-
|
|
|
- for (const auto& name : buffer_names) {
|
|
|
- int score = fuzzy_match_score(name, input);
|
|
|
- if (score > 0) {
|
|
|
- auto buffer = core_.get_buffer_by_name(name);
|
|
|
- std::string desc = "buffer";
|
|
|
- if (buffer && buffer->is_modified()) {
|
|
|
- desc += " (modified)";
|
|
|
- }
|
|
|
- candidates.emplace_back(name, score, desc);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- std::sort(candidates.begin(), candidates.end());
|
|
|
- return candidates;
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> CommandSystem::complete_file_path(const std::string& input) const {
|
|
|
- return fs_provider_.complete_path(input);
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> CommandSystem::complete_theme_name(const std::string& input) const {
|
|
|
- std::vector<CompletionCandidate> candidates;
|
|
|
- auto theme_names = core_.theme_manager().theme_names();
|
|
|
- auto current_theme = core_.active_theme();
|
|
|
- std::string current_name = current_theme ? current_theme->name() : "";
|
|
|
-
|
|
|
- for (const auto& name : theme_names) {
|
|
|
- int score = fuzzy_match_score(name, input);
|
|
|
- if (score > 0) {
|
|
|
- std::string desc = "theme";
|
|
|
- if (name == current_name) {
|
|
|
- desc += " (current)";
|
|
|
- }
|
|
|
- candidates.emplace_back(name, score, desc);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- std::sort(candidates.begin(), candidates.end());
|
|
|
- return candidates;
|
|
|
-}
|
|
|
-
|
|
|
-void CommandSystem::register_completion_provider(const std::string& command_name, CompletionProvider provider) {
|
|
|
- completion_providers_[command_name] = std::move(provider);
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<CompletionCandidate> CommandSystem::get_completions(const std::string& command_name, const std::string& input) const {
|
|
|
- auto it = completion_providers_.find(command_name);
|
|
|
- if (it != completion_providers_.end()) {
|
|
|
- return it->second(input);
|
|
|
- }
|
|
|
- return {};
|
|
|
-}
|
|
|
-
|
|
|
-int CommandSystem::fuzzy_match_score(const std::string& candidate, const std::string& input) {
|
|
|
- if (input.empty()) return 50;
|
|
|
- if (candidate == input) return 100;
|
|
|
- if (candidate.starts_with(input)) return 90;
|
|
|
-
|
|
|
- // Convert to lowercase for case-insensitive matching
|
|
|
- std::string lower_candidate = candidate;
|
|
|
- std::string lower_input = input;
|
|
|
- std::transform(lower_candidate.begin(), lower_candidate.end(), lower_candidate.begin(), ::tolower);
|
|
|
- std::transform(lower_input.begin(), lower_input.end(), lower_input.begin(), ::tolower);
|
|
|
-
|
|
|
- if (lower_candidate == lower_input) return 95;
|
|
|
- if (lower_candidate.starts_with(lower_input)) return 85;
|
|
|
-
|
|
|
- // Fuzzy matching - check if all input characters appear in order
|
|
|
- size_t candidate_idx = 0;
|
|
|
- size_t matched_chars = 0;
|
|
|
-
|
|
|
- for (char c : lower_input) {
|
|
|
- while (candidate_idx < lower_candidate.size() && lower_candidate[candidate_idx] != c) {
|
|
|
- candidate_idx++;
|
|
|
- }
|
|
|
- if (candidate_idx < lower_candidate.size()) {
|
|
|
- matched_chars++;
|
|
|
- candidate_idx++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (matched_chars == input.size()) {
|
|
|
- // All characters matched, score based on ratio and position
|
|
|
- int base_score = 40;
|
|
|
- base_score += (matched_chars * 20) / input.size();
|
|
|
- base_score += std::max(0, 20 - (int)candidate.size() + (int)input.size());
|
|
|
- return std::min(80, base_score);
|
|
|
- }
|
|
|
-
|
|
|
- return 0; // No match
|
|
|
-}
|
|
|
-
|
|
|
-bool CommandSystem::fuzzy_match(const std::string& candidate, const std::string& input) {
|
|
|
- return fuzzy_match_score(candidate, input) > 0;
|
|
|
-}
|
|
|
-
|
|
|
-void CommandSystem::register_builtin_commands() {
|
|
|
- // File operations
|
|
|
- register_command("save-buffer", "Save current buffer to file",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- if (core_.buffer().save()) {
|
|
|
- return CommandResult(true, "Buffer saved: " + core_.buffer().name());
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Failed to save buffer");
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- register_command("find-file", "Open a file",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- core_.enter_find_file_mode();
|
|
|
- return CommandResult(true, "Find file mode activated");
|
|
|
- } else {
|
|
|
- if (core_.load_file(args[0])) {
|
|
|
- return CommandResult(true, "Loaded: " + args[0]);
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Failed to load: " + args[0]);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // Buffer operations
|
|
|
- register_command("switch-to-buffer", "Switch to buffer",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- core_.enter_buffer_switch_mode();
|
|
|
- return CommandResult(true, "Buffer switch mode activated");
|
|
|
- } else {
|
|
|
- if (core_.switch_buffer_in_window(args[0])) {
|
|
|
- return CommandResult(true, "Switched to: " + args[0]);
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Buffer not found: " + args[0]);
|
|
|
- }
|
|
|
- }
|
|
|
- }, {"switch-buffer"});
|
|
|
-
|
|
|
- register_command("kill-buffer", "Close buffer",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- core_.enter_kill_buffer_mode();
|
|
|
- return CommandResult(true, "Kill buffer mode activated");
|
|
|
- } else {
|
|
|
- if (core_.close_buffer(args[0])) {
|
|
|
- return CommandResult(true, "Killed buffer: " + args[0]);
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Cannot kill buffer: " + args[0]);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- register_command("list-buffers", "List all open buffers",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- auto buffer_info = core_.get_all_buffer_info();
|
|
|
- if (buffer_info.empty()) {
|
|
|
- return CommandResult(true, "No buffers open");
|
|
|
- }
|
|
|
-
|
|
|
- std::ostringstream ss;
|
|
|
- ss << "Open buffers (" << buffer_info.size() << "):\n";
|
|
|
- for (const auto& info : buffer_info) {
|
|
|
- ss << " " << (info.modified ? "*" : " ") << info.name
|
|
|
- << " (" << info.size << " chars)";
|
|
|
- if (info.filepath) {
|
|
|
- ss << " - " << info.filepath->string();
|
|
|
- }
|
|
|
- ss << "\n";
|
|
|
- }
|
|
|
-
|
|
|
- return CommandResult(true, ss.str());
|
|
|
- });
|
|
|
-
|
|
|
- // Navigation
|
|
|
- register_command("goto-line", "Go to line number",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- return CommandResult(false, "Line number required");
|
|
|
- }
|
|
|
- try {
|
|
|
- size_t line = std::stoull(args[0]);
|
|
|
- core_.goto_line(line - 1); // Convert to 0-based
|
|
|
- return CommandResult(true, "Moved to line " + args[0]);
|
|
|
- } catch (...) {
|
|
|
- return CommandResult(false, "Invalid line number: " + args[0]);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // Window management
|
|
|
- register_command("split-window-below", "Split window horizontally",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- core_.split_horizontally();
|
|
|
- return CommandResult(true, "Window split horizontally");
|
|
|
- });
|
|
|
-
|
|
|
- register_command("split-window-right", "Split window vertically",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- core_.split_vertically();
|
|
|
- return CommandResult(true, "Window split vertically");
|
|
|
- });
|
|
|
-
|
|
|
- register_command("delete-window", "Close current window",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- core_.close_active_window();
|
|
|
- return CommandResult(true, "Window closed");
|
|
|
- });
|
|
|
-
|
|
|
- register_command("other-window", "Switch to next window",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- core_.next_window_safe();
|
|
|
- return CommandResult(true, "Switched to other window");
|
|
|
- });
|
|
|
-
|
|
|
- // Editing
|
|
|
- register_command("undo", "Undo last change",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- if (core_.undo()) {
|
|
|
- return CommandResult(true, "Undid last change");
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Nothing to undo");
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- register_command("redo", "Redo last undo",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- if (core_.redo()) {
|
|
|
- return CommandResult(true, "Redid last change");
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Nothing to redo");
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // Theme management
|
|
|
- register_command("list-themes", "List all available themes",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- auto theme_names = core_.theme_manager().theme_names();
|
|
|
- if (theme_names.empty()) {
|
|
|
- return CommandResult(true, "No themes available");
|
|
|
- }
|
|
|
-
|
|
|
- std::ostringstream ss;
|
|
|
- ss << "Available themes (" << theme_names.size() << "):\n";
|
|
|
- auto current_theme = core_.active_theme();
|
|
|
- std::string current_name = current_theme ? current_theme->name() : "none";
|
|
|
-
|
|
|
- for (const auto& name : theme_names) {
|
|
|
- ss << " " << (name == current_name ? "* " : " ") << name << "\n";
|
|
|
- }
|
|
|
-
|
|
|
- return CommandResult(true, ss.str());
|
|
|
- });
|
|
|
-
|
|
|
- register_command("set-theme", "Switch to a theme",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- core_.enter_theme_selection_mode();
|
|
|
- return CommandResult(true, "Theme selection mode activated");
|
|
|
- } else {
|
|
|
- const std::string& theme_name = args[0];
|
|
|
- auto theme_names = core_.theme_manager().theme_names();
|
|
|
- auto it = std::find(theme_names.begin(), theme_names.end(), theme_name);
|
|
|
-
|
|
|
- if (it == theme_names.end()) {
|
|
|
- return CommandResult(false, "Theme not found: " + theme_name);
|
|
|
- }
|
|
|
-
|
|
|
- core_.set_theme(theme_name);
|
|
|
- return CommandResult(true, "Switched to theme: " + theme_name);
|
|
|
- }
|
|
|
- }, {"switch-theme", "theme"});
|
|
|
-
|
|
|
- register_command("reload-themes", "Reload all themes",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- // Clear and recreate default themes
|
|
|
- core_.theme_manager().create_default_themes();
|
|
|
-
|
|
|
- // Reload themes.lua if available
|
|
|
- if (core_.lua_api()) {
|
|
|
- std::filesystem::path themes_file = std::filesystem::current_path() / "themes.lua";
|
|
|
- if (std::filesystem::exists(themes_file)) {
|
|
|
- core_.lua_api()->load_file(themes_file);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return CommandResult(true, "Themes reloaded");
|
|
|
- });
|
|
|
-
|
|
|
- // System
|
|
|
- register_command("quit", "Quit editor",
|
|
|
- [this](const std::vector<std::string>&) -> CommandResult {
|
|
|
- core_.request_quit();
|
|
|
- return CommandResult(true, "Quitting...");
|
|
|
- }, {"q", "exit"});
|
|
|
-
|
|
|
- register_command("eval", "Evaluate Lua code",
|
|
|
- [this](const std::vector<std::string>& args) -> CommandResult {
|
|
|
- if (args.empty()) {
|
|
|
- return CommandResult(false, "Lua code required");
|
|
|
- }
|
|
|
-
|
|
|
- std::string code = args[0];
|
|
|
- for (size_t i = 1; i < args.size(); ++i) {
|
|
|
- code += " " + args[i];
|
|
|
- }
|
|
|
-
|
|
|
- if (core_.lua_api() && core_.lua_api()->execute(code)) {
|
|
|
- return CommandResult(true, "Lua code executed");
|
|
|
- } else {
|
|
|
- return CommandResult(false, "Lua code execution failed");
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-std::vector<std::string> CommandSystem::parse_command_string(const std::string& command_string) const {
|
|
|
- std::vector<std::string> parts;
|
|
|
- std::istringstream iss(command_string);
|
|
|
- std::string part;
|
|
|
-
|
|
|
- while (iss >> std::quoted(part) || iss >> part) {
|
|
|
- parts.push_back(part);
|
|
|
+std::optional<std::string> CommandSystem::get_command_doc_string(const std::string& name) const {
|
|
|
+ auto it = commands_.find(name);
|
|
|
+ if (it != commands_.end()) {
|
|
|
+ return it->second.doc_string;
|
|
|
}
|
|
|
-
|
|
|
- return parts;
|
|
|
+ return std::nullopt;
|
|
|
}
|
|
|
|
|
|
-} // namespace lumacs
|
|
|
+} // namespace lumacs
|