Skip to content

feat(ide): подсветка парных ключевых слов блоков и директив #Область - #431

Open
flamber-dev wants to merge 1 commit into
itrous:developfrom
flamber-dev:pr/highlight-paired-keywords
Open

flamber-dev wants to merge 1 commit into
itrous:developfrom
flamber-dev:pr/highlight-paired-keywords

Conversation

@flamber-dev

Copy link
Copy Markdown
Contributor

Что видит пользователь

Сейчас курсор на Если, Цикл, Попытка, Процедура или #Область не подсвечивает ничего: document_highlights
отвечает только на имя (идентификатор), а на ключевом слове возвращает пустой список. В длинном методе, где КонецЕсли
стоит на два экрана ниже, найти конец блока можно только глазами по отступам.

После правки курсор на любом структурном слове конструкции подсвечивает все структурные слова этой одной конструкции -
как редактор подсвечивает парную скобку:

Если А Тогда            // курсор на любом из подсвеченных слов
	Если Б Тогда        // вложенный Если НЕ подсвечивается
	КонецЕсли;
ИначеЕсли В Тогда
Иначе
КонецЕсли;

Подсвечиваются: Если, Тогда, ИначеЕсли, Тогда, Иначе, КонецЕсли внешнего блока; курсор на вложенном Если
подсвечивает только его тройку.

Какие пары

Конструкция Слова
Если Если, Тогда, ИначеЕсли (+ его Тогда), Иначе, КонецЕсли
Пока Пока, Цикл, КонецЦикла
Для ... По Для, Цикл, КонецЦикла
Для Каждого Для, Каждого, Цикл, КонецЦикла
Попытка Попытка, Исключение, КонецПопытки
Процедура / Функция Процедура/КонецПроцедуры, Функция/КонецФункции (в том числе под &НаСервере и т.п.)
#Если #Если, Тогда, #ИначеЕсли (+ его Тогда), #Иначе, #КонецЕсли
#Область #Область и парная ей #КонецОбласти (и наоборот)

