Создание метаданных
Создание объектов, реквизитов, маршрутов и форм движком вместо ручного yaml – и шаблоны кода.
Писать yaml руками – значит помнить про UUID, имена свойств вида и место, где регистрируется форма. Эту часть берёт на себя движок, и те же операции доступны из CLI, редактора и агента.
Создание метаданных
Механику создания метаданных инструмент берёт на себя: UUID, отступы, точечные вставки в yaml,
проверки дублей и совместимости секции с видом объекта. Одни и те же операции доступны из трёх
мест. В CLI это подкоманды с выводом в JSON. В MCP – инструменты meta_* для агентов. В LSP –
собственные запросы xbsl/meta*, на которых работает дерево метаданных расширения VS Code.
Создаётся 33 вида элементов проекта: от Справочника и Документа до ВиртуальнойТаблицы вместе с
обязательным парным запросом .xbql, ЗапланированногоЗадания, контрактов, прав и команд. Каждый
вид несёт то, что документация делает обязательным. Это платформенное умолчание видимости
ВПодсистеме – расширяйте его осознанно, ключом --scope. Это заготовка обработчика, без
которого вид бесполезен. И это заметка о том, чего генератор не станет выдумывать за
разработчика. Виды, чьё содержимое рисуют в конструкторе, – ПанельОтчетов и ПроцессИнтеграции –
намеренно отсутствуют.
xbsl new-project . vendor Приложение # описание проекта + модуль + подсистема
xbsl new-object <каталог-подсистемы> <вид> <имя> # вид: Справочник, Документ, Перечисление, ...
xbsl new-object <каталог-подсистемы> <вид> <имя> --presentation <мн-число> --object-presentation <ед-число>
xbsl add-field <объект>.yaml <вид-элемента> <имя> --type <тип> # вид: реквизит, ... (их перечисляет --help)
xbsl add-form . --name <объект> # формы объекта и списка + регистрация
xbsl add-form . --name <объект> --forms list-cards # форма списка карточками
xbsl new-object <каталог-подсистемы> <вид-http-сервиса> <имя> --routes "GET /, POST /, GET /{id}"
xbsl add-route <сервис>.yaml "DELETE /{id}" # шаблон URL + заготовка обработчика
xbsl add-method <модуль>.xbsl <метод> --annotations <аннотация> --after <существующий-метод>
xbsl set-localization <строки>.yaml <ключ> --value <язык>=<текст> ... # одна строка, все языки
xbsl add-subsystem vendor/Приложение <имя>
xbsl add-dependency . acme CurrencyConverter 2.0 # подключить библиотеку к проекту
xbsl rename-object . <старое-имя> <новое-имя> # файлы + ссылки по всему проекту
xbsl delete-object . --name <объект> # план; --apply удаляет и называет хвосты
xbsl move-object . <объект>.yaml <каталог-пакета> # в пакет: файлы + импорты + полные имена
xbsl rename-package . <каталог-пакета> <новое-имя> # каталог + импорты + полные имена
xbsl move-resource . <ресурс> <папка> # в другую папку: файлы + ключи
xbsl rename-resource-folder . <папка> <новое-имя> # папка + ключи её файлов
xbsl delete-resource-folder . <папка> # план; --apply удаляет и называет ссылки
xbsl resource-references . <ресурс> # места, которые называют ресурс
xbsl set-access . --name <объект> --default <способ-доступа>
xbsl object-info . --name <объект> # реквизиты, ТЧ, формы, namespace
xbsl project-info . # проекты, подсистемы, объекты по видам
xbsl project-info . --package <пакет> # объекты одного пакета
Вид объекта принимается в любом написании, а какие виды бывают в проекте, покажет
xbsl new-object --help. Вид элемента у add-field и set-field-property – слово самого
инструмента: реквизит, табличная-часть, строка и остальные, которые перечисляет
xbsl add-field --help. У каждого есть английский двойник для проекта на английском – термин
самой платформы для того же элемента: attribute, tabular-part, string; регистр и дефисы
роли не играют. Способы доступа и права set-access тоже принимаются на любом языке, как их
пишет yaml этого языка: PermitAuthenticated или русское имя, Read или Чтение. Аннотации и
все идентификаторы пишутся на языке разработки проекта: платформа так документирует свой словарь
метаданных, и инструмент принимает ровно эти написания. Поэтому в примерах выше стоят условные
имена в угловых скобках.
--presentation записывает заголовок элемента туда, где его держит вид. У справочника,
документа, плана обмена и хранилища настроек заголовков в разделе Интерфейс два:
--presentation – заголовок списка во множественном числе, --object-presentation – заголовок
объекта в единственном (в MCP presentation и object_presentation). Одно из другого инструмент
не выводит, а naming/presentation требует оба, поэтому объект, созданный с обоими, проходит линт
без находок. Другие виды --object-presentation не принимают. У регистра сведений к заголовку
списка в пару идет заголовок записи, --record-presentation (в MCP record_presentation), и
другие виды его не принимают. У набора констант заголовок один, заголовок записи, и пишет его
--presentation. У обработки тоже один – заголовок самого раздела Интерфейс, и его тоже пишет
--presentation. --periodicity (в MCP periodicity) делает набор констант периодическим –
День, Месяц, Квартал или Год. У периодического набора рядом с записью есть список, и
заголовки у него как у регистра сведений: --presentation – заголовок списка,
--record-presentation – заголовок записи.
Свойство, с которым рождается вид, записывается, только если его знает режим совместимости
проекта. Ключ доступа рождается вычисляемым (РучнаяВыдача: Ложь и заглушка его обработчика):
проект в новейшем режиме не принимает ключ, у которого разновидность не выбрана, а в режиме,
который свойства еще не знает, ключ создается без этой строки, и заметка объясняет почему.
project-info отвечает узко. --kind, --subsystem и --package сужают перечень объектов,
--brief убирает перечни и оставляет счётчики, а --project обходит только названный проект – по
Имя, Поставщик::Имя или каталогу: в корне репозитория рядом с проектом часто лежат примеры. У
каждого объекта есть subsystem, package (пусто в корне подсистемы, Партии или Партии::Архив
внутри пакета) и полное namespace – Поставщик::Проект::Подсистема::Пакет, с этим префиксом
сгенерированные формы пишут тип строки. packages перечисляет пакеты перечисленных объектов с их
каталогами. Виды объектов, виды секций и методы доступа приходят с --reference или --brief: от
исходников они не зависят.
Сами исходники бывают написаны и по-английски, и создание метаданных читает оба написания.
Объект с ElementKind: Catalog и секцией Attributes: находится, описывается и правится ровно
так же, как объект с ВидЭлемента: Справочник и Реквизиты:. Пишет инструмент на языке того
файла, в который пишет: новый реквизит английского объекта получает Name: и Type:,
недостающая секция создаётся как Attributes:, подсистема и запись библиотеки оформляются как
проект вокруг них. Пары написаний берутся из данных платформы – из английского имени свойства в
метамодели. Поэтому имя, которое платформа в разных классах пишет по-разному, остаётся русским и
при чтении, и при записи: Элементы у перечисления это Items, а в остальных местах
Elements, и угадывать тут нечего. В значениях имена автора остаются как переданы, а слова
платформы следуют файлу: платформенные типы в выражении типа, способ доступа (PermitEveryone в
английском файле, русское имя в русском). Логическое значение, которое вызов MCP передает как
true или false, пишется словом языка файла: Истина и Ложь в русском файле, True и
False в английском, как в исходниках дистрибутива.
add-field ставит новый элемент в конец секции своего вида, а саму секцию заводит только тогда,
когда её в файле нет. Данные регистра лежат в Измерения и Ресурсы. Поэтому реквизит,
запрошенный у регистра, где есть ресурсы и нет реквизитов, попадает в новую секцию Реквизиты в
конце файла. Поле notes говорит об этом и называет вид, который положил бы поле рядом с
существующими: здесь это ресурс, а для ресурса там, где есть только реквизиты, наоборот.
Так же пополняются свойства компонента интерфейса. Вид свойство пишет в Свойства имя и тип, без
Ид: у класса свойства компонента его нет. Через --prop задаются три ключа, которые документация
называет у такого свойства: ЗначениеПоУмолчанию, СохраняемыеДанные и Контекстное
(--prop ЗначениеПоУмолчанию=0). Компоненту без этой секции она заводится там, где ее пишет среда
разработки, – после Наследует и перед События. Вид событие так же пополняет собственные
События компонента: имя и тип объекта события – СобытиеКомпонента, если --type не задан, или
СобытиеСДанными<Строка> и подобные для события, которое передает значение. Недостающая секция
События встает сразу после Свойства, а у компонента без свойств – в конец файла. --doc
задает элементу описание, и текст становится его документирующим комментарием: строками ## в
начале элемента, единственным комментарием, который среда разработки сохраняет, когда записывает
файл заново.
Формы генерируются с наполнением: поля ввода по реквизитам объекта, включая стандартные
Наименование, Номер и Дата и поддержку иерархии, колонки динамического списка, таблицы табличных
частей, форма отчёта с параметрами. Готовая форма регистрируется в секции Интерфейс владельца.
Ключ --dry-run печатает изменения вместе с полными текстами файлов и ничего не записывает.
Этим ключом пользуется расширение: правки оно применяет само, через свой механизм отмены.
Заголовки формы и колонок пишутся через словарь проекта. Если в папке подсистемы лежит один
элемент ЛокализованныеСтроки, а в описании проекта заявлено два языка локализации, заголовок
получает вид $Словарь.Имя. Нужные ключи в той же операции добавляются и в словарь, и в его
переводы: ссылка на необъявленный ключ роняет применение сборки. Без такого словаря заголовок
остаётся литералом.
--forms list-cards собирает форму списка сеткой карточек вместо таблицы: ПроизвольныйСписок
с матричной группой в КонтейнерСтрок плюс компонент строки СтрокаСписка<Имя>. На карточку
идут заголовок, фото и ещё до трёх полей, даты – с форматированием. Реквизит
ДвоичныйОбъект.Ссылка переключает карточку на ПроизвольнаяКарточка с изображением над
подписью. Что попало на карточку, а что нет, перечисляет поле notes. Ширину колонки сетки
задаёт --card-min-width: по умолчанию 400, а с фото 250. Картинку-заглушку пустого фото
задаёт --card-placeholder.
set-localization записывает одну строку локализации сразу во все языки. Текст языка по
умолчанию ложится в сам элемент ЛокализованныеСтроки – там его и держит платформа. Каждый
другой язык получает свой Локализация/<Код>/<Имя>.yaml. Это недостающая половина
add-localization, который добавляет язык. Раньше заголовок набирали в элементе и ещё раз в его
английском двойнике, файлы расходились молча, и сверить их было нечем, кроме глаз. Язык,
названный без файла перевода, отвергается: сначала добавьте язык. Язык, о котором вызов молчит,
всё равно получает строку – значением языка по умолчанию и с заметкой, – чтобы ни один перевод
не остался на ключ короче. Ключ сохраняет секцию, в которой уже живёт, а новый ложится в
Строки, если --section не говорит Шаблоны.
--dry-run здесь устроен иначе, чем у остальных команд семейства: сводка по строкам (ключ,
язык, файл, значение до и после) вместо целых файлов – один ключ на проекте с двумя языками
раньше выводил больше 100 КБ ради однострочной правки. --full-text возвращает файлы рядом со
сводкой. --entries-file <json-или-yaml> пишет много ключей за один запуск – {Ключ: {Язык: Текст}}, а --entry Ключ={"Язык":"Текст"} добавляет ещё один ключ прямо в командной строке;
оба сочетаются с одиночным аргументом name (--value относится к этому аргументу и без него
отвергается). Файл пакета читается так, что всякое значение остаётся текстом: Yes, No, On и
12:30 остаются подписями, какими и выглядят, а не булевыми и числом; значение, которое текстом
не является, отвергается с именем ключа. Файл, который трогают несколько ключей, читается
и пишется один раз, сколько бы ключей в него ни попало, а весь пакет планируется целиком, прежде
чем что-то применяется: неверный ключ отклоняет вызов раньше, чем запишется хоть один файл, и не
оставляет на диске уже применённые перед ним строки. Перевод, который вызов только прочитал –
каждый его ключ уже нёс ровно тот же текст, – сохраняет и байты, и время правки и не попадает в
список files. MCP meta_set_localization принимает те же
entries, dry_run и full_text.
add-field <строки>.yaml строка <ключ> (meta_add_field с видом строка) добавляет ключ, а
--type несет его текст на языке по умолчанию; без --type текстом становится сам ключ. Ключ
ложится в элемент и дописывается в готовые переводы значением языка по умолчанию, чтобы ни один
из них не остался на ключ короче. Заметка называет set-localization: текст перевода пишется
именно им. Вызов по самому файлу перевода отвергается, потому что у файла нет ни вида, ни секций
элемента. Отказ называет элемент, к которому файл относится, и тот же set-localization. Раньше
на этом месте стояло “у вида ? нет секции”.
add-localization <строки>.yaml <язык> добавляет перевод, а localization-info перечисляет
языки, для которых его можно добавить. Языки – это перечисление самой платформы: инструмент
берет их из данных вместе с режимом совместимости, который нужен каждому, поэтому китайский,
вьетнамский и таджикский стоят рядом с русским и английским, как только они есть в данных.
Язык, добавленный более новым режимом, предлагается и добавляется только проекту этого режима, а
значение ЯзыкиЛокализации, в котором данные не узнают ни одного языка, названо в заметках и
не пропадает из ответа молча. Новый перевод ложится в папку с кодом языка с заглавной буквы
(Локализация/Vi); папка, уже записанная в другом регистре (vi), находится как есть:
платформа читает код без учета регистра.
add-dependency подключает библиотеку: пишет раздел Библиотеки файла Проект.yaml с полями
Имя, Поставщик и Версия. Версия здесь – это версия релиза библиотеки. Релиз выпускают
в панели управления, а версию сборки с суффиксом вида 1.0-42 инструмент отклоняет. Разные
версии одной библиотеки в проекте не допускаются, поэтому повторное подключение обновляет версию
существующей записи. Что подключено сейчас, показывает project-info в
projects[].libraries. Поставщика, имя, версию и полные имена типов библиотеки даёт разбор её
архива: elemctl inspect <файл.xlib>.
set-access точечно правит КонтрольДоступа.Разрешения и знает, что допускает вид объекта.
--default задаёт право ПоУмолчанию. --permission Чтение=РазрешеноВсем задаёт отдельное
право, в том числе пользовательское от ПравоНаЭлемент. Годится любой язык, а в файл право и
способ попадают на языке файла: английский объект получит Read: PermitEveryone, даже если вызов
был на русском. --calc-by заполняет
РасчетРазрешенийПо, обязательный для РазрешенияВычисляютсяДляКаждогоОбъекта. Неизвестный
способ, право, которого у вида нет, и построчные разрешения у НаборКонстант, сервиса и
Обработка отклоняются: у их настроек доступа нет РасчетРазрешенийПо.
Обработчики вычисления разрешений остаются за разработчиком, а какие именно нужны, скажет поле
notes. object-info показывает текущие разрешения и права вида, project-info – ПоУмолчанию
каждого объекта, те и другие русскими именами, на каком бы языке ни был файл. Нет секции –
значит, платформа применяет РазрешеноАдминистраторам.
rename-object переименовывает файлы объекта вместе с формами, компонентом строки
СтрокаСписка<Имя> и описаниями WSDL клиента SOAP-сервиса. Номер описания сохраняется, а ссылка
одного описания на другое по имени файла получает новое имя. Импортированные схемы XML
(<Имя>.Wsdl.<N>.xsd) тоже переносятся, переименовываются и удаляются вместе с клиентом.
Локальные ссылки location и schemaLocation получают новое имя, внешние URL сохраняются.
Ссылки команда обновляет по всему
проекту и с учётом контекста: значения ссылочных ключей yaml (Тип, Таблица, ИсточникДанных, Форма,
ТипФормы), привязки =... и код .xbsl. Реквизиты, компоненты и поля динамических списков, которые
просто совпали со старым именем, она не трогает. Строковые литералы с текстом интерфейса – тоже.
Комментарии в модулях и в yaml получают новое имя целиком, вместе с текстом в кавычках внутри
комментария. Словарь перевода команда не переписывает как исходник. Для каждой строки комментария,
которую изменило переименование, перевод из phrases записывается заново под новой строкой, а
прежний ключ остаётся для текста, который его ещё читает. Пары tokens команда не трогает: старое
имя может остаться у тёзки. Заметки называют эти пары, английское написание нового имени задаёте вы.
Ключи --new-presentation и --old-presentation обновляют Заголовок и Представление объекта и его
форм. Ид объекта сохраняется, поэтому данные на платформе переживают переименование.
delete-object удаляет объект целиком: пару yaml и модуль, его формы и компонент строки списка
СтрокаСписка<Имя> вместе с их парами, а у клиента SOAP-сервиса – и описания WSDL. Подсистема – это
папка, в которой лежат файлы, поэтому вхождение в неё уходит вместе с ними. Каждое оставшееся
упоминание имени по проекту команда называет файлом и строкой, включая строковые литералы и
комментарии. Маршрутизатор, который открывает форму по имени из строки, и начальное заполнение –
ровно те хвосты, которые иначе всплывают ошибкой применения. Править их команда не станет: какое
упоминание мёртвый код, решает автор. Удаление необратимо, поэтому без --apply команда печатает
план. Инструмент MCP meta_delete_object так же отвечает планом, потому что по умолчанию у него
dry_run=true.
Переименование, отличающееся только регистром (Товары в товары), инструмент выполняет в два
шага, через временное имя. Регистронезависимая файловая система – Windows, macOS – адресует
старое и новое имя как один файл, и одношаговое переименование между ними не гарантировано. Сбой
второго шага откатывает первый, временное имя на диске не остаётся. На регистрозависимой
файловой системе имя свободно, и переименование выполняется одним шагом. Файлы инструмент
переименовывает сам, а вот систему контроля версий проверьте. Git на регистронезависимой
файловой системе сличает имена без учёта регистра только для латиницы. Латинское переименование
он поэтому не заметит – зафиксируйте его явно, командой git mv <старое> <новое>. Кириллическое
он покажет как удаление и добавление, и тогда в остальных клонах на такой же файловой системе
git pull остановится с “untracked working tree files would be overwritten by merge”. Там перед
обновлением надо удалить файл со старым именем.
move-object (MCP meta_move_object) переносит объект в другой каталог его проекта: в пакет
подсистемы – несуществующий каталог им становится, – в другой пакет, в корень подсистемы или в
другую подсистему. Объект переезжает вместе с формами, модулями, строкой и таблицей списка, а клиент
SOAP-сервиса – с описаниями WSDL. Элемент пакета живёт в пространстве имён самого пакета, поэтому
перенос чинит всё, что обращалось к объекту по старому месту: модуль или yaml другой подсистемы
получает импорт Подсистема::Пакет или элемент Импорт, перенесённые файлы при переходе в другую
подсистему получают импорты прежней, переписываются квалифицированные имена – включая полное
Поставщик::Проект::Подсистема::ИмяФормы.ДанныеСтрокиСписка сгенерированной формы, – а подсистема,
которая начала импортировать другую, получает её в Использование. Что чинить, решают правила
импорта и видимости линтера, прогнанные до и после переноса. Внутри одной подсистемы корень и пакеты
видят друг друга, поэтому импорт там не дописывается, а за файлами следуют только полные имена.
Команда отказывает, если имя занято или непубличный элемент станет нужен другой подсистеме:
видимость решает автор. Ставший лишним импорт называется в notes, но не снимается – его покажет
code/unused-import. rename-package (MCP meta_rename_package) переименовывает каталог пакета со
всеми файлами и правит импорт Подсистема::Партии[::Архив], элементы Импорт и квалифицированные
имена по проекту. Для кириллического имени пакета в проекте со словарём перевода обе команды
напоминают о паре для этого имени.
move-resource (MCP meta_move_resource) переносит файл ресурса или папку в другую папку того же
каталога Ресурсы; несуществующая папка создаётся переносом – пустую папку не хранят ни git, ни
сборка. rename-resource-folder (MCP meta_rename_resource_folder) переименовывает папку внутри
каталога. Ресурс адресуется путём внутри каталога, поэтому обе команды переписывают статические
ссылки на перенесённые файлы: литералы Ресурс{...} в модулях и привязках yaml и голые значения
свойств-картинок (Изображение: Картинки/Флаг.svg). Ключ с пространством имён указывает на каталог
этой подсистемы или пакета; голый ключ ищется во всех каталогах ресурсов своей подсистемы и
импортированных пространств имён, а ключ, который есть в двух из них, не переписывается, а
называется в notes. Обращение по вычисленной строке
(ПакетРесурсов.Текущий().Получить("Папка/...")) разрешается при выполнении: такие строки команды
перечисляют с файлом и строкой и оставляют автору. delete-resource-folder (MCP
meta_delete_resource_folder) удаляет папку с файлами и перечисляет оба вида ссылок, не правя их;
без --apply печатается план. Перенос в каталог Ресурсы другой подсистемы или пакета запрещён:
файл сменил бы пространство имён, а обращение по строке на старом месте поправить нечем.
resource-references (MCP meta_resource_references) находит места, которые называют файл ресурса
или папку, и ничего не меняет. Исходники читаются так же, как при переносе, и у каждого места есть
файл, диапазон, строка и вид: reference – статическая ссылка, ambiguous – ключ в нескольких
пространствах имен с одинаковым выбранным приоритетом, string – строка с путем, computed – строка с папкой и вычисляемым именем
файла, stem – строка, целиком равная ключу файла без расширения. Так начальные данные называют
картинку кодом, а расширение добавляет код при выполнении; для этого читаются и JSON-файлы ресурсов
проекта. Дерево метаданных расширения VS Code показывает ответ в панели “Ссылки”.
Ресурс своей подсистемы имеет приоритет перед одноименным импортированным. Корень и пакеты
своей подсистемы равноправны; ключ в двух таких пространствах имен остается неоднозначным.
--limit (в MCP limit, по умолчанию 100) оставляет в списке первые места, а total считает
все. Если лимит отрезал часть мест, в ответе стоит hasMore: true, а команда еще печатает в
stderr строку с --limit, при котором список будет полным.
xbsl unused-resources <root> и MCP meta_unused_resources показывают кандидатов без
известных обращений. Анализируются литералы ресурсов, свойства изображений, методы загрузки,
ключи JSON и ссылки из используемых CSS/HTML/SVG/JS. Ссылки из неиспользуемого ресурса
сами по себе не делают другой файл используемым. Вычисляемые пути и неопределенные
обращения учитываются отдельно. --include-protected добавляет эти отдельные списки,
--limit ограничивает каждый список; итоговые числа остаются полными. У каждой записи
стоит confidence: candidate: внешние данные и произвольные вычисляемые имена требуют
дополнительной проверки. Команда ничего не удаляет.
Шаблоны кода
Шаблон – это аббревиатура и конструкция за ней. Набираете есл, жмёте Ctrl+Space, выбираете
если и получаете готовый оператор с точками ввода, между которыми ходит Tab. Механизм
повторяет шаблоны 1С:EDT (Параметры – Шаблоны), включая формат файла выгрузки.
Шаблоны предлагаются до остальных подсказок: конструкция, которую вы набираете, нужнее
имени, которое просто начинается так же. Данные Элемента им не нужны, нужен только LSP-сервер
(xbsl.lsp.enabled, включён по умолчанию). В режиме CLI-индекса расширение шаблоны не
предлагает.
Встроенный набор – 51 шаблон, все в xbsl/templates_builtin.py. Это управляющие конструкции,
объявления (методы с аннотациями, структуры, перечисления, типы исключений), запросы и
прикладные идиомы: обход справочника, движения регистра, обработчик HttpСервиса, разрешения
доступа по объектам, события объекта, обработчики формы. Каждый шаблон разбирается тем же
парсером, что и линтер (tests/test_templates.py), поэтому вставить некомпилируемый код он не
может.
В тексте шаблона живут точки ввода и списки выбора. Переменные записываются синтаксисом шаблонов
1С:EDT (${...}) и именуются на языке разработки проекта:
| Переменная | Во что разворачивается |
|---|---|
${Редактировать("подсказка")} |
точка ввода; подсказка – заранее выделенный текст |
${Выбрать("а", "б")} |
выпадающий список заданных вариантов |
${ИмяОбъектаМетаданного(Справочник)} |
список справочников вашего проекта, из индекса |
${ПолноеИмяОбъектаМетаданного("Перечисление")} |
то же, но вставляется как Вид.Имя |
Собственные шаблоны лежат в .xbsl-templates.json в корне рабочей области; путь задаёт ключ
--file или настройка xbsl.templates.file. Файл дополняет встроенный набор, а одноимённый
шаблон замещает встроенный. Хранится только то, что отличается от встроенного набора, поэтому
следующий релиз до вас дойдёт.
xbsl templates list # весь набор: встроенные и свои (свои помечены *)
xbsl templates export --output мои.json # выгрузка (например, чтобы перенести на другую машину)
xbsl templates import выгрузка.json # влить выгрузку в свой файл
В VS Code то же самое делает панель XBSL: шаблоны кода, устроенная как диалог EDT: список с контекстом вызова, описание и текст шаблона, кнопки добавления, правки, удаления, импорта, экспорта и восстановления умолчаний. После сохранения работающий сервер перечитывает набор, и следующий Ctrl+Space уже предлагает исправленный шаблон.