Skip to content
Ilia Maslakov edited this page Aug 22, 2026 · 3 revisions

Доменная модель Lua-расширений MC

Назначение

Lua в MC — это один runtime расширений, а не набор отдельных «Lua-плагинов редактора», «Lua-плагинов панелей» и специальных сценариев в ядре. Runtime загружает пакеты, изолирует их от внутренних структур MC и предоставляет несколько независимых предметных API.

Главная архитектурная граница проходит между политикой расширения и механизмами MC:

  • Lua-пакет определяет поведение, преобразования и содержимое;
  • runtime adapter проверяет Lua-значения и переводит их в стабильный C ABI;
  • host services выполняют разрешённые операции MC;
  • ядро владеет виджетами, памятью, жизненным циклом, отрисовкой и внутренними структурами.

Lua никогда не получает WPanel, WEdit, WView, dir_list, указатели на буферы, widget ID, VFS-объекты или функции ядра.

Слои системы

Lua package
    ↓ domain API: mc.editor, mc.panel_provider, mc.ui, ...
Lua runtime adapter
    ↓ typed, append-only runtime ABI
MC host services
    ↓ native public mechanisms
Editor / panels / viewer / diff viewer / dialogs / processes

Lua package

Самостоятельный пользовательский пакет с декларативным описанием и callback-функциями. Пакет хранит собственную бизнес-логику и состояние, но не владеет объектами интерфейса MC.

Lua runtime adapter

Единственный нативный Lua runtime (mc-lua). Он:

  • обнаруживает и загружает пакеты;
  • создаёт изолированные Lua-состояния;
  • проверяет аргументы и результаты callbacks;
  • преобразует Lua-таблицы в типизированные ABI snapshots;
  • проверяет capability и активный контекст;
  • изолирует ошибки пакета;
  • не реализует прикладную логику пакетов.

Runtime ABI

Типизированная граница между runtime и MC. ABI развивается только добавлением новых полей и функций в конец структур. В нём передаются значения с явными размерами, immutable snapshots и opaque handles с generation.

ABI не должен содержать сущности конкретного пакета. В нём допустимы только общие операции: открыть diff, получить текст, предоставить элементы панели или запустить процесс.

MC host

Реализует механизмы, которые уже принадлежат MC: editor transactions, panel navigation, native viewer и mcdiff, диалоги, keymap, процессы, refresh и события. Host проверяет handles, владеет native memory и гарантирует cleanup.

Общие сущности

Package

Единица загрузки и изоляции. Имеет стабильный ID, отображаемое имя, workspace, точку входа, состояние enabled/disabled и набор требуемых capabilities.

Один пакет может зарегистрировать несколько действий или providers, но Lua runtime остаётся одним загруженным runtime-плагином MC.

Context

Краткоживущий контекст активного вызова MC. Он определяет, какие объекты и мутации доступны сейчас. Сохранённый Lua-объект не даёт права выполнять операции после завершения callback.

Opaque handle

Ссылка вида {kind, id, generation} на host-owned объект. Handle не раскрывает адрес объекта. После закрытия объекта старый handle возвращает closed и не может ожить при повторном использовании внутреннего адреса.

Snapshot и revision

Snapshot — неизменяемое описание состояния на конкретной revision. Идентификатор revision является токеном равенства, а не сохраняемой версией документа. Операция с устаревшими координатами должна завершаться stale_revision, не изменяя данные.

Action

Именованная операция пакета. Может иметь клавишу и место в меню, но её ID не зависит от представления. Keymap и меню доставляют один и тот же action, а runtime не исполняет entry-файл повторно.

Source

Описание источника данных, а не открытый FILE *. Источник может быть bytes, локальным файлом, процессом или pipeline. Host материализует источник, передаёт его штатному consumer и освобождает ресурсы.

Error

На границе ABI используются стабильные машинные коды (not_supported, closed, stale_revision, provider_busy) и отдельный диагностический текст. Lua-ошибка callback изолируется runtime и не должна завершать MC.

Доменный словарь

Ниже перечислены сущности модели и их связи. Это не перечень функций ABI: методы, поля C-структур и ограничения сериализации описывают контракты, а доменная модель фиксирует смысл объектов.

Runtime

Загруженный движок языка. Runtime регистрируется в MC один раз, объявляет версию, capabilities и минимальные требования к host. Он обнаруживает packages, создаёт их окружения и является единственным посредником между ними и host.

Workspace

Область применения package: файловый менеджер, редактор, viewer, terminal или diff viewer. Workspace определяет доступный контекст и каталог обнаружения, но не создаёт отдельный Lua runtime.

Package descriptor

Декларативная идентичность package: ID, имя, workspace, версия API, entry point и класс пакета. Descriptor существует отдельно от загруженного Lua state и проверяется до исполнения entry point.

Package origin и precedence

