agent ONLINE
posts 17
labs 11
mode roaming
focus hyperfocus
uptime 3360d
now --:-- UTC

highlight.js для 1С: мытарства, баг и npm-пакет

Подключил highlight.js к блогу — и Функция осталась серой. КонецФункции тоже. Если/Тогда/Пока светятся, а объявление функции — нет. Пять минут разобраться «почему» превратились в несколько часов.

Дизайн, не баг

В hljs у языков помимо ключевых слов есть пары «открыть / закрыть блок» — то, что обозначает начало и конец именованной области кода. В грамматике 1c такими парами объявлены Функция/КонецФункции и Процедура/КонецПроцедуры. Они не получают класс hljs-keyword — им достаётся другая роль, и раскрашиваются иначе (или не раскрашиваются вовсе).

Знач — модификатор параметра — просто отсутствует в списке ключевых слов грамматики. Вообще. Можно было написать пост-процессор на пять строк и закрыть тему. Но пока копался в исходниках, нашёл кое-что интереснее.

Английский BSL — нулевая подсветка

Грамматика 1c для hljs написана только под русский синтаксис. Если у кого-то конфигурация с английской локализацией — Function, EndFunction, If, Then и всё остальное рендерится как простой текст. Совсем, без единого цвета.

Это неочевидно: большинство 1С-разработчиков работают с русским синтаксисом, и проблема просто не всплывала. Но международные команды встречаются. Значит фикс должен быть двуязычным.

Что взял из atom-language-1c-bsl

Нашёл atom-language-1c-bsl — грамматику для Atom от того же сообщества. Три вещи пошли прямо в патч:

Регистронезависимость. BSL не чувствителен к регистру (если = ЕСЛИ = Если), а JavaScript \b работает только с ASCII и ломается на кириллице. Поэтому явные символьные классы для границ слов и флаг gi вместо просто g.

Литералы. Истина, Ложь, Неопределено (и EN-аналоги) не получают hljs-literal нативно — добавил отдельным проходом.

Аннотации. &НаСервере, &AtClient и подобные рендерились как обычный текст. Регулярное выражение &[a-zA-Zа-яёА-ЯЁ_][…] закрыло это одним проходом.

Баг — чей он вообще?

В теме уже жил отдельный патч для подсветки обращений через точку — Запрос.Текст, Объект.Реквизит. Патч писал ИИ. Я проверил что работает — закрыл задачу.

Оказалось: он обходит все текстовые узлы, включая внутри строковых литералов. В тексте запроса "ОстаткиИОбороты.Номенклатура"Номенклатура подсвечивалась как свойство объекта, хотя это просто часть строки.

Когда нашёл — спросил себя: а чья это недоработка? ИИ написал код с ошибкой. Но я не проверил граничный случай со строками-запросами и спокойно закрыл задачу. Нашёл до прода — уже хорошо.

Фикс — три строки: перед обработкой текстового узла проверять предков на класс hljs-string. Если есть — пропускать.

Про примеры кода

Демо-страница нужна была с реалистичными примерами — запрос с виртуальной таблицей, серверная процедура, HTTP. Попросил ИИ сгенерить. Подсветка сработала, выглядело хорошо — поехали.

Потом посмотрел внимательнее: Справочники.Склады.НайтиПоНаименованию("Главный"). Сообщить(Выборка.Номенклатура.Наименование) в цикле — отдельный запрос к БД на каждой строке. ОбщегоНазначенияСервер — модуля с таким именем в БСП нет. Голый ПодробноеПредставлениеОшибки() — устаревший глобальный метод с 8.3.17+.

Пришлось попросить переделать по правилам. Получилось — но не без напоминания)


Запрос с виртуальной таблицей

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    ОстаткиИОбороты.Номенклатура КАК Номенклатура,
|    ОстаткиИОбороты.Номенклатура.Наименование КАК НоменклатураНаименование,
|    ОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстаток,
|    ОстаткиИОбороты.КоличествоКонечныйОстаток  КАК КонечныйОстаток,
|    ОстаткиИОбороты.КоличествоПриход КАК Приход,
|    ОстаткиИОбороты.КоличествоРасход КАК Расход
|ИЗ
|    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(
|        &НачалоПериода, &КонецПериода, Авто, Движения,
|        Склад = &Склад) КАК ОстаткиИОбороты
|ГДЕ
|    ОстаткиИОбороты.КоличествоКонечныйОстаток <> 0";

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода",  КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Склад",         Склад); // передавать ссылкой, не искать по имени

Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        ОбщегоНазначения.СообщитьПользователю(СтрШаблон(
            "%1, остаток: %2",
            Выборка.НоменклатураНаименование,
            Формат(Выборка.КонечныйОстаток, "ЧДЦ=3")));
    КонецЦикла;
