Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ markers = [
"ocaml: language server running for OCaml and Reason",
"scala: language server running for Scala",
"al: language server running for AL (Microsoft Dynamics 365 Business Central)",
"bsl: language server running for BSL (1C:Enterprise)",
"fsharp: language server running for F#",
"rego: language server running for Rego",
"markdown: language server running for Markdown",
Expand Down
21 changes: 21 additions & 0 deletions src/serena/resources/config/contexts/bsl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
description: Context for 1C Enterprise BSL development
prompt: |
You are working with a 1C:Enterprise codebase using BSL (Built-in Scripting Language).
Guidelines:
- Use Cyrillic-aware identifier regex: [A-Za-zА-Яа-я_][A-Za-zА-Яа-я0-9_]*
- Prefer semantic tools (find_symbol, find_referencing_symbols) for rename operations
- For cross-file operations, check forms (.xml) in addition to modules (.bsl)
- Manager modules, forms, info registers, and common modules have different
visibility semantics — consult SymbolKind before renaming
- Export methods (marked with keyword Export) are visible across modules;
module-local methods are not
- EDT project structure: src/Catalogs/*/Ext/{ManagerModule,ObjectModule,Forms/*/Ext/Form/Module}.bsl
allowed_tools:
- find_symbol
- find_referencing_symbols
- replace_symbol_body
- insert_after_symbol
- insert_before_symbol
- search_for_pattern
excluded_tools:
- execute_shell_command
549 changes: 549 additions & 0 deletions src/solidlsp/language_servers/bsl_language_server.py

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/solidlsp/ls_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Language(str, Enum):
ERLANG = "erlang"
OCAML = "ocaml"
AL = "al"
BSL = "bsl" # 1C:Enterprise and OneScript language
FSHARP = "fsharp"
REGO = "rego"
SCALA = "scala"
Expand Down Expand Up @@ -268,6 +269,8 @@ def get_source_fn_matcher(self) -> FilenameMatcher:
return FilenameMatcher("*.ml", "*.mli", "*.re", "*.rei")
case self.AL:
return FilenameMatcher("*.al", "*.dal")
case self.BSL:
return FilenameMatcher("*.bsl", "*.os", "*.bsl")
case self.FSHARP:
return FilenameMatcher("*.fs", "*.fsx", "*.fsi")
case self.REGO:
Expand Down Expand Up @@ -492,6 +495,10 @@ def get_ls_class(self) -> type["SolidLanguageServer"]:
from solidlsp.language_servers.r_language_server import RLanguageServer

return RLanguageServer
case self.BSL:
from solidlsp.language_servers.bsl_language_server import BslLanguageServer

return BslLanguageServer
case self.SCALA:
from solidlsp.language_servers.scala_language_server import ScalaLanguageServer

Expand Down
161 changes: 161 additions & 0 deletions test/resources/repos/bsl/test_repo/ОбщийМодуль.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Общий модуль с утилитами

#Область СтроковыеФункции

// Функция проверки заполненности строки
Функция СтрокаЗаполнена(Значение) Экспорт
Возврат ЗначениеЗаполнено(Значение) И НЕ ПустаяСтрока(СокрЛП(Значение));
КонецФункции

// Замена подстроки в строке
Функция ЗаменитьПодстроку(ИсходнаяСтрока, Подстрока, НоваяПодстрока) Экспорт
Возврат СтрЗаменить(ИсходнаяСтрока, Подстрока, НоваяПодстрока);
КонецФункции

// Разделение строки на массив
Функция РазделитьСтроку(Строка, Разделитель = ",") Экспорт
МассивСтрок = Новый Массив;

Если НЕ СтрокаЗаполнена(Строка) Тогда
Возврат МассивСтрок;
КонецЕсли;

НачальнаяПозиция = 1;
ДлинаСтроки = СтрДлина(Строка);
ДлинаРазделителя = СтрДлина(Разделитель);

Пока НачальнаяПозиция <= ДлинаСтроки Цикл
ПозицияРазделителя = СтрНайти(Строка, Разделитель, НачальнаяПозиция);