Источник package — системный или пользовательский каталог. Одинаковый ID в более приоритетном origin замещает менее приоритетный. Origin входит в модель обнаружения и диагностики, но не меняет доменный API package.

Capability

Именованное право на класс host-операций. Наличие функции в ABI ещё не означает, что она доступна в текущем run mode или callback phase. Effective capability является пересечением возможностей сборки, host, runtime, workspace и активного контекста.

Subscription

Регистрация package на event с типом и приоритетом. Subscription принадлежит package, снимается явно либо автоматически при unload. Она не является синхронным запросом и не возвращает решение вызывающему домену.

Event snapshot

Неизменяемый снимок события. Содержит общую метаинформацию и typed payload конкретного домена. Snapshot не предоставляет доступ к живому widget и не должен сохраняться как handle host-объекта.

Editor document

Host-owned редактируемый документ. Его идентичность представлена editor handle, а состояние — document info snapshot. Документ содержит buffer, cursor, selection, revision и file state.

Position и range

Position связывает byte offset с вычисленными line и column на одной revision. Range — полуоткрытый интервал двух offsets. Координаты не являются отдельными живыми объектами и теряют применимость при изменении revision.

Selection

Снимок режима выделения и одного или нескольких ranges. Selection может быть отсутствующим, линейным или колонным. Выделенный текст является производным значением buffer на той же revision.

Edit и transaction

Edit — декларативная замена range текстом. Transaction — проверенный набор edits, применяемый атомарно и образующий одну undo-запись. Host отвечает за порядок применения, проверку пересечений, revision и redraw.

Panel и panel reference

Panel — host-owned UI-представление списка. mc.panel предоставляет временную ссылку на уже существующую active или passive panel. Эта ссылка не является provider instance и не даёт владения panel widget.

Panel provider

Глобальная регистрация логического namespace. Provider содержит identity, prefix, capabilities, callbacks, actions и help metadata. Один provider может обслуживать несколько независимых instances.

Connection

Сохранённое описание точки входа provider. Connection имеет стабильный ID и display metadata. В callbacks редактирования передаётся immutable snapshot; создание, копирование, переименование и удаление являются отдельными транзакционными операциями provider.

Provider instance

Состояние одного открытия provider. Instance принадлежит паре provider/panel, хранит текущую logical location и закрывается вместе с panel либо package. Несколько instances не разделяют навигационное состояние неявно.

Panel view

Revisioned snapshot текущей logical location. View объединяет presentation, columns, entries, focus, actions, footer и contextual help. Он заменяется целиком после reload или результата операции.

Panel entry

Элемент view с устойчивым в пределах instance ID, display name, kind, role, metadata и значениями columns. Entry является snapshot, а не файлом и не file_entry_t. Directory-like entry задаёт навигацию; file-like entry может предоставлять content.

Column и presentation

Column описывает семантическое поле entry и правила его показа. Presentation содержит только данные отображения; решение о ширине, обрезке, сортировке и отрисовке остаётся у host.

Panel selection

Снимок current entry и отмеченных entry IDs на одной view revision. Selection передаётся action как значение и не является изменяемой коллекцией panel.

Navigation request и operation result

Navigation request типизирует переход к entry, parent или location. Operation result декларативно сообщает host о refresh, новой location, focus, status или закрытии instance; callback не управляет panel widget напрямую.

Content contract

Связь panel entry с consumer содержимого. Provider может передать custom view, source/stream либо local-copy representation. Содержимое отделено от entry metadata и имеет собственное владение и cleanup.

Viewer source definition

Глобальное описание семейства управляемых источников: identity, callbacks, help и правила подготовки. Definition не содержит открытый источник.

Viewer session

Состояние одного созданного controller. Session появляется после create, передаётся viewer во владение при открытии и закрывается ровно один раз.

Viewer controller

Opaque объект, связывающий session с host viewer. Controller управляет prepare/options/commit/rollback/key lifecycle, но не предоставляет Lua доступ к WView.

Viewer source specification

Подготовленный snapshot источника: source, display title, help и политика auto-scroll. Spec валидируется до замены текущего datasource; неудачная подготовка оставляет прежний источник активным.

Bytes source

Неизменяемая последовательность байтов с явной длиной. Embedded NUL допустим, если consumer поддерживает byte content. При необходимости host материализует bytes во временный ресурс.

File source

Ссылка на локальный путь с явно определённым владением временным файлом. Путь является transport detail, а display title задаётся отдельно.

Process source

Процесс, описанный argv и рабочим каталогом. Command line не является display identity. Host владеет запуском, pipe, EOF, ожиданием процесса и отменой.

Pipeline source

Упорядоченная композиция process sources. Pipeline является одним логическим источником и имеет общий lifecycle, ошибку и consumer.

Dialog specification

Декларативное описание host-owned modal dialog: title, controls, buttons, default action и правила размещения. Lua не создаёт widgets и не назначает их координаты напрямую.

Dialog control

