Подключил 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