Если ПозицияРазделителя = 0 Тогда
ПодСтрока = Сред(Строка, НачальнаяПозиция);
МассивСтрок.Добавить(СокрЛП(ПодСтрока));
Прервать;
Иначе
ПодСтрока = Сред(Строка, НачальнаяПозиция, ПозицияРазделителя - НачальнаяПозиция);
МассивСтрок.Добавить(СокрЛП(ПодСтрока));
НачальнаяПозиция = ПозицияРазделителя + ДлинаРазделителя;
КонецЕсли;
КонецЦикла;

Возврат МассивСтрок;
КонецФункции

#КонецОбласти

#Область РаботаСМассивами

// Функция поиска в массиве
Функция НайтиВМассиве(Массив, ИскомоеЗначение) Экспорт
Для Индекс = 0 По Массив.ВГраница() Цикл
Если Массив[Индекс] = ИскомоеЗначение Тогда
Возврат Индекс;
КонецЕсли;
КонецЦикла;

Возврат -1;
КонецФункции

// Удаление дублей из массива
Функция УдалитьДубли(Массив) Экспорт
УникальныйМассив = Новый Массив;

Для Каждого Элемент Из Массив Цикл
Если УникальныйМассив.Найти(Элемент) = Неопределено Тогда
УникальныйМассив.Добавить(Элемент);
КонецЕсли;
КонецЦикла;

Возврат УникальныйМассив;
КонецФункции

// Объединение массивов
Функция ОбъединитьМассивы(Массив1, Массив2) Экспорт
РезультирующийМассив = Новый Массив;

Для Каждого Элемент Из Массив1 Цикл
РезультирующийМассив.Добавить(Элемент);
КонецЦикла;

Для Каждого Элемент Из Массив2 Цикл
РезультирующийМассив.Добавить(Элемент);
КонецЦикла;

Возврат РезультирующийМассив;
КонецФункции

#КонецОбласти

#Область РаботаСДатами

// Получение начала дня
Функция НачалоДня(Дата) Экспорт
Возврат НачалоДня(Дата);
КонецФункции

// Получение конца дня
Функция КонецДня(Дата) Экспорт
Возврат КонецДня(Дата);
КонецФункции

// Рабочие дни между датами
Функция КоличествоРабочихДней(ДатаНачала, ДатаОкончания) Экспорт
КоличествоДней = 0;
ТекущаяДата = ДатаНачала;

Пока ТекущаяДата <= ДатаОкончания Цикл
ДеньНедели = ДеньНедели(ТекущаяДата);
Если ДеньНедели >= 1 И ДеньНедели <= 5 Тогда // Понедельник - Пятница
КоличествоДней = КоличествоДней + 1;
КонецЕсли;
ТекущаяДата = ТекущаяДата + 86400; // +1 день в секундах
КонецЦикла;

Возврат КоличествоДней;
КонецФункции

#КонецОбласти

#Область Валидация

// Проверка ИНН
Функция ПроверитьИНН(ИНН) Экспорт
ИНН = СокрЛП(ИНН);

Если НЕ СтрокаЗаполнена(ИНН) Тогда
Возврат Ложь;
КонецЕсли;

ДлинаИНН = СтрДлина(ИНН);
Если ДлинаИНН <> 10 И ДлинаИНН <> 12 Тогда
Возврат Ложь;
КонецЕсли;

// Проверка, что все символы - цифры
Для Позиция = 1 По ДлинаИНН Цикл
Символ = Сред(ИНН, Позиция, 1);
Если НЕ (Символ >= "0" И Символ <= "9") Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;

// Здесь может быть реализована проверка контрольных сумм

Возврат Истина;
КонецФункции

// Проверка email
Функция ПроверитьEmail(Email) Экспорт
Email = СокрЛП(Email);

Если НЕ СтрокаЗаполнена(Email) Тогда
Возврат Ложь;
КонецЕсли;

ПозицияСобаки = СтрНайти(Email, "@");
ПозицияТочки = СтрНайти(Email, ".", ПозицияСобаки);

Возврат ПозицияСобаки > 1 И ПозицияТочки > ПозицияСобаки + 1 И ПозицияТочки < СтрДлина(Email);
КонецФункции

#КонецОбласти
174 changes: 174 additions & 0 deletions test/resources/repos/bsl/test_repo/ОсновнойМодуль.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Главный модуль с примерами кода 1С:Предприятие

#Область ОписаниеПеременных

Перем глСчетчик Экспорт;
Перем глМассивДанных;
Перем глСоединение;

