Skip to content

feat(inlayhints): кликабельные LabelPart для хинтов вызова метода#4111

Merged
nixel2007 merged 3 commits into
developfrom
claude/inlayhint-clickable-labelparts
Jun 14, 2026
Merged

feat(inlayhints): кликабельные LabelPart для хинтов вызова метода#4111
nixel2007 merged 3 commits into
developfrom
claude/inlayhint-clickable-labelparts

Conversation

@nixel2007

@nixel2007 nixel2007 commented Jun 14, 2026

Copy link
Copy Markdown
Member

Что сделано

Подсказки имён параметров для вызовов source-defined методов
(SourceDefinedMethodCallInlayHintSupplier) теперь рендерятся не голой
строкой, а единственной частью InlayHintLabelPart со ссылкой
(location) на объявление соответствующего параметра в сигнатуре
вызываемого метода. Клик по подсказке выполняет переход к объявлению
параметра.

Откуда берётся location параметра

Сапплаер уже резолвит вызываемый метод (через ReferenceIndex) ради
показа имён его параметров. Эта же резолюция переиспользуется для ссылки:
URI берётся из MethodSymbol.getOwner().getUri(), диапазон — из
ParameterDefinition.getRange() соответствующего параметра.

resolveSupport

Учитывается клиентская возможность inlayHint.resolveSupport:

  • если клиент объявил отложенное разрешение свойства label.location
    ссылка строится лениво на inlayHint/resolveInlayHint.data
    кладётся новый DTO MethodCallInlayHintData с координатами объявления
    параметра; на резолве из них собирается Location и проставляется
    единственной части метки);
  • если поддержки нет — ссылка проставляется жадно.

Флаг labelLocationResolveSupport кэшируется один раз на
@EventListener(LanguageServerInitializeRequestReceivedEvent.class)
как у CompletionProvider/SignatureHelpProvider, не на каждый запрос.
Резолв диспатчится существующей инфраструктурой InlayHintProvider
(getInlayHintDataClass()MethodCallInlayHintData, авторегистрация
jackson-сабтайпа по id сапплаера).

Явный scope без type-link

  • Платформенные методы (PlatformMethodCallInlayHintSupplier) не
    имеют исходного расположения — их хинты остаются голыми строками без
    ссылок, никаких фабрикованных location.
  • Хинты типа переменной (VariableTypeInlayHintSupplier) оставлены
    как есть: в 1С нет понятия «определение типа», ссылки на типы в проекте
    отклонены ранее. Кликабельность добавлена только хинтам вызова метода,
    ведущим на реальные исходные расположения.

Тесты

  • ссылка части метки указывает на диапазон объявления параметра (жадный
    путь, без resolveSupport);
  • при resolveSupport ссылка пуста до резолва и заполняется через
    inlayHint/resolve (round-trip данных), data очищается;
  • платформенные хинты — без ссылки (поведение не изменилось);
  • существующие тесты обновлены: метка из String стала
    List<InlayHintLabelPart> с сохранением видимого текста.

Все *InlayHint* тесты зелёные, compileJava/compileTestJava
BUILD SUCCESSFUL.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for lazy resolution of parameter name inlay hint label locations when the client supports deferred hint resolution.
  • Refactor

    • Improved internal inlay hint system architecture to support multiple data types.
  • Tests

    • Updated test coverage to verify lazy and eager label resolution behaviors.

Подсказки имён параметров для вызовов source-defined методов теперь
рендерятся не голой строкой, а единственной частью InlayHintLabelPart
со ссылкой (location) на объявление соответствующего параметра в
сигнатуре вызываемого метода — клик по подсказке выполняет переход к
объявлению параметра.

Учитывается inlayHint.resolveSupport клиента: если клиент объявил
отложенное разрешение свойства label.location, ссылка строится лениво
на inlayHint/resolve (в data хинта кладутся координаты объявления
параметра), иначе проставляется жадно. Флаг поддержки кэшируется на
LanguageServerInitializeRequestReceivedEvent, как и у других провайдеров.

Платформенные методы не имеют исходного расположения, поэтому их хинты
остаются голыми строками без ссылок. Хинты типа переменной также без
ссылок — в 1С нет понятия определения типа.

Co-Authored-By: Claude Fable 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@nixel2007, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 12 minutes and 53 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6072e4c8-03db-4564-982a-209454834a20

📥 Commits

Reviewing files that changed from the base of the PR and between 729f165 and 372be70.

