feat(form-decompile,form-compile): суппресс авто-вывода (MainAttribute/SavedData/AutoTitle) + AutoFillAvailableFields + SaveWindowSettings

Раундтрип TOTAL 40→25, match 138→146. Три класса «компилятор додумывает на
main/titled формах» (декомпилятор не давал суппресс-маркера) + два непокрытых свойства.

- MainAttribute: эвристика 11b.3 (нет явного main + ровно 1 объектный реквизит
  → помечает main). Декомпилятор зеркалит условие → ставит main:false (компилятор
  уже исключает такие кандидаты). Объектные реквизиты часто НЕ main (DynamicList
  1207 без, RecordSet 226 без, и т.д.). Decompiler-only.
- SavedData: эвристика $mainSaved (main + Catalog/Document/ChartOf*/ExchangePlan/
  BusinessProcess/Task Object + RecordManager → SavedData=true). Часто отсутствует
  (DocumentObject 332 без = 23%). Компилятор: явный savedData:false побеждает;
  декомпилятор ставит savedData:false для main-реквизита saved-типа без <SavedData>.
- AutoTitle: компилятор инъектит false при наличии title (~95% форм). Редкие 5%
  (Title есть, AutoTitle нет) → декомпилятор ставит autoTitle:"", компилятор
  пропускает пустую строку в Emit-Properties (общий ""-суппресс).
- AutoFillAvailableFields: свойство <Settings> дин-списка (дефолт true, эмит только
  отклонение false; ключ settings.autoFillAvailableFields).
- SaveWindowSettings: форменный bool (KNOWN_FORM_PROPS + auto-PascalCase).

Зеркало py (компилятор). Кейс dynamic-list-form +saveWindowSettings (сертифицирован).
Формы ЗадачаИсполнителя/Дополнительно, БизнесСеть/*, АнализПравДоступа → чисто.
Регресс 39/39 в обоих рантаймах.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-09 13:07:38 +03:00
parent 1a601a4d0c
commit bc81faf892
6 changed files with 62 additions and 14 deletions
+5 -3
View File
@@ -49,7 +49,8 @@
| DSL ключ | XML элемент | Значения |
|----------|-------------|----------|
| `autoTitle` | `<AutoTitle>` | `true` / `false` |
| `autoTitle` | `<AutoTitle>` | `true` / `false`. **При наличии `title` компилятор сам инъектит `false`** (≈95% форм). Маркер `""` подавляет инъекцию (редкие формы с title, но без `<AutoTitle>`) |
| `saveWindowSettings` | `<SaveWindowSettings>` | `true` / `false` |
| `windowOpeningMode` | `<WindowOpeningMode>` | `LockOwnerWindow`, `Modeless` |
| `commandBarLocation` | `<CommandBarLocation>` | `Top`, `Bottom`, `None` |
| `saveDataInSettings` | `<SaveDataInSettings>` | `UseList`, `Use`, `DontUse` |
@@ -768,14 +769,14 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
|----------|-----|----------|
| `name` | string | Имя реквизита (обязательно) |
| `type` | string | Тип (shorthand) |
| `main` | bool | Основной реквизит формы |
| `main` | bool | Основной реквизит формы (`<MainAttribute>`). **`true`** → пометить главным. **`false`** → суппресс: подавить авто-вывод компилятора (эвристика «нет явного main + ровно 1 реквизит объектного типа → пометить его»). Нет ключа → авто-вывод |
| `title` | string/object | Заголовок. **Нет ключа** → авто-вывод из имени (как у элементов; кроме `main`). **`""`** → подавить (`<Title>` не эмитится — так платформа и хранит реквизит без синонима). Строка → ru; объект `{ru,en}` → мультиязычный. Декомпилятор опускает ключ, когда ru-заголовок совпадает с авто-выводом из имени |
| `view` | bool/object | Просмотр по ролям (`<View>`). См. §4.1c |
| `edit` | bool/object | Редактирование по ролям (`<Edit>`). См. §4.1c |
| `functionalOptions` | array | Функциональные опции (`<FunctionalOptions><Item>FunctionalOption.X</Item>…`). Массив имён; forgiving: `"X"`/`"FunctionalOption.X"`. Также у колонок (`columns[*]`) и команд (§7) |
| `useAlways` | array | Поля, всегда читаемые (`<UseAlways><Field>Имя.Поле</Field>…`). Массив коротких имён полей (forgiving: с/без префикса `Имя.`). **Маркер `~`** (query-поля дин-списка): `~Остановлен``<Field>~Список.Остановлен</Field>` (префикс ставится ПОСЛЕ `~`; полная форма `~Список.Остановлен` тоже принимается verbatim). **Две формы**: этот массив на реквизите ИЛИ `useAlways: true` на колонке (`columns[*]`) — компилятор сливает. Для дин-списка — только массив (колонки не эмитятся, но формируют `<UseAlways>`) |
| `valueType` | string | Тип значений у реквизита типа `ValueList` (`<Settings xsi:type="v8:TypeDescription">`). Грамматика — как у `type`, включая составной `A \| B`. **Три состояния**: нет ключа → нет `<Settings>`; `""` → пустой `<Settings…/>` (список без ограничения типа); тип → с типом. Forgiving-синонимы: `typeDescription` (≈1С «ОписаниеТипов» / XML), `описаниеТипов`, `типЗначений`. Пример: `"valueType": "CatalogRef.Контрагенты"` |
| `savedData` | bool | Сохраняемые данные (`<SavedData>`) |
| `savedData` | bool | Сохраняемые данные (`<SavedData>`). **`false`** → суппресс авто-вывода компилятора (main-реквизит объектного типа Catalog/Document/ChartOf*/ExchangePlan/BusinessProcess/Task Object + RecordManager → `SavedData=true`). Нет ключа → авто-вывод |
| `save` | bool/string/array | Сохранение значения в пользовательских настройках (`<Save><Field>…`). `true``<Field>имя</Field>`; строка/массив строк → под-поля с авто-префиксом `имя.` (путь с точкой / UUID `1/0:…` / совпадающее с именем — берётся как есть). Нет ключа или `false` → не эмитится. Пример периода: `["Период","EndDate","StartDate","Variant"]` |
| `fillCheck` | bool/string | Проверка заполнения реквизита (`<FillCheck>`). `true``ShowError` (единственное значение в схеме); строка → verbatim. Синоним `fillChecking`. (`<FillChecking>` в схеме нет — был багом) |
| `columns` | array | Колонки для ValueTable/ValueTree (`{ name, type, title?, functionalOptions?, useAlways? }`) |
@@ -801,6 +802,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
| `mainTable` | string | Основная таблица. Принимает рус-имена метаданных (`Справочник.X``Catalog.X`) |
| `query` | string | Текст запроса (`ManualQuery=true`). Поддерживает `@file.sql` (путь относительно JSON) |
| `dynamicDataRead` | bool | Динамическое считывание. **Умолчание `true`** — указывать только для отключения (`false`) |
| `autoFillAvailableFields` | bool | Автозаполнение доступных полей (`<AutoFillAvailableFields>`). **Умолчание `true`** — указывать только для отключения (`false`; тогда поля берутся из явного запроса, не авто). Эмитится первым в `<Settings>` |
| `fields` | array | Явные поля набора (редко): `{ field, dataPath?, title? }` — для переопределения заголовка. Обычно поля выводятся из запроса автоматически |
| `parameters` | array | Параметры схемы запроса (`DataCompositionSchemaParameter`) — см. ниже |
| `order` | array | Сортировка списка (см. ниже) |