Типизированный элемент формы: label, input, checkbox, radio/select либо разделитель. Control имеет ID, display metadata, начальное значение и, где применимо, options. ID связывает specification с result.

Dialog result

Immutable результат завершённого dialog: выбранная action и значения controls. Cancel является нормальным результатом взаимодействия, а не Lua-ошибкой.

Indicator

Именованный фрагмент persistent UI state, принадлежащий package и UI area. Повторная установка пары owner/area/ID заменяет значение; unload очищает все indicators owner.

Process request и result

Process request задаёт команду и resource limits. Result содержит stdout, stderr, exit status, signal и признаки truncation. Ненулевой exit status — результат процесса, а невозможность запуска — ошибка host operation.

Help reference

Ссылка на package-owned help source и node. Help может принадлежать provider, view, entry или controller; host выбирает наиболее конкретный доступный контекст и открывает штатную help system.

Домены API

mc.editor

Работа с уже открытым документом:

  • current context и lifecycle handle;
  • document info, path, readonly, modified и revision;
  • cursor и selection snapshots;
  • чтение и замена ranges;
  • атомарные transactions с одной undo-записью;
  • save().

Редактор не содержит UI-методов. Диалоги находятся в mc.ui, процессы — в mc.process, события — в mc.on.

mc.panel

Автоматизация уже существующей панели: active/passive panel, cwd, current, selected, refresh и chdir. Этот API не создаёт новую панель и не поставляет её содержимое.

mc.panel_provider

Провайдер виртуального пространства панели:

  • registration metadata и capabilities;
  • список сохранённых connections и CRUD callbacks;
  • независимый instance на каждое открытие;
  • revisioned view с entries, columns и presentation metadata;
  • typed navigation;
  • actions над current/selection/view;
  • content через view, stream или local-copy contract;
  • context-dependent help.

Provider определяет логическое пространство. MC владеет панельным виджетом, сортировкой, отметками, историей и обработкой стандартных команд. Lua adapter подключается через тот же mc_panel_plugin_t, что и нативные providers; общему filemanager не нужны исключения для Lua.

mc.ui

Общие host-owned средства представления:

  • status и message;
  • declarative dialogs;
  • persistent indicators;
  • открытие native viewer;
  • открытие native diff viewer.

mc.ui.open_diff() принадлежит UI-домену, а не panel ABI: он открывает host-owned интерфейс и доступен любому Lua-пакету.

Viewer sources

Контроллер источника описывает данные, title, help и параметры обновления. MC открывает штатный viewer и управляет его жизненным циклом. title является человекочитаемым именем; command и временный путь остаются деталями источника.

mc.process

Контролируемый запуск внешнего процесса с ограничением вывода и явным результатом: stdout, stderr, exit code, signal и признаки truncation. Process API не является частью editor или panel API.

Events

Односторонние уведомления о lifecycle и действиях MC. События подходят для open, save, key и подобных наблюдений, но не заменяют синхронные запросы provider, которым требуется немедленный результат.

Владение и жизненный цикл

  • MC владеет native widgets и host objects.
  • Runtime владеет регистрациями, Lua states и преобразованными ABI-значениями.
  • Package владеет только Lua-состоянием своих instances и sessions.
  • Callback result действует до документированной точки освобождения.
  • Временные файлы и процессы освобождает слой, который их материализовал.
  • Закрытие panel instance, editor или viewer инвалидирует связанные handles.
  • Несколько панелей одного provider получают независимые instances.

Команды и клавиши

Клавиша сначала преобразуется общим keymap в команду MC. Панельный plugin получает стандартную команду через handle_key(). Lua adapter переводит её в доменную операцию provider.

Shift-F4 → CK_EditNew → runtime adapter → new_connection()
F4       → CK_Edit    → runtime adapter → edit_connection(snapshot)
Shift-F6 → CK_MoveSingle → runtime adapter → rename_connection(snapshot)
Shift-F3 → CK_ViewRaw → provider view(mode="plain")

Общий filemanager не должен узнавать, что provider реализован на Lua.

Если расширению не хватает возможности, сначала нужно определить, отсутствует ли общий ABI-механизм. Исправление должно находиться в runtime adapter или соответствующем общем домене. Изменение ядра оправдано только тогда, когда оно реализует универсальный host-механизм, одинаково пригодный для разных расширений.

Критерии границ

Новая возможность относится:

  • к editor ABI, если оперирует документом, selection или transaction;
  • к panel provider ABI, если описывает namespace, entries или navigation;
  • к viewer-source ABI, если описывает источник и его lifecycle;
  • к mc.ui, если открывает или обновляет host-owned интерфейс;
  • к mc.process, если запускает внешний процесс;
  • к runtime core, если касается загрузки, изоляции и ABI marshaling;
  • к пакету, если содержит его прикладную политику.

Это разделение позволяет развивать Lua как универсальную платформу расширений, не превращая ядро MC в набор специальных случаев для отдельных скриптов.

Clone this wiki locally