Ответ одинаковый с любого слова конструкции, в том числе с закрывающего. Английские написания (If/Then/ElsIf/
Else/EndIf, While/Do/EndDo, Try/Except/EndTry, Procedure/EndProcedure, #If/#EndIf,
#Region/#EndRegion) дают те же виды токенов и работают так же. Вид подсветки - Text.

Исправление

Метод LSP - textDocument/documentHighlight; обработчик в bsl-analyzer не меняется, вся правка в
crates/ide/src/document_highlight.rs, до прежнего поиска символа:

  • Ключевые слова. Слово считается структурным, только если стоит прямо в узле конструкции (IF_STMT,
    WHILE_STMT, FOR_STMT, FOR_EACH_STMT, TRY_STMT, PROCEDURE_DEF, FUNCTION_DEF, PRE_IF_DIR) или в его узле-ветке
    (ELSIF_CLAUSE, ELSE_CLAUSE, EXCEPT_CLAUSE, PRE_ELSIF_CLAUSE, PRE_ELSE_CLAUSE). Собираются слова из набора этой
    конструкции только с этих двух уровней, поэтому вложенные конструкции в ответ не попадают, а то же слово в другой
    позиции (например, в узле ошибки разбора) не считается структурным и уходит в прежнюю ветку поиска символа.
  • Области. #Область/#КонецОбласти - плоские маркеры, не контейнеры (см. grammar::preprocessor_region), поэтому пара
    восстанавливается стеком в порядке исходного текста - так же, как её строит hir-def::region_tree. Своё сопоставление,
    а не RegionTree, потому что дерево областей не хранит безымянные #Область, а пару нужно найти и для них.
  • Незаконченный код. Конструкция, которую разбор оставил без закрывающего слова, и маркер области без пары отвечают
    пустым списком: половина пары указывала бы на конец блока, которого нет.
  • На идентификаторе поведение прежнее (поиск ссылок в файле).

Единственный существующий тест, который менялся: no_symbol_returns_empty_highlights ставил курсор на Процедура и
ждал пустой ответ - теперь там законная пара, курсор перенесён на (, смысл теста (нет символа - нет подсветки)
сохранён.

Доказательство

Новый файл crates/ide/tests/document_highlight_paired_keywords.rs (через публичный Analysis::document_highlights,
полное сравнение списка (строка, текст); в каждом входе рядом стоит вложенная конструкция того же вида):

  • if_keywords_light_every_branch_but_not_the_nested_if - курсор на каждом из шести слов внешнего Если даёт один и тот
    же ответ; вложенный Если - только свою тройку, в том числе с КонецЕсли;
  • keyword_highlights_are_text_kind - вид подсветки Text;
  • loop_keywords_pair_with_their_end_do - Для Каждого / Для ... По / Пока, вложенные друг в друга, каждый
    с начала и с КонецЦикла;
  • try_keywords_pair_and_skip_the_nested_try - внешний и вложенный Попытка, курсор на Исключение/КонецПопытки;
  • method_keywords_pair_under_an_annotation - &НаСервере Функция ... КонецФункции с Если внутри, затем процедура;
  • nested_regions_pair_with_the_balanced_partner - внешняя область и две вложенные: с каждого конца пара - сбалансированный
    партнёр, а не ближайший маркер;
  • preprocessor_if_lights_its_branches_not_the_code_inside - #Если/#ИначеЕсли/#Иначе/#КонецЕсли вокруг процедуры с
    Если: ответ с каждой директивы и с её Тогда не задевает код внутри; Тогда обычного Если внутри - его тройка;
  • english_keywords_pair_like_russian_ones - If, While, Try, Procedure, #If, #Region;
  • identifier_inside_a_block_keeps_its_symbol_highlight - контроль: идентификатор внутри блока подсвечивается как раньше
    (Text на объявлении, Write на присваивании);
  • unfinished_pairs_answer_nothing - контроль: Если без КонецЕсли, #Область без пары, лишняя #КонецОбласти,
    процедура без КонецПроцедуры - пустой ответ, без паники.

Тесты лежат отдельным интеграционным файлом, чтобы их можно было прогнать на develop без правки.

На develop (34a3fba), только тесты:

     Running tests\document_highlight_paired_keywords.rs (...\debug\deps\document_highlight_paired_keywords-42f643112c9afc7f.exe)
running 10 tests
thread 'nested_regions_pair_with_the_balanced_partner' (108692) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:195:5:
  left: []
 right: [(0, "#Область"), (7, "#КонецОбласти")]
thread 'preprocessor_if_lights_its_branches_not_the_code_inside' (127524) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:230:9:
  left: []
 right: [(0, "#Если"), (0, "Тогда"), (5, "#ИначеЕсли"), (5, "Тогда"), (6, "#Иначе"), (7, "#КонецЕсли")]
thread 'keyword_highlights_are_text_kind' (133488) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:106:5:
  left: 0
 right: 6
thread 'try_keywords_pair_and_skip_the_nested_try' (135136) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:152:5:
  left: []
 right: [(1, "Попытка"), (7, "Исключение"), (9, "КонецПопытки")]
thread 'loop_keywords_pair_with_their_end_do' (121624) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:124:5:
  left: []
 right: [(1, "Для"), (1, "Каждого"), (1, "Цикл"), (7, "КонецЦикла")]
thread 'english_keywords_pair_like_russian_ones' (83936) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:260:5:
  left: []
 right: [(3, "If"), (3, "Then"), (6, "ElsIf"), (6, "Then"), (7, "Else"), (8, "EndIf")]
thread 'method_keywords_pair_under_an_annotation' (112152) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:175:5:
  left: []
 right: [(1, "Функция"), (6, "КонецФункции")]
thread 'if_keywords_light_every_branch_but_not_the_nested_if' (128852) panicked at crates\ide\tests\document_highlight_paired_keywords.rs:90:9:
  left: []
 right: [(1, "Если"), (1, "Тогда"), (5, "ИначеЕсли"), (5, "Тогда"), (7, "Иначе"), (9, "КонецЕсли")]
test result: FAILED. 2 passed; 8 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.75s

На develop проходят только два контрольных теста - identifier_inside_a_block_keeps_its_symbol_highlight и
unfinished_pairs_answer_nothing.

На ветке:

running 10 tests
test keyword_highlights_are_text_kind ... ok
test method_keywords_pair_under_an_annotation ... ok
test try_keywords_pair_and_skip_the_nested_try ... ok
test unfinished_pairs_answer_nothing ... ok
test english_keywords_pair_like_russian_ones ... ok
test nested_regions_pair_with_the_balanced_partner ... ok
test preprocessor_if_lights_its_branches_not_the_code_inside ... ok
test loop_keywords_pair_with_their_end_do ... ok
test if_keywords_light_every_branch_but_not_the_nested_if ... ok
test identifier_inside_a_block_keeps_its_symbol_highlight ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.77s

Проверка

  • cargo test -p ide --no-fail-fast: списки упавших тестов на develop и на ветке совпадают, кроме 8 новых тестов,
    которые падают только на develop (9 упавших на ветке, все из-за окружения Windows: graph::tests::method_graph_id_*,
    name_lookup::tests::the_address_phases_observe_a_cancelled_request, a_self_qualified_call_* и соседние с ними);
    на ветке 1342 прошедших против 1334 на develop.
  • cargo fmt -p ide --check: чисто.
  • cargo clippy -p ide --all-targets: замечаний по ide нет ни на develop, ни на ветке.

Вне рамок

  • #Вставка/#КонецВставки и #Удаление/#КонецУдаления (расширения) - не подсвечиваются; их легко добавить тем же
    способом, если нужно.
  • По в Для ... По, Из в Для Каждого ... Из, Асинх перед методом, Возврат/Прервать/Продолжить внутри
    блока в пару не входят.
  • Перекрёстные случаи, когда #Если открывается внутри Если, а ИначеЕсли стоит внутри области #Если: ответ
    следует дереву разбора как есть, отдельной обработки нет.
  • Курсор на Тогда/Цикл, которые разбор восстановил в узел ошибки, - пустой ответ, как раньше.

🤖 Generated with Claude Code

On a structural keyword textDocument/documentHighlight now answers the
keywords of that one construct, the way an editor matches a bracket:
Если/Тогда/ИначеЕсли/Иначе/КонецЕсли, Пока|Для|Для Каждого ... Цикл/
КонецЦикла, Попытка/Исключение/КонецПопытки, Процедура/КонецПроцедуры,
Функция/КонецФункции and #Если/#ИначеЕсли/#Иначе/#КонецЕсли. A region
directive answers itself and its balanced partner. English spellings
share the token kinds and work the same way.

Only a keyword standing in the construct node or one of its branch nodes
counts, so nested constructs keep their own keywords. A construct left
without its closing keyword, and a region marker without a partner,
answer nothing. Identifiers keep the symbol highlight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@itrous

itrous commented Oct 15, 2026

Copy link
Copy Markdown
Owner

🤖 Начата автоматическая приёмка — этот pull request взят в работу ИИ-агентом:
изменения переносятся на актуальную целевую ветку, проходят независимое ревью
и соответствующие тесты, очевидные замечания правятся по ходу. Обновления появятся
в этом треде; merge возможен только после завершения проверки.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants