| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- #include "lumacs/lua_api.hpp"
- #include "lumacs/command_system.hpp"
- #include "lumacs/minibuffer_mode.hpp" // Added for MinibufferMode enum binding
- #include "lumacs/completion_system.hpp" // Added for CompletionSystem access
- #include "lumacs/plugin_manager.hpp" // Added for PluginManager access
- #include "lumacs/buffer_manager.hpp" // Added for BufferManager::BufferInfo
- #include "lumacs/logger.hpp"
- #include "../src/defaults.hpp" // Include embedded defaults
- #include <spdlog/spdlog.h>
- #include <iostream> // Kept for now if needed by sol2 internals or other parts, but trying to remove direct usage
- #include <fstream>
- #include <filesystem> // For std::filesystem::path
- namespace lumacs {
- // Helper class to adapt a lambda/function to ICompletionSource
- class LambdaCompletionSource : public ICompletionSource {
- public:
- explicit LambdaCompletionSource(sol::function provider) : provider_(std::move(provider)) {}
- std::vector<CompletionCandidate> get_candidates(const std::string& input) override {
- sol::protected_function_result lua_result = provider_(input); // Get the raw Lua result
- std::vector<CompletionCandidate> candidates;
- if (lua_result.valid()) {
- sol::type result_type = lua_result.get_type();
- if (result_type == sol::type::table) {
- sol::table result_table = lua_result; // Convert to sol::table
- for (auto& pair : result_table) {
- if (pair.second.get_type() == sol::type::table) {
- sol::table candidate_table = pair.second;
- std::string text = "";
- int score = 50;
- std::string desc = "";
-
- if (candidate_table["text"].valid()) {
- text = candidate_table["text"];
- }
- if (candidate_table["score"].valid()) {
- score = candidate_table["score"];
- }
- if (candidate_table["description"].valid()) {
- desc = candidate_table["description"].get<std::string>(); // Get the string
- }
-
- candidates.emplace_back(text, score, "", desc); // Fix: Explicitly pass empty string for display_text
- }
- }
- }
- }
- return candidates;
- }
- private:
- sol::function provider_;
- };
- LuaApi::LuaApi() {
- lua_.open_libraries(
- sol::lib::base,
- sol::lib::package,
- sol::lib::string,
- sol::lib::math,
- sol::lib::table,
- sol::lib::io,
- sol::lib::os,
- sol::lib::debug // Open debug library early for MobDebug
- );
- }
- void LuaApi::set_core(EditorCore& core) {
- core_ = &core;
- setup_api();
- }
- bool LuaApi::load_file(const std::filesystem::path& path) {
- try {
- spdlog::debug("Loading Lua file: {}", path.string());
- lua_.script_file(path.string());
- spdlog::debug("Lua file loaded successfully");
- spdlog::info("Loaded Lua file: {}", path.string());
- return true;
- } catch (const sol::error& e) {
- spdlog::error("Lua error loading {}: {}", path.string(), e.what());
- return false;
- } catch (const std::exception& e) {
- spdlog::error("Exception loading {}: {}", path.string(), e.what());
- return false;
- }
- }
- bool LuaApi::execute(std::string_view code) {
- try {
- lua_.script(code);
- return true;
- } catch (const sol::error& e) {
- spdlog::error("Lua error: {}", e.what());
- return false;
- }
- }
- bool LuaApi::load_init_file() {
- // 1. ALWAYS load the embedded defaults first
- spdlog::info("Loading embedded defaults...");
- try {
- lua_.script(LUA_DEFAULTS);
- } catch (const sol::error& e) {
- spdlog::critical("FAILED TO LOAD EMBEDDED DEFAULTS: {}", e.what());
- // Continue anyway
- }
- // Get home directory
- const char* home = getenv("HOME");
- std::string home_dir = home ? home : ".";
- // Try multiple locations in order
- std::vector<std::filesystem::path> search_paths = {
- std::filesystem::current_path() / "init.lua",
- std::filesystem::path(home_dir) / ".config" / "lumacs" / "init.lua",
-
- std::filesystem::path(home_dir) / ".lumacs" / "init.lua",
- };
- for (const auto& path : search_paths) {
- if (std::filesystem::exists(path)) {
- spdlog::info("Found init file: {}", path.string());
- return load_file(path);
- }
- }
- spdlog::warn("No init.lua found (searched: ./init.lua, ~/.config/lumacs/init.lua, ~/.lumacs/init.lua). Using defaults only.");
- return true; // Return true because defaults were loaded
- }
- void LuaApi::bind_key(std::string key, sol::function callback, std::string description) {
- spdlog::debug("Registering key binding: {}", key);
-
- // Create a unique command name for the Lua function
- // This is a simple way; a more robust system might handle conflicts or namespaces
- std::string command_name = "lua_cmd_" + key; // Use key itself as part of name
- // Register a C++ CommandFunction that wraps the Lua callback
- core_->command_system().register_command(command_name,
- [callback, this](CommandContext& context) -> CommandResult { // Added 'this' to capture
- try {
- // Pass args from context to Lua function
- sol::table args_table = get_lua_state().create_table(); // Use get_lua_state()
- const auto& args = context.get_args(); // Use getter
- for (size_t i = 0; i < args.size(); ++i) {
- args_table[i + 1] = args[i];
- }
- auto result = callback(args_table);
- if (result.valid()) {
- if (result.get_type() == sol::type::table) {
- sol::table res_table = result;
- lumacs::CommandStatus status = lumacs::CommandStatus::Success; // Namespace fix
- std::string message = "";
-
- if (res_table["success"].valid()) {
- status = res_table["success"].get<bool>() ? lumacs::CommandStatus::Success : lumacs::CommandStatus::Failure;
- }
- if (res_table["message"].valid()) {
- message = res_table["message"];
- }
- return CommandResult{status, message};
- } else if (result.get_type() == sol::type::string) {
- std::string message = result.get<std::string>();
- return CommandResult{lumacs::CommandStatus::Success, message};
- } else {
- return CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } else {
- return CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } catch (const sol::error& e) {
- return CommandResult{lumacs::CommandStatus::Failure, "Lua error in key binding callback: " + std::string(e.what())};
- }
- },
- description, // Use original description
- false, // Not interactive directly via spec, Lua callback handles interactivity
- "" // No interactive spec for this wrapper command
- );
- // Now bind the key to the newly registered C++ command's name
- core_->keybinding_manager().bind(KeySequence(key), command_name, description);
- }
- KeyProcessingResult LuaApi::process_key(const std::string& key) { // Corrected return type
- return core_->keybinding_manager().process_key(key);
- }
- // Legacy methods for backward compatibility
- bool LuaApi::has_key_binding(const std::string& key) const {
- return legacy_key_bindings_.find(key) != legacy_key_bindings_.end() ||
- core_->keybinding_manager().has_exact_binding(KeySequence(key));
- }
- bool LuaApi::execute_key_binding(const std::string& key) {
- auto it = legacy_key_bindings_.find(key);
- if (it == legacy_key_bindings_.end()) {
- return false;
- }
- try {
- it->second();
- return true;
- } catch (const sol::error& e) {
- spdlog::error("Lua error executing key binding '{}': {}", key, e.what());
- return false;
- }
- }
- // Manual C functions implementations
- int LuaApi::get_active_buffer_line_count_lua() {
- if (!core_ || !core_->active_window() || !core_->active_window()->buffer_ptr()) {
- spdlog::warn("get_active_buffer_line_count_lua: Core, active window or buffer is null!");
- return 0;
- }
- return static_cast<int>(core_->buffer().line_count());
- }
- void LuaApi::lua_editor_move_right() {
- if (!core_) {
- spdlog::warn("lua_editor_move_right: Core is null!");
- return;
- }
- core_->move_right();
- }
- void LuaApi::lua_editor_new_buffer(const std::string& name) {
- if (!core_) {
- spdlog::warn("lua_editor_new_buffer: Core is null!");
- return;
- }
- core_->new_buffer(name);
- }
- sol::optional<std::string> LuaApi::lua_editor_get_buffer_by_name(const std::string& name) {
- if (!core_) {
- spdlog::warn("lua_editor_get_buffer_by_name: Core is null!");
- return sol::nullopt;
- }
- if (auto buffer = core_->get_buffer_by_name(name)) {
- return buffer->name(); // Return name as a string to avoid complex Buffer usertype issues
- }
- return sol::nullopt;
- }
- void LuaApi::lua_editor_set_message(const std::string& message) {
- if (!core_) {
- spdlog::warn("lua_editor_set_message: Core is null!");
- return;
- }
- core_->set_message(message);
- }
- void LuaApi::lua_config_set_string(const std::string& key, const std::string& value) {
- if (!core_) {
- spdlog::warn("lua_config_set_string: Core is null!");
- return;
- }
- core_->config().set(key, value);
- }
- std::string LuaApi::lua_config_get_string(const std::string& key, const std::string& default_val) {
- if (!core_) {
- spdlog::warn("lua_config_get_string: Core is null!");
- return default_val;
- }
- return core_->config().get<std::string>(key, default_val);
- }
- void LuaApi::lua_config_set_bool(const std::string& key, bool value) {
- if (!core_) {
- spdlog::warn("lua_config_set_bool: Core is null!");
- return;
- }
- core_->config().set(key, value);
- }
- bool LuaApi::lua_config_get_bool(const std::string& key, bool default_val) {
- if (!core_) {
- spdlog::warn("lua_config_get_bool: Core is null!");
- return default_val;
- }
- return core_->config().get<bool>(key, default_val);
- }
- void LuaApi::lua_config_set_int(const std::string& key, int value) {
- if (!core_) {
- spdlog::warn("lua_config_set_int: Core is null!");
- return;
- }
- core_->config().set(key, value);
- }
- int LuaApi::lua_config_get_int(const std::string& key, int default_val) {
- if (!core_) {
- spdlog::warn("lua_config_get_int: Core is null!");
- return default_val;
- }
- return core_->config().get<int>(key, default_val);
- }
- // New: Implement lua_create_and_register_theme
- sol::object LuaApi::lua_create_and_register_theme(const std::string& name) {
- if (!core_) {
- spdlog::warn("lua_create_and_register_theme: Core is null!");
- return sol::nil; // Return nil in Lua if core is not available
- }
- // Create a new Theme shared_ptr
- auto new_theme = std::make_shared<Theme>(name);
- // Register it with the ThemeManager
- core_->theme_manager().register_theme(new_theme);
- spdlog::info("Registered new theme: {}", name);
- // Return the new_theme as a sol::object. Sol2 will automatically handle the shared_ptr and usertype conversion.
- // The Theme usertype is defined in register_types(), so we need access to the lua_ state.
- // We pass a reference to the shared_ptr so that the Lua object holds a reference, preventing early destruction.
- return sol::make_object(lua_, new_theme);
- }
- void LuaApi::setup_api() {
- register_types();
- register_functions();
- // Set version info and add constructor functions to lumacs namespace
- auto lumacs_table = lua_.create_table();
- lumacs_table["version"] = "0.1.0";
- lumacs_table["name"] = "Lumacs";
- // Add constructor functions
- lumacs_table["Position"] = [](size_t line, size_t column) {
- return Position{line, column};
- };
- lumacs_table["Range"] = [](Position start, Position end) {
- return Range{start, end};
- };
- lumacs_table["Color"] = [](int r, int g, int b) { // Explicitly define Color constructor
- return Color(r, g, b);
- };
- lumacs_table["TextAttribute"] = [](lumacs::TextAttribute::ColorType color, int style) {
- return TextAttribute{color, style};
- };
- lumacs_table["FaceAttributes"] = []() { return FaceAttributes(); }; // Explicitly define FaceAttributes constructor
- lumacs_table["StyledRange"] = [](Range range, TextAttribute attr) {
- return StyledRange{range, attr};
- };
- // Add enums to lumacs namespace for convenience
- lumacs_table["ColorType"] = lua_["ColorType"];
- lumacs_table["Style"] = lua_["Style"];
- lumacs_table["BufferEvent"] = lua_["BufferEvent"];
- lumacs_table["ThemeElement"] = lua_["ThemeElement"];
- lumacs_table["FontWeight"] = lua_["FontWeight"];
- lumacs_table["FontSlant"] = lua_["FontSlant"];
- // lumacs_table["FaceAttributes"] = lua_["FaceAttributes"]; // Not needed here if constructor is explicit
- lumacs_table["MinibufferMode"] = lua_["MinibufferMode"]; // Expose MinibufferMode to Lua
-
- lua_["lumacs"] = lumacs_table;
- // Initialize MobDebug if enabled
- if (core_->config().get<bool>("debug_lua", false)) { // Corrected Config access
- spdlog::debug("Lua debugging enabled.");
- std::string debug_host = core_->config().get<std::string>("debug_host", "127.0.0.1"); // Corrected Config access
- int debug_port = core_->config().get<int>("debug_port", 8171); // Corrected Config access
- try {
- // MobDebug requires socket library, which is already opened in constructor.
- // Explicitly require 'mobdebug' module
- sol::optional<sol::table> mobdebug = lua_["require"]("mobdebug");
- if (mobdebug) {
- spdlog::debug("MobDebug module loaded.");
- sol::function mobdebug_start = mobdebug.value()["start"];
- if (mobdebug_start.valid()) {
- mobdebug_start(debug_host, debug_port);
- spdlog::info("MobDebug started on {}:{}", debug_host, debug_port);
- } else {
- spdlog::error("MobDebug.start function not found.");
- }
- } else {
- spdlog::error("Failed to load MobDebug module. Is LuaRocks installed and configured?");
- }
- } catch (const sol::error& e) {
- spdlog::error("Lua error during MobDebug initialization: {}", e.what());
- }
- }
- }
- void LuaApi::register_types() {
- // Position type
- lua_.new_usertype<Position>("Position",
- sol::constructors<Position(), Position(size_t, size_t)>(),
- "line", &Position::line,
- "column", &Position::column
- );
- // Range type
- lua_.new_usertype<Range>("Range",
- sol::constructors<Range(), Range(Position, Position)>(),
- "start", &Range::start,
- "end", &Range::end
- );
- // Font weight
- lua_.new_enum<FontWeight>("FontWeight",
- {
- {"Normal", FontWeight::Normal},
- {"Bold", FontWeight::Bold},
- {"Light", FontWeight::Light}
- }
- );
- // Font slant
- lua_.new_enum<FontSlant>("FontSlant",
- {
- {"Normal", FontSlant::Normal},
- {"Italic", FontSlant::Italic},
- {"Oblique", FontSlant::Oblique}
- }
- );
- // FaceAttributes type
- lua_.new_usertype<FaceAttributes>("FaceAttributes",
- sol::constructors<FaceAttributes()>(),
- "family", &FaceAttributes::family,
- "height", &FaceAttributes::height,
- "weight", &FaceAttributes::weight,
- "slant", &FaceAttributes::slant,
- "foreground", &FaceAttributes::foreground,
- "background", &FaceAttributes::background,
- "underline", &FaceAttributes::underline,
- "inverse", &FaceAttributes::inverse,
- "inherit", &FaceAttributes::inherit
- );
- // TextAttribute type
- lua_.new_usertype<TextAttribute>("TextAttribute",
- sol::constructors<TextAttribute(), TextAttribute(std::string), TextAttribute(lumacs::TextAttribute::ColorType, int)>(), // Namespace fix
- "face_name", &TextAttribute::face_name
- );
- // TextAttribute::ColorType enum
- lua_.new_enum<TextAttribute::ColorType>("ColorType",
- {
- {"Default", TextAttribute::ColorType::Default},
- {"Keyword", TextAttribute::ColorType::Keyword},
- {"String", TextAttribute::ColorType::String},
- {"Comment", TextAttribute::ColorType::Comment},
- {"Function", TextAttribute::ColorType::Function},
- {"Type", TextAttribute::ColorType::Type},
- {"Number", TextAttribute::ColorType::Number},
- {"Operator", TextAttribute::ColorType::Operator},
- {"Variable", TextAttribute::ColorType::Variable},
- {"Constant", TextAttribute::ColorType::Constant},
- {"Error", TextAttribute::ColorType::Error}
- }
- );
- // TextAttribute::Style enum
- lua_.new_enum<TextAttribute::Style>("Style",
- {
- {"Normal", TextAttribute::Style::Normal},
- {"Bold", TextAttribute::Style::Bold},
- {"Italic", TextAttribute::Style::Italic},
- {"Underline", TextAttribute::Style::Underline}
- }
- );
- // BufferEvent enum
- lua_.new_enum<BufferEvent>("BufferEvent",
- {
- {"Created", BufferEvent::Created},
- {"Loaded", BufferEvent::Loaded},
- {"Closed", BufferEvent::Closed},
- {"BeforeChange", BufferEvent::BeforeChange},
- {"AfterChange", BufferEvent::AfterChange},
- {"LineChanged", BufferEvent::LineChanged},
- {"BeforeSave", BufferEvent::BeforeSave},
- {"AfterSave", BufferEvent::AfterSave},
- {"LanguageChanged", BufferEvent::LanguageChanged}
- }
- );
- // BufferEventData type
- lua_.new_usertype<BufferEventData>("BufferEventData",
- sol::no_constructor,
- "event", &BufferEventData::event,
- "line", &BufferEventData::line,
- "language", &BufferEventData::language
- );
- // StyledRange type
- lua_.new_usertype<StyledRange>("StyledRange",
- sol::constructors<StyledRange(), StyledRange(Range, TextAttribute)>(),
- "range", &StyledRange::range,
- "attr", &StyledRange::attr
- );
- // BufferInfo type
- lua_.new_usertype<BufferManager::BufferInfo>("BufferInfo", // Corrected
- sol::no_constructor,
- "name", &BufferManager::BufferInfo::name, // Corrected
- "size", &BufferManager::BufferInfo::size, // Corrected
- "modified", &BufferManager::BufferInfo::modified, // Corrected
- "mode", &BufferManager::BufferInfo::mode, // Corrected
- "filepath", sol::property([](const BufferManager::BufferInfo& info) -> sol::optional<std::string> { // Corrected
- if (info.filepath.has_value()) {
- return info.filepath->string();
- }
- return sol::nullopt;
- })
- );
- // Color type
- lua_.new_usertype<Color>("Color",
- sol::constructors<Color(), Color(int, int, int)>(),
- "r", &Color::r,
- "g", &Color::g,
- "b", &Color::b
- );
- // ThemeElement enum
- lua_.new_enum<ThemeElement>("ThemeElement",
- {
- {"Normal", ThemeElement::Normal},
- {"Keyword", ThemeElement::Keyword},
- {"String", ThemeElement::String},
- {"Comment", ThemeElement::Comment},
- {"Function", ThemeElement::Function},
- {"Type", ThemeElement::Type},
- {"Number", ThemeElement::Number},
- {"Constant", ThemeElement::Constant},
- {"Error", ThemeElement::Error},
- {"StatusLine", ThemeElement::StatusLine},
- {"StatusLineInactive", ThemeElement::StatusLineInactive},
- {"MessageLine", ThemeElement::MessageLine},
- {"LineNumber", ThemeElement::LineNumber},
- {"Cursor", ThemeElement::Cursor},
- {"Selection", ThemeElement::Selection},
- {"SearchMatch", ThemeElement::SearchMatch},
- {"SearchFail", ThemeElement::SearchFail}, // Corrected
- {"WindowBorder", ThemeElement::WindowBorder},
- {"WindowBorderActive", ThemeElement::WindowBorderActive},
- {"Background", ThemeElement::Background}
- }
- );
- // Theme type
- lua_.new_usertype<Theme>("Theme",
- sol::no_constructor,
- "set_color", [](Theme& theme, ThemeElement element, Color fg, sol::optional<Color> bg) {
- Color bg_color = bg.value_or(Color(-1, -1, -1));
- theme.set_color(element, fg, bg_color);
- },
- "set_face", &Theme::set_face,
- "get_fg_color", &Theme::get_fg_color,
- "get_bg_color", &Theme::get_bg_color,
- "name", &Theme::name
- );
- // ThemeManager type
- lua_.new_usertype<ThemeManager>("ThemeManager",
- sol::no_constructor,
- "set_active_theme", &ThemeManager::set_active_theme,
- "theme_names", &ThemeManager::theme_names,
- "active_theme", &ThemeManager::active_theme
- );
- // Config type
- lua_.new_usertype<Config>("Config",
- sol::no_constructor,
- "set", [](Config& config, const std::string& key, sol::object value) {
- if (value.is<bool>()) {
- config.set(key, value.as<bool>());
- } else if (value.is<int>()) {
- config.set(key, value.as<int>());
- } else if (value.is<std::string>()) {
- config.set(key, value.as<std::string>());
- }
- },
- "get_bool", [](const Config& config, const std::string& key, sol::optional<bool> default_val) {
- return config.get<bool>(key, default_val.value_or(false));
- },
- "get_int", [](const Config& config, const std::string& key, sol::optional<int> default_val) {
- return config.get<int>(key, default_val.value_or(0));
- },
- "get_string", [](const Config& config, const std::string& key, sol::optional<std::string> default_val) {
- return config.get<std::string>(key, default_val.value_or(""));
- },
- "has", &Config::has,
- "keys", &Config::keys
- );
- // MinibufferMode enum
- lua_.new_enum<MinibufferMode>("MinibufferMode", {
- {"None", MinibufferMode::None},
- {"Command", MinibufferMode::Command},
- {"FilePath", MinibufferMode::FilePath},
- {"BufferName", MinibufferMode::BufferName},
- {"ThemeName", MinibufferMode::ThemeName},
- {"ISearch", MinibufferMode::ISearch},
- {"ystring", MinibufferMode::ystring},
- {"y_or_n_p", MinibufferMode::y_or_n_p}
- });
- // Buffer type
- lua_.new_usertype<Buffer>("Buffer",
- sol::no_constructor,
- "name", &Buffer::name,
- "set_name", &Buffer::set_name,
- "filepath", [](const Buffer& b) -> sol::optional<std::string> {
- auto path = b.file_path();
- if (!path.has_value()) return sol::nullopt;
- return path->string();
- },
- "line_count", &Buffer::line_count,
- "is_modified", &Buffer::is_modified,
- "line", [](Buffer& b, size_t idx) {
- if (idx >= b.line_count()) return std::string("");
- return b.line(idx);
- },
- "content", &Buffer::content,
- "insert", &Buffer::insert,
- "insert_newline", &Buffer::insert_newline,
- "erase", &Buffer::erase,
- "erase_char", &Buffer::erase_char,
- "clear", &Buffer::clear,
- "save", &Buffer::save,
-
- // Mark & Region
- "set_mark", &Buffer::set_mark,
- "deactivate_mark", &Buffer::deactivate_mark,
- "has_mark", [](const Buffer& b) { return b.mark().has_value(); },
- "mark", sol::property([](const Buffer& b) -> sol::optional<Position> {
- auto m = b.mark();
- if (m.has_value()) return *m;
- return sol::nullopt;
- }),
- "get_region", [](Buffer& b, Position p) -> sol::optional<Range> {
- auto r = b.get_region(p);
- if (r.has_value()) return *r;
- return sol::nullopt;
- },
- "get_text_in_range", &Buffer::get_text_in_range,
- "get_all_text", &Buffer::content, // Alias
- "replace", [](Buffer& b, Range r, const std::string& text) {
- b.erase(r);
- b.insert(r.start, text);
- },
- // Styling
- "set_style", &Buffer::set_style,
- "clear_styles", &Buffer::clear_styles,
- "get_line_styles", &Buffer::get_line_styles,
- // Events
- "on_buffer_event", [](Buffer& b, sol::function callback) {
- b.on_buffer_event([callback](const BufferEventData& data) {
- try {
- callback(data);
- } catch (const sol::error& e) {
- spdlog::error("Lua error in buffer event listener: {}", e.what());
- }
- });
- },
-
- // Search
- "find", [](Buffer& b, const std::string& query, sol::optional<Position> start_pos) -> sol::optional<Range> {
- auto r = b.find(query, start_pos.value_or(Position{0,0}));
- if (r.has_value()) return *r;
- return sol::nullopt;
- }
- );
- // EditorCore type
- lua_.new_usertype<EditorCore>("EditorCore",
- sol::no_constructor,
- "cursor", sol::property(&EditorCore::cursor, &EditorCore::set_cursor), // Added cursor property
- "set_cursor", &EditorCore::set_cursor, // Explicit setter method
-
- // Movement
- "move_up", &EditorCore::move_up,
- "move_down", &EditorCore::move_down,
- "move_left", &EditorCore::move_left,
- "move_right", &EditorCore::move_right,
- "move_to_line_start", &EditorCore::move_to_line_start,
- "move_to_line_end", &EditorCore::move_to_line_end,
- "move_forward_word", &EditorCore::move_forward_word,
- "move_backward_word", &EditorCore::move_backward_word,
- "page_up", &EditorCore::page_up,
- "page_down", &EditorCore::page_down,
- "goto_beginning", &EditorCore::goto_beginning,
- "goto_end", &EditorCore::goto_end,
- "goto_line", &EditorCore::goto_line,
- "load_file", &EditorCore::load_file,
- "split_horizontally", &EditorCore::split_horizontally,
- "split_vertically", &EditorCore::split_vertically,
- "close_window", &EditorCore::close_active_window,
- "next_window", &EditorCore::next_window, // Corrected
- "set_active_window", &EditorCore::set_active_window,
- "undo", &EditorCore::undo,
- "redo", &EditorCore::redo,
- "command_mode", &EditorCore::enter_command_mode,
- "buffer_switch_mode", &EditorCore::enter_buffer_switch_mode,
- "kill_buffer_mode", &EditorCore::enter_kill_buffer_mode,
- "find_file_mode", &EditorCore::enter_find_file_mode,
- "isearch_mode", &EditorCore::enter_isearch_mode,
- "isearch_backward_mode", &EditorCore::enter_isearch_backward_mode,
- "quit", &EditorCore::request_quit,
- // Kill ring
- "kill_line", &EditorCore::kill_line,
- "kill_region", &EditorCore::kill_region,
- "kill_word", &EditorCore::kill_word,
- "backward_kill_word", &EditorCore::backward_kill_word,
- "copy_region_as_kill", &EditorCore::copy_region_as_kill,
- "yank", &EditorCore::yank,
- "yank_pop", &EditorCore::yank_pop,
- // Registers
- "copy_to_register", &EditorCore::copy_to_register,
- "insert_register", &EditorCore::insert_register,
- "copy_region_to_register", &EditorCore::copy_region_to_register,
- "yank_from_register", &EditorCore::yank_from_register,
- // Keyboard macros
- "start_kbd_macro", &EditorCore::start_kbd_macro,
- "end_kbd_macro_or_call", &EditorCore::end_kbd_macro_or_call,
- "record_key_sequence", &EditorCore::record_key_sequence,
- "is_recording_macro", &EditorCore::is_recording_macro,
- // Rectangles
- "kill_rectangle", &EditorCore::kill_rectangle,
- "yank_rectangle", &EditorCore::yank_rectangle,
- "string_rectangle", &EditorCore::string_rectangle,
- // Buffer management
- "get_buffer_names", &EditorCore::get_buffer_names,
- "buffer", sol::property([](EditorCore& e) -> Buffer* {
- try {
- return &e.buffer();
- } catch (const std::exception& ex) {
- spdlog::error("Exception in editor.buffer: {}", ex.what());
- return nullptr;
- }
- }),
- "switch_buffer_in_window", &EditorCore::switch_buffer_in_window,
- "close_buffer", &EditorCore::close_buffer,
- "get_all_buffer_info", &EditorCore::get_all_buffer_info,
- // Theme management
- "theme_manager", sol::property([](EditorCore& e) -> ThemeManager& { return e.theme_manager(); }),
- "config", sol::property([](EditorCore& e) -> Config& { return e.config(); }), // Added config property
- "set_theme", [](EditorCore& e, const std::string& theme_name) { e.theme_manager().set_active_theme(theme_name); }, // Corrected
- "active_theme", sol::property([](EditorCore& e) -> const Theme& { return *e.theme_manager().active_theme(); }), // Corrected
- "message", &EditorCore::set_message, // Added message alias for set_message
-
- // Key binding (method on EditorCore)
- "bind_key", [this](EditorCore& core, std::string key, sol::object callback_or_cmd, sol::optional<std::string> description) {
- if (callback_or_cmd.is<std::string>()) {
- // Bind directly to an existing command name
- std::string cmd_name = callback_or_cmd.as<std::string>();
- core.keybinding_manager().bind(KeySequence(key), cmd_name, description.value_or(""));
- }
- else if (callback_or_cmd.is<sol::function>()) {
- // Create a unique command name for the Lua function
- std::string command_name = "lua_cmd_" + key; // Use key itself as part of name
- sol::function callback = callback_or_cmd.as<sol::function>();
- // Register a C++ CommandFunction that wraps the Lua callback
- core.command_system().register_command(command_name,
- [callback, this](CommandContext& context) -> lumacs::CommandResult { // Namespace fix
- try {
- // Pass args from context to Lua function
- sol::table args_table = get_lua_state().create_table(); // Use get_lua_state()
- const auto& args = context.get_args(); // Use getter
- for (size_t i = 0; i < args.size(); ++i) {
- args_table[i + 1] = args[i];
- }
- auto result = callback(args_table);
- if (result.valid()) {
- if (result.get_type() == sol::type::table) {
- sol::table res_table = result;
- lumacs::CommandStatus status = lumacs::CommandStatus::Success; // Namespace fix
- std::string message = "";
-
- if (res_table["success"].valid()) {
- status = res_table["success"].get<bool>() ? lumacs::CommandStatus::Success : lumacs::CommandStatus::Failure;
- }
- if (res_table["message"].valid()) {
- message = res_table["message"];
- }
- return lumacs::CommandResult{status, message};
- } else if (result.get_type() == sol::type::string) {
- std::string message = result.get<std::string>();
- return lumacs::CommandResult{lumacs::CommandStatus::Success, message};
- } else {
- return lumacs::CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } else {
- return lumacs::CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } catch (const sol::error& e) {
- return lumacs::CommandResult{lumacs::CommandStatus::Failure, "Lua error in key binding callback: " + std::string(e.what())};
- }
- },
- description.value_or(""), // Use original description
- false, // Not interactive directly via spec, Lua callback handles interactivity
- "" // No interactive spec for this wrapper command
- );
- // Now bind the key to the newly registered C++ command's name
- core.keybinding_manager().bind(KeySequence(key), command_name, description.value_or(""));
- }
- else {
- spdlog::error("bind_key: Invalid argument type for callback. Expected string or function.");
- }
- },
- // Register command (method on EditorCore)
- "register_command", [this](EditorCore& core, std::string name, std::string description, sol::function func, sol::optional<sol::table> aliases_table, sol::optional<bool> interactive, sol::optional<std::string> interactive_spec) {
- std::vector<std::string> aliases;
- if (aliases_table) {
- for (auto& pair : aliases_table.value()) {
- aliases.push_back(pair.second.as<std::string>());
- }
- }
-
- CommandFunction command_func = [this, func](CommandContext& context) -> lumacs::CommandResult { // Namespace fix
- try {
- // Pass args from context to Lua function
- sol::table args_table = get_lua_state().create_table(); // Use get_lua_state()
- const auto& args = context.get_args(); // Use getter
- for (size_t i = 0; i < args.size(); ++i) {
- args_table[i + 1] = args[i];
- }
- auto result = func(args_table);
- if (result.valid()) {
- if (result.get_type() == sol::type::table) {
- sol::table res_table = result;
- lumacs::CommandStatus status = lumacs::CommandStatus::Success; // Namespace fix
- std::string message = "";
-
- if (res_table["success"].valid()) {
- status = res_table["success"].get<bool>() ? lumacs::CommandStatus::Success : lumacs::CommandStatus::Failure;
- }
- if (res_table["message"].valid()) {
- message = res_table["message"];
- }
- return lumacs::CommandResult{status, message};
- } else if (result.get_type() == sol::type::string) {
- std::string message = result.get<std::string>();
- return lumacs::CommandResult{lumacs::CommandStatus::Success, message};
- } else {
- return lumacs::CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } else {
- return lumacs::CommandResult{lumacs::CommandStatus::Success, ""};
- }
- } catch (const sol::error& e) {
- return lumacs::CommandResult{lumacs::CommandStatus::Failure, "Lua error: " + std::string(e.what())};
- }
- };
-
- core.command_system().register_command(name, command_func, description, interactive.value_or(true), interactive_spec.value_or(""), aliases);
- },
- // Define face (method on EditorCore)
- "define_face", [](EditorCore& core, std::string name, const FaceAttributes& attrs) {
- if (auto theme = core.theme_manager().active_theme()) {
- theme->set_face(name, attrs);
- }
- },
- // New: create_and_register_theme method
- "create_and_register_theme", [this](EditorCore& /* core */, const std::string& name) -> sol::object {
- return this->lua_create_and_register_theme(name);
- },
- // Command system (method on EditorCore)
- "execute_command", [](EditorCore& core, std::string command_name, sol::optional<sol::table> args_table) {
- std::vector<std::string> args;
- if (args_table) {
- for (auto& pair : args_table.value()) {
- args.push_back(pair.second.as<std::string>());
- }
- }
- auto result = core.command_system().execute(command_name, args);
- return std::make_tuple((bool)(result.status == lumacs::CommandStatus::Success), result.message); // Namespace fix
- }
- );
- }
- void LuaApi::register_functions() {
- // Global editor instance
- lua_["editor"] = std::ref(*core_);
- // Print function that goes to stderr (since stdout is used by TUI)
- lua_.set_function("print", [](sol::variadic_args args) { // Use set_function
- std::string message;
- for (auto arg : args) {
- message += lua_tostring(arg.lua_state(), arg.stack_index());
- message += "\t";
- }
- spdlog::info("[Lua] {}", message);
- });
- // Command system functions
- lua_.set_function("execute_command_line", [this](std::string command_string) { // Use set_function
- auto result = core_->minibuffer_manager().parse_and_execute_command_string(command_string);
- return std::make_tuple((bool)(result.status == lumacs::CommandStatus::Success), result.message); // Namespace fix
- });
- lua_.set_function("get_command_names", [this]() { // Use set_function
- return core_->command_system().get_command_names();
- });
- // New binding for executing interactive commands from Lua
- lua_.set_function("execute_interactive_command", [this](std::string command_name) { // Use set_function
- auto result = core_->command_system().execute_interactive(command_name);
- return std::make_tuple((bool)(result.status == lumacs::CommandStatus::Success), result.message); // Namespace fix
- });
- // New binding for getting interactive spec
- lua_.set_function("get_command_interactive_spec", [this](std::string command_name) { // Use set_function
- return core_->command_system().get_command_interactive_spec(command_name);
- });
- // New manual bindings for core interactions
- lua_.set_function("lumacs_get_active_buffer_line_count", [this]() { // Use set_function
- return this->get_active_buffer_line_count_lua();
- });
- lua_.set_function("lumacs_editor_move_right", [this]() { // Use set_function
- this->lua_editor_move_right();
- });
- lua_.set_function("lumacs_editor_new_buffer", [this](const std::string& name) { // Use set_function
- this->lua_editor_new_buffer(name);
- });
- lua_.set_function("lumacs_editor_get_buffer_by_name", [this](const std::string& name) { // Use set_function
- return this->lua_editor_get_buffer_by_name(name);
- });
- lua_.set_function("lumacs_editor_set_message", [this](const std::string& message) { // Use set_function
- this->lua_editor_set_message(message);
- });
- lua_.set_function("lumacs_config_set_string", [this](const std::string& key, const std::string& value) { // Use set_function
- this->lua_config_set_string(key, value);
- });
- lua_.set_function("lumacs_config_get_string", [this](const std::string& key, const std::string& default_val) { // Use set_function
- return this->lua_config_get_string(key, default_val);
- });
- lua_.set_function("lumacs_config_set_bool", [this](const std::string& key, bool value) { // Use set_function
- this->lua_config_set_bool(key, value);
- });
- lua_.set_function("lumacs_config_get_bool", [this](const std::string& key, bool default_val) { // Use set_function
- return this->lua_config_get_bool(key, default_val);
- });
- lua_.set_function("lumacs_config_set_int", [this](const std::string& key, int value) { // Use set_function
- this->lua_config_set_int(key, value);
- });
- lua_.set_function("lumacs_config_get_int", [this](const std::string& key, int default_val) { // Use set_function
- return this->lua_config_get_int(key, default_val);
- });
- // Completion functions
- lua_.set_function("get_completion_candidates", [this](int mode_int, std::string input) { // Use set_function
- MinibufferMode mode = static_cast<MinibufferMode>(mode_int);
- auto candidates = (*core_).completion_system().get_candidates_for_mode(mode, input);
- sol::table result = lua_.create_table();
- for (size_t i = 0; i < candidates.size(); ++i) {
- sol::table candidate = lua_.create_table();
- candidate["text"] = candidates[i].text;
- candidate["score"] = candidates[i].score;
- candidate["description"] = candidates[i].description;
- result[i + 1] = candidate;
- }
- return result;
- });
- lua_.set_function("register_completion_provider", [this](int mode_int, sol::function provider_func) { // Use set_function
- MinibufferMode mode = static_cast<MinibufferMode>(mode_int);
- // Pass the sol::function directly to the LambdaCompletionSource constructor
- (*core_).completion_system().register_source(mode, std::make_unique<LambdaCompletionSource>(provider_func));
- });
- // Plugin Manager functions
- lua_["plugin_manager"] = std::ref(core_->plugin_manager()); // Expose PluginManager to Lua
- // PluginManager types
- lua_.new_usertype<PluginManager>("PluginManager",
- sol::no_constructor,
- "discover_plugins", &PluginManager::discover_plugins,
- "load_plugin", &PluginManager::load_plugin,
- "unload_plugin", &PluginManager::unload_plugin,
- "get_discovered_plugins", sol::overload(
- [] (PluginManager& pm) { return pm.get_discovered_plugins(); },
- [] (PluginManager& pm, sol::this_state s) {
- // Convert to Lua table
- sol::state_view lua(s);
- sol::table t = lua.create_table();
- auto names = pm.get_discovered_plugins();
- for(size_t i = 0; i < names.size(); ++i) {
- t[i+1] = names[i];
- }
- return t;
- }
- ),
- "get_loaded_plugins", sol::overload(
- [] (PluginManager& pm) { return pm.get_loaded_plugins(); },
- [] (PluginManager& pm, sol::this_state s) {
- // Convert to Lua table
- sol::state_view lua(s);
- sol::table t = lua.create_table();
- auto names = pm.get_loaded_plugins();
- for(size_t i = 0; i < names.size(); ++i) {
- t[i+1] = names[i];
- }
- return t;
- }
- )
- );
- }
- } // namespace lumacs
|