| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073 |
- #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_;
- };
- CommandFunction LuaApi::wrap_lua_callback(sol::function callback, const std::string& error_context) {
- return [callback, error_context, this](CommandContext& context) -> CommandResult {
- try {
- // Pass args from context to Lua function
- sol::table args_table = get_lua_state().create_table();
- const auto& args = context.get_args();
- 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;
- CommandStatus status = CommandStatus::Success;
- std::string message;
- if (res_table["success"].valid()) {
- status = res_table["success"].get<bool>() ? CommandStatus::Success : CommandStatus::Failure;
- }
- if (res_table["message"].valid()) {
- message = res_table["message"];
- }
- return CommandResult{status, message};
- } else if (result.get_type() == sol::type::string) {
- return CommandResult{CommandStatus::Success, result.get<std::string>()};
- } else {
- return CommandResult{CommandStatus::Success, ""};
- }
- } else {
- return CommandResult{CommandStatus::Success, ""};
- }
- } catch (const sol::error& e) {
- return CommandResult{CommandStatus::Failure, "Lua error in " + error_context + ": " + std::string(e.what())};
- }
- };
- }
- 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
- std::string command_name = "lua_cmd_" + key;
- // Register a C++ CommandFunction that wraps the Lua callback
- core_->command_system().register_command(
- command_name,
- wrap_lua_callback(callback, "key binding '" + key + "'"),
- description,
- false, // Not interactive directly via spec
- "" // No interactive spec
- );
- // 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, const std::string& severity_str) {
- if (!core_) {
- spdlog::warn("lua_editor_set_message: Core is null!");
- return;
- }
- // Parse severity string
- MessageSeverity severity = MessageSeverity::Info;
- if (severity_str == "warning" || severity_str == "warn") {
- severity = MessageSeverity::Warning;
- } else if (severity_str == "error") {
- severity = MessageSeverity::Error;
- } else if (severity_str == "debug") {
- severity = MessageSeverity::Debug;
- }
- core_->set_message(message, severity);
- }
- 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::lua_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,
- "from_hex", &Color::from_hex
- );
- // 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.set_theme(theme_name); }, // Corrected
- "active_theme", sol::property([](EditorCore& e) -> const Theme& { return *e.theme_manager().active_theme(); }), // Corrected
- "message", [](EditorCore& e, const std::string& msg, sol::optional<std::string> severity_str) {
- MessageSeverity severity = MessageSeverity::Info;
- if (severity_str) {
- const std::string& s = *severity_str;
- if (s == "warning" || s == "warn") severity = MessageSeverity::Warning;
- else if (s == "error") severity = MessageSeverity::Error;
- else if (s == "debug") severity = MessageSeverity::Debug;
- }
- e.set_message(msg, severity);
- }, // Message with optional severity
- // Message history navigation
- "previous_message", &EditorCore::previous_message,
- "next_message", &EditorCore::next_message,
- "exit_message_history", &EditorCore::exit_message_history,
- "is_browsing_message_history", &EditorCore::is_browsing_message_history,
- "message_history_size", &EditorCore::message_history_size,
- // Key binding information
- "get_all_bindings", [this](EditorCore& core) {
- sol::table bindings = lua_.create_table();
- auto all = core.keybinding_manager().get_all_bindings();
- int idx = 1;
- for (const auto& binding : all) {
- sol::table b = lua_.create_table();
- b["sequence"] = binding.sequence.to_string();
- b["command"] = binding.command_name;
- b["description"] = binding.description;
- bindings[idx++] = b;
- }
- return bindings;
- },
- "is_building_sequence", [](EditorCore& core) {
- return core.keybinding_manager().is_building_sequence();
- },
- "current_sequence", [](EditorCore& core) {
- return core.keybinding_manager().current_sequence_display();
- },
- "has_prefix_bindings", [](EditorCore& core, const std::string& prefix) {
- return core.keybinding_manager().has_prefix_bindings(KeySequence(prefix));
- },
- // Extended echo area (for packages like which-key)
- "set_echo_area", [](EditorCore& core, sol::table lines_table) {
- std::vector<std::string> lines;
- for (size_t i = 1; i <= lines_table.size(); ++i) {
- sol::optional<std::string> line = lines_table[i];
- if (line) {
- lines.push_back(*line);
- }
- }
- core.set_echo_area_lines(lines);
- },
- "clear_echo_area", [](EditorCore& core) {
- core.clear_echo_area_lines();
- },
- "echo_area_lines", [this](EditorCore& core) {
- sol::table result = lua_.create_table();
- const auto& lines = core.echo_area_lines();
- for (size_t i = 0; i < lines.size(); ++i) {
- result[i + 1] = lines[i];
- }
- return result;
- },
- // Idle time tracking (for packages like which-key)
- "idle_time_ms", [](EditorCore& core) {
- return core.idle_time_ms();
- },
- "notify_activity", [](EditorCore& core) {
- core.notify_user_activity();
- },
- // 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;
- sol::function callback = callback_or_cmd.as<sol::function>();
- // Register using the helper function
- core.command_system().register_command(
- command_name,
- wrap_lua_callback(callback, "key binding '" + key + "'"),
- description.value_or(""),
- false, // Not interactive
- "" // No interactive spec
- );
- 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>());
- }
- }
- core.command_system().register_command(
- name,
- wrap_lua_callback(func, "command '" + name + "'"),
- 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, sol::optional<std::string> severity) { // Use set_function
- this->lua_editor_set_message(message, severity.value_or("info"));
- });
- lua_.set_function("lumacs_editor_previous_message", [this]() {
- core_->previous_message();
- });
- lua_.set_function("lumacs_editor_next_message", [this]() {
- core_->next_message();
- });
- lua_.set_function("lumacs_editor_exit_message_history", [this]() {
- core_->exit_message_history();
- });
- 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;
- }
- )
- );
- }
- void LuaApi::call_idle_hooks() {
- // Call the which-key idle check if registered
- try {
- sol::optional<sol::function> which_key_check = lua_["lumacs"]["which_key_check_idle"];
- if (which_key_check) {
- (*which_key_check)();
- }
- } catch (const sol::error& e) {
- spdlog::error("Error in idle hook: {}", e.what());
- }
- }
- } // namespace lumacs
|