Skip to content

feat(ide): директивы компиляции и ключевые слова объявления различимы в семантической подсветке - #429

Open
flamber-dev wants to merge 1 commit into
itrous:developfrom
flamber-dev:pr/highlight-directives-and-declarations
Open

flamber-dev wants to merge 1 commit into
itrous:developfrom
flamber-dev:pr/highlight-directives-and-declarations

Conversation

@flamber-dev

Copy link
Copy Markdown
Contributor

Проблема

Семантическая подсветка отдаёт клиенту один и тот же тип токена для разных по смыслу конструкций, поэтому тема не может
раскрасить их по-разному:

  • директива компиляции (&НаКлиенте, &НаСервере, &НаСервереБезКонтекста, …) и аннотация расширения
    (&Перед("..."), &После("..."), &Вместо("..."), &ИзменениеИКонтроль("...")) - обе decorator. Директива
    задаёт контекст исполнения метода, аннотация расширения - перехват чужого метода; на методе расширения они стоят
    рядом и сливаются:

    &НаСервере
    &После("ПриСозданииНаСервере")
    Процедура Расш_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
  • ключевые слова объявления (Процедура, Функция, КонецПроцедуры, КонецФункции, Перем, Знач, Экспорт,
    Асинх) и управляющие (Если, Цикл, Возврат, …) - все keyword без модификаторов. Привычная по Конфигуратору
    и по TextMate-грамматикам схема, где объявления окрашены иначе, чем управление, в семантической подсветке
    недостижима: семантический токен перекрывает цвет грамматики.