⛔ Files ignored due to path filters (1)
  • src/test/resources/inlayhints/VariableTypeInlayHintModuleLink.bsl is excluded by !src/test/resources/**
📒 Files selected for processing (9)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/SourceDefinedMethodCallInlayHintSupplier.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintData.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/GlobalScopeProvider.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/SourceDefinedMethodCallInlayHintSupplierTest.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintLinkTest.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintOScriptLinkTest.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplierTest.java
📝 Walkthrough

Walkthrough

AbstractMethodCallInlayHintSupplier is generified with a type parameter T extends InlayHintData. A new MethodCallInlayHintData class stores parameter declaration range coordinates and target URI for deferred resolution. SourceDefinedMethodCallInlayHintSupplier gains ClientCapabilitiesHolder injection, a cached labelLocationResolveSupport flag, and a resolve() override. PlatformMethodCallInlayHintSupplier is updated to bind DefaultInlayHintData. Tests are updated accordingly.

Changes

Lazy inlayHint/resolve for source-defined method calls

Layer / File(s) Summary
Generic base class and MethodCallInlayHintData contract
...inlayhints/AbstractMethodCallInlayHintSupplier.java, ...inlayhints/MethodCallInlayHintData.java
AbstractMethodCallInlayHintSupplier becomes generic over T extends InlayHintData, removing the hardcoded DefaultInlayHintData override. MethodCallInlayHintData is added as an immutable Lombok value class holding uri, id, targetUri, and parameter range coordinates for deferred label part resolution.
PlatformMethodCallInlayHintSupplier binds DefaultInlayHintData
...inlayhints/PlatformMethodCallInlayHintSupplier.java
Extends the generic base with DefaultInlayHintData and adds an explicit getInlayHintDataClass() override returning DefaultInlayHintData.class.
SourceDefinedMethodCallInlayHintSupplier: capability detection and resolve()
...inlayhints/SourceDefinedMethodCallInlayHintSupplier.java
Adds ClientCapabilitiesHolder constructor injection, a labelLocationResolveSupport flag cached on LanguageServerInitializeRequestReceivedEvent, getInlayHintDataClass() returning MethodCallInlayHintData.class, and a resolve() override reconstructing and attaching Location from stored coordinates.
toInlayHints and setLabelAndPadding refactor
...inlayhints/SourceDefinedMethodCallInlayHintSupplier.java
toInlayHints now carries DocumentContext; target URI is derived from the owning symbol; setLabelAndPadding constructs InlayHintLabelPart and chooses between eager location attachment or storing coordinates in MethodCallInlayHintData based on the cached capability flag.
Test infrastructure and assertion updates
...inlayhints/SourceDefinedMethodCallInlayHintSupplierTest.java
Adds ClientCapabilitiesHolder/InlayHintProvider autowiring, @AfterEach reset, enableLabelLocationResolveSupport() helper, labelValue() extraction helper. All existing label assertions are migrated to labelValue(). New tests verify eager vs. lazy label-location behavior and the full provider round-trip.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant InlayHintProvider
  participant SourceDefinedMethodCallInlayHintSupplier
  participant MethodCallInlayHintData

  Client->>InlayHintProvider: textDocument/inlayHint request
  InlayHintProvider->>SourceDefinedMethodCallInlayHintSupplier: getInlayHints(documentContext)

  alt labelLocationResolveSupport = false
    SourceDefinedMethodCallInlayHintSupplier->>SourceDefinedMethodCallInlayHintSupplier: setLabelAndPadding → attach Location to InlayHintLabelPart
    SourceDefinedMethodCallInlayHintSupplier-->>InlayHintProvider: InlayHint (label.location set, data=null)
  else labelLocationResolveSupport = true
    SourceDefinedMethodCallInlayHintSupplier->>MethodCallInlayHintData: embed targetUri + startLine/startChar/endLine/endChar
    SourceDefinedMethodCallInlayHintSupplier-->>InlayHintProvider: InlayHint (label.location unset, data=MethodCallInlayHintData)
  end

  InlayHintProvider-->>Client: InlayHint list

  opt Client resolves hint
    Client->>InlayHintProvider: inlayHint/resolve
    InlayHintProvider->>SourceDefinedMethodCallInlayHintSupplier: resolve(documentContext, unresolved, data)
    SourceDefinedMethodCallInlayHintSupplier->>MethodCallInlayHintData: read range coordinates
    SourceDefinedMethodCallInlayHintSupplier-->>InlayHintProvider: InlayHint (label.location set, data=null)
    InlayHintProvider-->>Client: resolved InlayHint
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • 1c-syntax/bsl-language-server#4107: Modifies SourceDefinedMethodCallInlayHintSupplier's getInlayHints/toInlayHints logic for building parameter label parts, directly overlapping with this PR's refactor of the same flow.
  • 1c-syntax/bsl-language-server#4100: Modifies the InlayHintSupplier<T extends InlayHintData> resolve infrastructure and AbstractMethodCallInlayHintSupplier, which this PR builds on top of.

Poem

🐇 A label once fixed, now deferred with care,
The rabbit encodes coords in data to spare.
When the client resolves, the location appears—
No eager attachment until the hint clears.
Generic types bloom where raw types once grew,
Lazy and precise—the hinting debut! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title in Russian refers to 'clickable LabelPart for method call hints', which accurately reflects the main feature added: making parameter name hints clickable with location links.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/inlayhint-clickable-labelparts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nixel2007

Copy link
Copy Markdown
Member Author

У оскрипта вполне есть пользовательские типы, как классы, так и модули. Для менеджеров объектов можно показывать ссылку на символ модуля. Аналогично для общих модулей.

Хинт выведенного типа переменной теперь рендерится частью метки
InlayHintLabelPart: когда тип объявлен в исходниках (общий модуль,
модуль менеджера объекта конфигурации, класс/модуль OneScript), к части
привязывается ссылка на объявление — клик выполняет переход к модулю/классу.
Платформенные и примитивные типы объявляющего символа не имеют — для них
метка остаётся без ссылки.

Маппинг тип -> объявляющий символ вынесен в TypeService.definingSymbol:
USER-типы берут объявление из UserType.getDeclaration(), CONFIGURATION-типы
(общие модули и менеджеры объектов) находят документ-модуль обратным индексом
GlobalScopeProvider.moduleUriByType и берут его getSymbolTree().getModule().

Построение ссылки учитывает inlayHint.resolveSupport клиента так же, как хинты
вызова метода: при поддержке label.location ссылка откладывается на
inlayHint/resolve (координаты в data), иначе проставляется жадно.

Co-Authored-By: Claude Fable 5 <[email protected]>
@nixel2007

Copy link
Copy Markdown
Member Author

Учёл. Расширил: типовой хинт теперь кликабелен, когда тип резолвится в исходный символ —

  • пользовательские типы OneScript (классы и модули) → объявляющий .os-модуль;
  • общие модули BSL и модули-менеджеры объектов → символ модуля (для менеджеров добавил обратный индекс GlobalScopeProvider.moduleUriByType, т.к. через common-module-маршрут они не доставались);
  • платформенные/примитивные типы — без ссылки.

Единый резолвер TypeService.definingSymbol(TypeRef, DocumentContext); resolveSupport(label.location) учтён (ленивый location). Коммит 2e4126a.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

 3 402 files  +12   3 402 suites  +12   1h 33m 15s ⏱️ +52s
 3 371 tests +14   3 353 ✅ +14   18 💤 ±0  0 ❌ ±0 
20 226 runs  +84  20 118 ✅ +84  108 💤 ±0  0 ❌ ±0 

Results for commit 2e4126a. ± Comparison against base commit 6e380f3.

@nixel2007

Copy link
Copy Markdown
Member Author

Вопрос к обсуждению: можно ли обойтись без нового обратного индекса GlobalScopeProvider.uriByModuleType/moduleUriByType(TypeRef) и использовать существующую инфраструктуру? Разобрал по коду:

  • Через ReferenceIndex напрямую — нет. ReferenceIndex резолвит по Symbol-сущности, несущей mdoRef/moduleType (ключ getSourceDefinedSymbol), и не имеет точки входа по TypeRef. А TypeRef — это record(kind, qualifiedName) (TypeRef.java:47), mdoRef в нём нет.
  • Общие модули + типы OneScript — новый индекс не нужен:
    • общий модуль: getConfiguration().findCommonModule(qualifiedName) → MD.getMdoRef() → getDocument(mdoRef, CommonModule) → symbolTree.getModule() (всё существующее);
    • OScript: UserType.getDeclaration() (мимо индекса вообще).
  • Модули-менеджеры объектов — затык: их TypeRef.qualifiedName это display-обёртка СправочникМенеджер.X (ConfigurationModuleMembersProvider.java:120), из неё mdoRef существующими API не восстановить → отсюда и обратная карта. Но и это устранимо без нового индекса: общемодульная ветка регистрации уже вешает sourceSymbol-supplier на synthetic-symbol (ConfigurationModuleMembersProvider.java:159-162), а менеджерная — нет; если зеркалить это для менеджеров, резолв пойдёт по существующему synthetic-symbol → getSourceSymbol(), и uriByModuleType не нужен.

Предложение: общие/OScript — на существующие API; менеджеры — добавить sourceSymbol-supplier при регистрации (как у общих модулей) и убрать uriByModuleType. Возражения/альтернативы?

if (labelLocationResolveSupport) {
// Ссылка строится лениво на inlayHint/resolve — в data кладём координаты
// объявления параметра, остальное (value/position/kind) жадно.
inlayHint.setData(new MethodCallInlayHintData(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В этом нет смысла в данном контексте. Если location УЖЕ рассчитан, его надо просто вернуть, а не отправлять dto на 6 параметров

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Согласен — раз location уже посчитан, слать DTO на резолв смысла нет. Теперь ссылка части метки проставляется жадно прямо при построении хинта (и для вызовов методов, и для ссылки на объявление типа). DTO MethodCallInlayHintData удалён, у хинта вызова метода вообще пропала надобность в data/resolve. VariableTypeInlayHintData ужат до полей ленивого tooltip (uri/id/typeName) — tooltip как тяжёлое поле остаётся за inlayHint/resolve, как в #4100. (372be70)

* объявляющего символа в исходниках (платформенный/примитивный тип) или
* документ-модуль больше не загружен.
*/
public Optional<SourceDefinedSymbol> definingSymbol(TypeRef typeRef, DocumentContext requestingContext) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кажется, здесь полностью опущен сценарий, когда тип задаётся ссылкой См. с указанием имени функции

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Разобрался — это не пропущенная ветка definingSymbol, а отсутствие самого понятия «тип задан ссылкой См. Функция» в модели типов.

Что есть сейчас:

  • См.-ссылка живёт только в модели описаний парсера: HyperlinkTypeDescription / TypeDescription.Variant.HYPERLINK (bsl-parser), несёт строку ссылки.
  • В модель типов (TypeKind / TypeRef) она НЕ попадает. SymbolTypeIndex.resolveTypeDescription (SymbolTypeIndex.java:247-253) для HYPERLINK отдаёт TypeSet.EMPTY, а потребители (SymbolTypeIndex.resolveHyperlink :110, ExpressionTypeInferencer.parameterHyperlinkTypes :1304) разворачивают ссылку в тип возвращаемого значения целевой функции и складывают обычные TypeRef. Идентичность самой функции при этом теряется.
  • TypeRef — это пара (kind, qualifiedName) (TypeRef.java:47), провенанса «откуда взялся тип» в нём нет.

Поэтому в definingSymbol приходит уже обычный TypeRef разрешённого типа-значения (его объявление корректно отдают ветки USER/CONFIGURATION) — отдельной формы TypeRef/TypeKind для «тип = См. Функция» нет, и добавлять для неё ветку некуда. А в VariableTypeInlayHintSupplier цепочка вообще схлопывается до inferredType.qualifiedName() (:148-149, :180-181), так что провенанс теряется ещё и на границе ленивого resolve.