КонецЕсли;

Серверная процедура с обработкой исключения

&НаСервере
Процедура ПровестиДокументНаСервере(Объект, Отказ)
    Попытка
        НаборЗаписей = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.Регистратор.Установить(Объект.Ссылка);

        Для Каждого СтрокаТЧ Из Объект.Товары Цикл
            Движение = НаборЗаписей.Добавить();
            Движение.ВидДвижения  = ВидДвиженияНакопления.Расход;
            Движение.Период       = Объект.Дата;
            Движение.Номенклатура = СтрокаТЧ.Номенклатура;
            Движение.Склад        = Объект.Склад;
            Движение.Количество   = СтрокаТЧ.Количество;
        КонецЦикла;

        НаборЗаписей.Записать();
    Исключение
        ОбщегоНазначения.СообщитьПользователю(
            ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()),
            ,,,, Отказ);
    КонецПопытки;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьОстатокНаСкладе(Номенклатура, Склад) Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки(
    |        , Номенклатура = &Номенклатура И Склад = &Склад) КАК Остатки";
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    Запрос.УстановитьПараметр("Склад",        Склад);
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.КоличествоОстаток;
    КонецЕсли;
    Возврат 0;
КонецФункции

JSON-сериализация и HTTP-запрос

ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ", Истина);
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);

Структура = Новый Структура;
Структура.Вставить("ШтрихКод",   "4607038302746");
Структура.Вставить("Количество", 5);
Структура.Вставить("Серия",      Строка(СерийныйНомер.УникальныйИдентификатор()));

ЗаписатьJSON(ЗаписьJSON, Структура);
JSONТекст = ЗаписьJSON.Закрыть();

HTTPСоединение = Новый HTTPСоединение("api.1c.ru", 443,,,, 30, Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапрос = Новый HTTPЗапрос("/v1/sync", Новый Соответствие);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
HTTPЗапрос.УстановитьТелоИзСтроки(JSONТекст, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);

Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если Ответ.КодСостояния = 200 Тогда
    ЗаписьЖурналаРегистрации(
        НСтр("ru = 'Интеграция.Синхронизация'"),
        УровеньЖурналаРегистрации.Информация, , , Ответ.ПолучитьТелоКакСтроку());
Иначе
    ЗаписьЖурналаРегистрации(
        НСтр("ru = 'Интеграция.Синхронизация'"),
        УровеньЖурналаРегистрации.Ошибка, , , СтрШаблон(
            НСтр("ru = 'HTTP %1: %2'"),
            Ответ.КодСостояния,
            Ответ.ПолучитьТелоКакСтроку()));
КонецЕсли;

Заодно — bash, sql, json

#!/bin/bash
# Bulk-import 1C XML files into infobase
ibcmd="C:/Program Files/1cv8/8.3.27.1989/bin/ibcmd.exe"
db="F:/Bases/test"

for xml in F:/exports/*.xml; do
  echo "Importing $xml..."
  "$ibcmd" config import --db-path="$db" "$xml" || exit 1
done
echo "Done"
SELECT
    p.id,
    p.title,
    pm.meta_value AS reading_time,
    COUNT(c.id) AS comment_count
FROM wp_posts p
LEFT JOIN wp_postmeta pm ON pm.post_id = p.id AND pm.meta_key = '_reading_time'
LEFT JOIN wp_comments c  ON c.comment_post_ID = p.id AND c.comment_approved = '1'
WHERE p.post_status = 'publish' AND p.post_type = 'post'
GROUP BY p.id
ORDER BY p.post_date DESC
LIMIT 10;
{
  "version": "0.6.18",
  "theme": "androman",
  "highlight": {
    "languages": ["1c", "bash", "sql", "json"],
    "theme": "github-dark",
    "version": "11.10.0"
  }
}

Итог

Патч вышел как npm-пакет highlightjs-1c-bsl: три прохода — ключевые слова, литералы, аннотации. RU + EN. Регистронезависимо.

Живые примеры с обоими синтаксисами: andromanpro.github.io/highlightjs-1c-bsl

Исходники: GitHub · README на русском · npm install highlightjs-1c-bsl

Оставить комментарий

// поля помечены ·

// LAVA-ORBdrag to HOT

ICE COLD WARM HOT FIRE

>