Инструкции препроцессора (#Если, #Область, …) уже отдаются как macro и в этом PR не меняются.

Что меняется

Конструкция develop ветка
&НаКлиенте, &НаСервере, &НаСервереБезКонтекста, &НаКлиентеНаСервере, &НаКлиентеНаСервереБезКонтекста (и &AtClient, …) decorator modifier
&Перед, &После, &Вместо, &ИзменениеИКонтроль (и &Before, …), пользовательские аннотации decorator decorator
Процедура, Функция, КонецПроцедуры, КонецФункции, Перем, Знач, Экспорт, Асинх (и Procedure, …) keyword keyword + модификатор declaration
Если, Тогда, Цикл, Возврат, Новый, Ждать, … keyword keyword
#Если, #КонецЕсли, #Область, #КонецОбласти, #Вставка, … macro macro
имя области (#Область Имя) без токена без токена

Оба новых признака - стандартные имена LSP 3.17 (SemanticTokenTypes.modifier,
SemanticTokenModifiers.declaration), собственных типов не добавлено. Поэтому клиент не требует настройки:

  • modifier: VS Code сопоставляет этот тип TextMate-области storage.modifier, и любая тема, раскрашивающая
    storage.modifier, раскрашивает директиву без доработок; раньше директива получала цвет decorator
    (entity.name.decorator, а в темах без него - цвет функции). Аннотации расширений остаются decorator, так что на
    методе расширения две строки теперь различимы в штатной теме.

  • keyword + declaration: тип прежний, поэтому в штатных темах ключевые слова выглядят как раньше; клиенты,
    не читающие модификаторы, видят ровно то же, что на develop. Отличить объявления можно правилом пользователя
    или темы, например в VS Code:

    "editor.semanticTokenColorCustomizations": {
        "rules": { "keyword.declaration": "#569CD6" }
    }

    Отдельный тип (storage и т.п.) здесь сознательно не вводится: нестандартный тип клиент без semanticTokenScopes
    не знает и оставляет такой токен без семантического цвета, то есть объявления в штатной теме потеряли бы цвет
    ключевого слова и остались бы на цвете TextMate-грамматики, если она вообще подключена.

Легенда

semantic_tokens_legend(): в token_types добавлен в конец modifier (индекс 16); прежние индексы 0-15 не
сдвинулись. Модификатор declaration в легенде уже был (бит 3) - сейчас им помечаются объявления переменных и
параметров, теперь и ключевые слова объявления. Клиент получает легенду при инициализации, так что совместимость
обратная: старый клиент с новой легендой работает, ничего не переназначено.

Код

  • crates/syntax/src/syntax_kind.rs: SyntaxKind::is_compilation_directive() (подмножество is_annotation) и
    SyntaxKind::is_declaration_keyword() (подмножество is_keyword) - классификация видов лексем живёт рядом с
    is_keyword/is_annotation/is_preprocessor.
  • crates/ide/src/syntax_highlighting.rs: новый HlTag::CompilerDirective (as_str() == "modifier");
    highlight_token проверяет директиву раньше общей ветки аннотаций и ставит HlMod::DECLARATION ключевым словам
    объявления.
  • crates/bsl-analyzer/src/lsp/to_proto.rs: SemanticTokenType::MODIFIER в легенде, HlTag::CompilerDirective => 16.
  • crates/ide/src/syntax_highlighting/token_stream_tests.rs: снапшоты обновлены (см. ниже).

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

Два новых интеграционных файла, чтобы их можно было прогнать на develop без правки (они сравнивают строки типа и
модификаторов, которые видит тема, а не варианты HlTag, поэтому компилируются и на develop):

crates/ide/tests/highlight_directives_and_declarations.rs (уровень ide::highlight):

  • compilation_directives_are_modifiers_not_decorators - пять директив, русское написание → modifier;
  • compilation_directives_english_are_modifiers - те же пять, английское написание;
  • extension_annotations_stay_decorators - &Перед/&После/&Вместо/&ИзменениеИКонтроль и
    &Before/&After/&Around/&ChangeAndValidate остаются decorator (контроль);
  • directive_and_annotation_on_one_method_get_different_types - &НаСервере + &После("...") на одном методе;
  • preprocessor_instructions_stay_macros_and_region_names_stay_untagged - #Если/#КонецЕсли/#Область/#КонецОбласти
    и английские формы остаются macro, а имя области, совпадающее с именем процедуры, по-прежнему без токена
    (контроль: регресс fix(ide): имя в директиве #Область не подсвечивается как вызов метода #318 не допущен);
  • declaration_keywords_carry_the_declaration_modifier - Перем, Экспорт, Асинх, Процедура, Знач,
    КонецПроцедуры, Функция, КонецФункции - keyword + declaration; Если, Тогда, Возврат, КонецЕсли,
    Пока, Цикл, КонецЦикла, Новый, Ждать - keyword без него;
  • declaration_keywords_english_carry_the_declaration_modifier - то же для английских слов.

crates/bsl-analyzer/tests/semantic_tokens_directives_and_declarations.rs (уровень LSP: токены кодируются
semantic_tokens и декодируются через semantic_tokens_legend() так, как это делает клиент):

  • legend_advertises_the_modifier_token_type - в легенде есть тип modifier и модификатор declaration;
  • client_decodes_directives_annotations_and_declaration_keywords - &НаСервере → modifier, &После →
    decorator, Процедура/Знач/Экспорт/КонецПроцедуры → keyword + declaration, Если/Тогда/Возврат/КонецЕсли →
    keyword без него.

На develop (4467ba9), только тесты:

Running tests\highlight_directives_and_declarations.rs (<target>/…exe)

running 7 tests
test extension_annotations_stay_decorators ... ok
test declaration_keywords_english_carry_the_declaration_modifier ... FAILED
test directive_and_annotation_on_one_method_get_different_types ... FAILED
test compilation_directives_english_are_modifiers ... FAILED
test compilation_directives_are_modifiers_not_decorators ... FAILED
test preprocessor_instructions_stay_macros_and_region_names_stay_untagged ... ok
test declaration_keywords_carry_the_declaration_modifier ... FAILED

failures:

---- declaration_keywords_english_carry_the_declaration_modifier stdout ----

thread 'declaration_keywords_english_carry_the_declaration_modifier' (132164) panicked at crates\ide\tests\highlight_directives_and_declarations.rs:37:9:
assertion `left == right` failed: `declaration` modifier of "Var": []
  left: false
 right: true

---- directive_and_annotation_on_one_method_get_different_types stdout ----

thread 'directive_and_annotation_on_one_method_get_different_types' (47712) panicked at crates\ide\tests\highlight_directives_and_declarations.rs:36:9:
assertion `left == right` failed: token type of "&НаСервере"
  left: "decorator"
 right: "modifier"

---- compilation_directives_english_are_modifiers stdout ----

thread 'compilation_directives_english_are_modifiers' (11268) panicked at crates\ide\tests\highlight_directives_and_declarations.rs:36:9:
assertion `left == right` failed: token type of "&AtClient"
  left: "decorator"
 right: "modifier"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- compilation_directives_are_modifiers_not_decorators stdout ----

thread 'compilation_directives_are_modifiers_not_decorators' (130908) panicked at crates\ide\tests\highlight_directives_and_declarations.rs:36:9:
assertion `left == right` failed: token type of "&НаКлиенте"
  left: "decorator"
 right: "modifier"

---- declaration_keywords_carry_the_declaration_modifier stdout ----

thread 'declaration_keywords_carry_the_declaration_modifier' (11976) panicked at crates\ide\tests\highlight_directives_and_declarations.rs:37:9:
assertion `left == right` failed: `declaration` modifier of "Перем": []
  left: false
 right: true


failures:
    compilation_directives_are_modifiers_not_decorators
    compilation_directives_english_are_modifiers
    declaration_keywords_carry_the_declaration_modifier
    declaration_keywords_english_carry_the_declaration_modifier
    directive_and_annotation_on_one_method_get_different_types

test result: FAILED. 2 passed; 5 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s

error: test failed, to rerun pass `-p ide --test highlight_directives_and_declarations`
Running tests\semantic_tokens_directives_and_declarations.rs (<target>/…exe)

running 2 tests
test legend_advertises_the_modifier_token_type ... FAILED
test client_decodes_directives_annotations_and_declaration_keywords ... FAILED

failures:

---- legend_advertises_the_modifier_token_type stdout ----

thread 'legend_advertises_the_modifier_token_type' (99360) panicked at crates\bsl-analyzer\tests\semantic_tokens_directives_and_declarations.rs:74:5:
legend: [SemanticTokenType("keyword"), SemanticTokenType("function"), SemanticTokenType("parameter"), SemanticTokenType("variable"), SemanticTokenType("string"), SemanticTokenType("number"), SemanticTokenType("comment"), SemanticTokenType("macro"), SemanticTokenType("decorator"), SemanticTokenType("property"), SemanticTokenType("operator"), SemanticTokenType("unresolvedReference"), SemanticTokenType("type"), SemanticTokenType("enumMember"), SemanticTokenType("namespace"), SemanticTokenType("class")]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- client_decodes_directives_annotations_and_declaration_keywords stdout ----

thread 'client_decodes_directives_annotations_and_declaration_keywords' (43656) panicked at crates\bsl-analyzer\tests\semantic_tokens_directives_and_declarations.rs:88:5:
assertion `left == right` failed
  left: "decorator"
 right: "modifier"


failures:
    client_decodes_directives_annotations_and_declaration_keywords
    legend_advertises_the_modifier_token_type

test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s

error: test failed, to rerun pass `-p bsl-analyzer --test semantic_tokens_directives_and_declarations`

На ветке:

Running tests\highlight_directives_and_declarations.rs (<target>/…exe)

running 7 tests
test directive_and_annotation_on_one_method_get_different_types ... ok
test compilation_directives_english_are_modifiers ... ok
test compilation_directives_are_modifiers_not_decorators ... ok
test extension_annotations_stay_decorators ... ok
test declaration_keywords_english_carry_the_declaration_modifier ... ok
test preprocessor_instructions_stay_macros_and_region_names_stay_untagged ... ok
test declaration_keywords_carry_the_declaration_modifier ... ok

test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.27s
Running tests\semantic_tokens_directives_and_declarations.rs (<target>/…exe)

running 2 tests
test legend_advertises_the_modifier_token_type ... ok
test client_decodes_directives_annotations_and_declaration_keywords ... ok

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

Изменённые снапшоты

token_stream_tests.rs закрепляет класс каждого токена, поэтому в нём меняются ровно две вещи - ключевые слова
объявления получили [DECLARATION], &НаСервере стал CompilerDirective. Диапазоны и тексты токенов не сдвинулись ни
в одной строке. Два из четырёх тестов файла помечены corpus_contract; их снапшоты обновлены и проверены прогоном с
--cfg corpus_contract на закреплённом корпусе (SHA-256 3c759994…cacce):

-            35..45 Keyword "Перем"
+            35..45 Keyword [DECLARATION] "Перем"
-            85..99 Keyword "Экспорт"
+            85..99 Keyword [DECLARATION] "Экспорт"
-            123..133 Keyword "Перем"
+            123..133 Keyword [DECLARATION] "Перем"
-            173..192 Annotation "&НаСервере"
-            193..211 Keyword "Процедура"
+            173..192 CompilerDirective "&НаСервере"
+            193..211 Keyword [DECLARATION] "Процедура"
-            276..290 Keyword "Экспорт"
-            295..305 Keyword "Перем"
+            276..290 Keyword [DECLARATION] "Экспорт"
+            295..305 Keyword [DECLARATION] "Перем"
-            561..589 Keyword "КонецПроцедуры"
-            591..605 Keyword "Функция"
+            561..589 Keyword [DECLARATION] "КонецПроцедуры"
+            591..605 Keyword [DECLARATION] "Функция"
-            767..791 Keyword "КонецФункции"
+            767..791 Keyword [DECLARATION] "КонецФункции"
-            0..18 Keyword "Процедура"
+            0..18 Keyword [DECLARATION] "Процедура"
-            427..455 Keyword "КонецПроцедуры"
+            427..455 Keyword [DECLARATION] "КонецПроцедуры"
-            0..14 Keyword "Функция"
+            0..14 Keyword [DECLARATION] "Функция"
-            512..536 Keyword "КонецФункции"
+            512..536 Keyword [DECLARATION] "КонецФункции"
-            40..49 Keyword "Procedure"
+            40..49 Keyword [DECLARATION] "Procedure"
-            62..74 Keyword "EndProcedure"
+            62..74 Keyword [DECLARATION] "EndProcedure"
running 4 tests
test syntax_highlighting::token_stream_tests::documentation_token_stream ... ok
test syntax_highlighting::token_stream_tests::sdbl_query_token_stream ... ok
test syntax_highlighting::token_stream_tests::module_shape_token_stream ... ok
test syntax_highlighting::token_stream_tests::mdo_and_typed_member_token_stream ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 605 filtered out; finished in 0.95s

Полные прогоны

cargo test -p ide -p syntax --no-fail-fast и cargo test -p bsl-analyzer --no-fail-fast на develop и на ветке (Windows): списки упавших
совпадают, кроме новых тестов, которые на develop падают, а на ветке проходят.

Упавшие на обеих сторонах одинаково (фон Windows и тесты отмены по времени, к подсветке отношения не имеют):

  • -p ide/-p syntax: 9 тестов - graph::tests::method_graph_id_file_fallback_normalization,
    graph::tests::method_graph_id_matches_encoder_and_round_trips_for_path_fallback,
    name_lookup::tests::the_address_phases_observe_a_cancelled_request и 6 тестов infer_three_level
    (a_self_qualified_call_*, manager_typed_local_is_not_reported_as_self_qualified_access,
    self_qualified_call_in_{constant,report}_manager_still_checks_the_method). На develop к ним добавляются 5 новых
    тестов из highlight_directives_and_declarations.
  • -p bsl-analyzer: 45 общих тестов на обеих сторонах (CLI, журнал и базовые линии на путях Windows, bench::*,
    mcp_install::*, server::tests::g1_*). На develop к ним добавляются 2 новых теста
    semantic_tokens_directives_and_declarations. Ещё два теста live-VFS
    (g1_live_vfs_pending_write_cancels_real_definition_then_fresh_request_succeeds,
    g1_propagated_without_new_revision_is_refused_controller_control) в одном полном прогоне упали только на ветке. Это
    тесты отмены по времени: при трёх повторах cargo test -p bsl-analyzer --lib g1_ на каждой стороне набор упавших
    g1_* меняется от прогона к прогону, и g1_propagated_without_new_revision_is_refused_controller_control падает и на
    develop (второй повтор).

cargo fmt --all -- --check - чисто. cargo clippy -p syntax -p ide -p bsl-analyzer --all-targets (без -D warnings:
под Windows он останавливается на чужих предупреждениях) - набор предупреждений совпадает с develop (на обеих сторонах только 2 предупреждения в mcp-server, в затронутых крейтах - ни одного).

Вне рамок

  • Отдельные цвета для инструкций препроцессора разных видов (#Область против #Если) - они уже macro, а имя
    области намеренно остаётся без токена (fix(ide): имя в директиве #Область не подсвечивается как вызов метода #318).
  • Модификатор для управляющих ключевых слов (как controlFlow у rust-analyzer) - нестандартное имя, и для задачи
    «отделить объявления» достаточно одного стандартного модификатора.
  • Асинх по-прежнему без модификатора async; Новый, Выполнить, ДобавитьОбработчик/УдалитьОбработчик и
    литералы Истина/Ложь/Неопределено/NULL остаются обычными keyword.
  • Сопоставление в клиентских расширениях (semanticTokenScopes) - в этом репозитории клиента VS Code нет, а
    стандартные имена в нём и не нуждаются.

… own semantic tokens

Compilation directives (&AtClient, &AtServer, ...) were emitted as `decorator`,
the same type as extension annotations (&Before, &After, &Around,
&ChangeAndValidate), and declaration keywords (Procedure, Function,
EndProcedure, EndFunction, Var, Val, Export, Async) were plain `keyword`
like control-flow keywords, so a theme could not tell them apart.

Directives now map to the standard LSP token type `modifier` (appended to
the legend at index 16; existing indices unchanged), and declaration keywords
keep the `keyword` type but carry the standard `declaration` modifier, so
clients that ignore modifiers see no change. The kind classification lives in
`SyntaxKind::is_compilation_directive` / `is_declaration_keyword`.

Token-stream snapshots change only in those two classifications; offsets and
token texts are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant