complition

This commit is contained in:
2026-10-30 20:50:04 +03:00
parent 56115302df
commit 55c3955897
15 changed files with 4103 additions and 32 deletions
+35 -13
View File
@@ -1,20 +1,24 @@
//! Выполнение Lua-сценариев приложения.
//!
//! При запуске приложение выполняет [`STARTUP_SCRIPT`] из каталога [`SCRIPT_DIR`]
//! При запуске приложение выполняет [`STARTUP_SCRIPT`] из каталога [`script_dir`]
//! (см. [`ScriptEngine::run_startup`]). Скриптам доступны:
//!
//! * объект `app` — управление окном и редактором (методы [`AppApi`], вызываются
//! через двоеточие: `app:next_tab()`);
//! * функция `include(path)` — выполняет другой Lua-файл относительно [`SCRIPT_DIR`]
//! * функция `include(path)` — выполняет другой Lua-файл относительно [`script_dir`]
//! (или по абсолютному пути) в общем глобальном окружении;
//! * стандартный `require`, который также ищет модули в [`SCRIPT_DIR`].
//! * стандартный `require`, который также ищет модули в [`script_dir`].
//!
//! Каталог по умолчанию — текущий рабочий каталог. Его можно переопределить
//! аргументом `--config <каталог>` при запуске приложения (см.
//! [`parse_command_line`](crate::constants::parse_command_line)).
use std::path::{Path, PathBuf};
use mlua::{Lua, Table};
use crate::api::{AppApi, TabInfo};
use crate::constants::{SCRIPT_DIR, STARTUP_SCRIPT};
use crate::constants::{STARTUP_SCRIPT, script_dir};
/// Вкладка превращается в Lua-таблицу `{ index = 0, title = "main.rs" }`.
impl mlua::IntoLua for TabInfo {
@@ -29,24 +33,40 @@ impl mlua::IntoLua for TabInfo {
/// Движок Lua и состояние его глобального окружения.
pub struct ScriptEngine {
lua: Lua,
/// Каталог, относительно которого ищутся `init.lua`, `include` и `require`.
script_dir: PathBuf,
}
impl ScriptEngine {
/// Создаёт движок и публикует в нём API приложения.
///
/// Каталог сценариев берётся из [`script_dir`] (то есть из `--config`
/// или текущего рабочего каталога).
pub fn new(api: AppApi) -> mlua::Result<Self> {
let engine = Self { lua: Lua::new() };
Self::new_in(api, script_dir())
}
/// Создаёт движок с явно заданным каталогом сценариев.
///
/// Полезно тестам и будущим режимам, где каталог конфигурации приходит
/// не из глобального `--config`.
pub fn new_in(api: AppApi, directory: impl AsRef<Path>) -> mlua::Result<Self> {
let engine = Self {
lua: Lua::new(),
script_dir: directory.as_ref().to_path_buf(),
};
engine.expose_app_api(api)?;
engine.configure_require()?;
Ok(engine)
}
/// Выполняет стартовый сценарий из [`SCRIPT_DIR`].
/// Выполняет стартовый сценарий из каталога движка.
///
/// # Errors
/// Возвращает ошибку, если файла нет, его не удалось прочитать или в нём
/// есть синтаксическая/исполняемая ошибка.
pub fn run_startup(&self) -> mlua::Result<()> {
self.run_file(&script_path(STARTUP_SCRIPT))
self.run_file(&script_path(&self.script_dir, STARTUP_SCRIPT))
}
/// Выполняет Lua-файл в глобальном окружении движка.
@@ -73,7 +93,7 @@ impl ScriptEngine {
if let Ok(package) = globals.get::<Table>("package") {
let current: String = package.get("path").unwrap_or_default();
let dir = SCRIPT_DIR;
let dir = self.script_dir.display().to_string().replace('\\', "/");
let extra = format!("{dir}/?.lua;{dir}/?/init.lua");
let path = if current.is_empty() {
extra
@@ -85,10 +105,12 @@ impl ScriptEngine {
// include выполняет файл сразу, как dofile, но путь по умолчанию
// отсчитывается от каталога сценариев, а не от cwd.
let directory = self.script_dir.clone();
globals.set(
"include",
self.lua
.create_function(|lua, path: String| run_file(lua, &script_path(&path)))?,
self.lua.create_function(move |lua, path: String| {
run_file(lua, &script_path(&directory, &path))
})?,
)?;
Ok(())
@@ -96,13 +118,13 @@ impl ScriptEngine {
}
/// Путь к файлу сценария: абсолютные пути используются как есть, остальные
/// отсчитываются от [`SCRIPT_DIR`].
fn script_path(path: &str) -> PathBuf {
/// отсчитываются от каталога сценариев.
fn script_path(directory: &Path, path: &str) -> PathBuf {
let path = Path::new(path);
if path.is_absolute() {
path.to_path_buf()
} else {
Path::new(SCRIPT_DIR).join(path)
directory.join(path)
}
}