Сделать хинт выведенного типа ссылающимся на саму функцию из См. (а не на объявление разрешённого типа-значения) — это не ветка в definingSymbol, а отдельная работа по выводу типов: нужно протащить провенанс ссылки (функция-источник) через весь конвейер инференса и через границу inlayHint/resolve.

Предлагаю вынести это отдельной задачей по type-inference. Завести issue и не блокировать этот PR? Или видишь более лёгкий путь, который я упустил?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Заведи issue, да

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Завёл: #4118 — с разбором текущей модели (HYPERLINK даёт тип возвращаемого значения, провенанса функции нет) и что нужно (протащить provenance ссылки через инференс + границу resolve). #4111 не блокирует.

…енивого резолва

Объявление параметра/типа уже разрешено на этапе построения хинта, поэтому
ссылка части метки (InlayHintLabelPart.location) проставляется жадно, без
отложенного inlayHint/resolve.

- SourceDefinedMethodCallInlayHintSupplier: убран ленивый путь location и
  гейтинг по inlayHint.resolveSupport(label.location); хинт вызова метода не
  несёт тяжёлых отложенных полей, поэтому переведён на DefaultInlayHintData и
  больше не кладёт data/не переопределяет resolve. Удалён неиспользуемый DTO
  MethodCallInlayHintData.
- VariableTypeInlayHintData: оставлены только поля для ленивого tooltip
  (uri, id, typeName); убраны targetUri, координаты и hasLocation/NO_LOCATION.
- VariableTypeInlayHintSupplier: location проставляется жадно, tooltip
  по-прежнему дорассчитывается лениво через inlayHint/resolve (как в #4100).
- Тесты: убраны проверки ленивого round-trip'а location, оставлены жадные
  проверки location и ленивая проверка tooltip.

Co-Authored-By: Claude Fable 5 <[email protected]>
@nixel2007
nixel2007 merged commit 9dca202 into develop Jun 14, 2026
41 checks passed
@nixel2007
nixel2007 deleted the claude/inlayhint-clickable-labelparts branch June 14, 2026 17:56
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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