#КонецОбласти

#Область ПроцедурыИФункции

// Основная процедура инициализации
Процедура ИнициализацияСистемы() Экспорт
глСчетчик = 0;
глМассивДанных = Новый Массив;

// Заполнение массива тестовыми данными
Для Индекс = 1 По 10 Цикл
глМассивДанных.Добавить("Элемент_" + Строка(Индекс));
КонецЦикла;

Сообщить("Система инициализирована");
КонецПроцедуры

// Функция расчета суммы
Функция РассчитатьСумму(Число1, Число2) Экспорт
Перем Результат;

Результат = Число1 + Число2;

Возврат Результат;
КонецФункции

// Процедура обработки документов
Процедура ОбработатьДокумент(Документ, ТипОбработки = "Стандартная") Экспорт

Если ТипОбработки = "Стандартная" Тогда
ВыполнитьСтандартнуюОбработку(Документ);
ИначеЕсли ТипОбработки = "Специальная" Тогда
ВыполнитьСпециальнуюОбработку(Документ);
Иначе
ВызватьИсключение "Неизвестный тип обработки: " + ТипОбработки;
КонецЕсли;

КонецПроцедуры

// Стандартная обработка документа
Процедура ВыполнитьСтандартнуюОбработку(Документ)
Документ.Дата = ТекущаяДата();
Документ.Статус = "Обработан";

Попытка
Документ.Записать();
глСчетчик = глСчетчик + 1;
Исключение
ЗаписатьОшибку(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры

// Специальная обработка документа
Процедура ВыполнитьСпециальнуюОбработку(Документ)
// Проверка реквизитов
Если НЕ ЗначениеЗаполнено(Документ.Контрагент) Тогда
ВызватьИсключение "Не заполнен контрагент";
КонецЕсли;

// Формирование движений
НаборДвижений = Документ.Движения.ТоварыНаСкладах;
НаборДвижений.Очистить();

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

НаборДвижений.Записать();
КонецПроцедуры

// Запись ошибки в журнал
Процедура ЗаписатьОшибку(ТекстОшибки)
ЗаписьЖурналаРегистрации("Обработка.Ошибка",
УровеньЖурналаРегистрации.Ошибка,,,
ТекстОшибки);
КонецПроцедуры

#КонецОбласти

#Область РаботаСДанными

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

Запрос.УстановитьПараметр("ВидТовар", Перечисления.ВидыНоменклатуры.Товар);
Запрос.УстановитьПараметр("ВидУслуга", Перечисления.ВидыНоменклатуры.Услуга);
Запрос.УстановитьПараметр("Родитель", ПараметрыОтбора.Родитель);

Результат = Запрос.Выполнить();
Возврат Результат.Выгрузить();
КонецФункции

// Обработка табличных данных
Функция ОбработатьТаблицу(ТаблицаДанных) Экспорт
НоваяТаблица = ТаблицаДанных.Скопировать();

// Добавление новой колонки
НоваяТаблица.Колонки.Добавить("СуммаСНДС", Новый ОписаниеТипов("Число"));

// Расчет суммы с НДС
Для Каждого Строка Из НоваяТаблица Цикл
Строка.СуммаСНДС = Строка.Сумма * 1.2; // 20% НДС
КонецЦикла;

Возврат НоваяТаблица;
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Проверка прав доступа
Функция ПроверитьПраваДоступа(Пользователь, Операция)

Если Пользователь.Роль = "Администратор" Тогда
Возврат Истина;
КонецЕсли;

Если Операция = "Чтение" И Пользователь.Роль = "Пользователь" Тогда
Возврат Истина;
КонецЕсли;

Возврат Ложь;

КонецФункции

// Асинхронная обработка
&НаКлиенте
Процедура ВыполнитьАсинхронно(Команда)
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыполнения", ЭтотОбъект);
НачатьВыполнениеОперации(ОписаниеОповещения);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыполнения(Результат, ДополнительныеПараметры) Экспорт
Если Результат.Статус = "Успешно" Тогда
Сообщить("Операция выполнена успешно");
Иначе
Сообщить("Ошибка: " + Результат.ОписаниеОшибки);
КонецЕсли;
КонецПроцедуры

#КонецОбласти
Loading
Loading