feat(hover): hover по имени типа в описании метода (BSLDoc) - #4133
feat(hover): hover по имени типа в описании метода (BSLDoc)#4133nixel2007 wants to merge 2 commits into
Conversation
При наведении на имя типа в секциях `// Параметры:` и `// Возвращаемое значение:` описания метода теперь показывается всплывающая документация типа. Работает одинаково для инстанцируемых (например, ТаблицаЗначений) и неинстанцируемых (например, СтрокаТаблицыЗначений) типов — в отличие от hover'а конструктора, синтаксис `Новый` не показывается. Реализовано по образцу существующих finder'ов: - TypeReferenceSymbol — synthetic-символ с разрешённым TypeRef; - DescriptionTypeReferenceFinder — резолвит TYPE_NAME-элемент описания под курсором в TypeRef (позиции элементов описания — абсолютные координаты); - TypeReferenceMarkupContentBuilder — рендерит имя типа, метку «тип» и описание типа. closes #3990 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds hover support for type names in BSLDoc method descriptions. A new ChangesHover for BSLDoc type names
Sequence DiagramsequenceDiagram
participant Client as LSP Client
participant HoverProvider
participant DescriptionTypeReferenceFinder
participant TypeService
participant TypeReferenceMarkupContentBuilder
Client->>HoverProvider: hover(uri, position)
HoverProvider->>DescriptionTypeReferenceFinder: findReference(uri, position)
DescriptionTypeReferenceFinder->>DescriptionTypeReferenceFinder: scan TYPE_NAME elements, check cursor containment
DescriptionTypeReferenceFinder->>TypeService: resolve(typeName)
TypeService-->>DescriptionTypeReferenceFinder: TypeRef
DescriptionTypeReferenceFinder-->>HoverProvider: Reference(TypeReferenceSymbol)
HoverProvider->>TypeReferenceMarkupContentBuilder: getContent(reference)
TypeReferenceMarkupContentBuilder-->>HoverProvider: MarkupContent(MARKDOWN)
HoverProvider-->>Client: Hover(markupContent, range)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Test Results 3 474 files + 6 3 474 suites +6 1h 47m 13s ⏱️ + 18m 10s Results for commit adea93a. ± Comparison against base commit 3dd3a91. This pull request removes 1 and adds 8 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
| private final TypeService typeService; | ||
|
|
||
| @Override | ||
| public Optional<Reference> findReference(URI uri, Position position) { |
There was a problem hiding this comment.
Посмотри как работает semantic token supplier на описания методов и переменных
There was a problem hiding this comment.
Посмотрел. BslDocSemanticTokensSupplier (и CommentSemanticTokensSupplier) перебирают ровно тот же набор описаний, что и финдер: getMethods() → getDescription(), getVariables() → getDescription() + getTrailingDescription(), и подсвечивают все TYPE_NAME-элементы единообразно, без различения вида типа.
Отсюда вывод, который я зря размыл в описании PR (пункт 3): пользовательские/ссылочные/OneScript-типы в описании — это такие же TYPE_NAME-элементы, и финдер резолвит их тем же typeService.resolve(name, fileType), что и платформенные. Никакой отдельной обработки нет и не нужно.
Добавил доказательство: HoverProviderOScriptLibraryTest.hoverOnUserTypeNameInMethodDescriptionResolves — hover по имени USER-класса (PublicEntity) в // Возвращаемое значение: резолвится (коммит 81-й в ветке). Формулировку пункта 3 в описании PR поправлю.
Если посыл был в другом — например, вынести общий обход описаний (методы + переменные + trailing) в шейред-хелпер, переиспользуемый обоими supplier'ами и финдером, — скажи, сделаю отдельным шагом.
There was a problem hiding this comment.
Посмотрел SymbolTree.getSymbolAtPosition(Position) (единственный метод получения символа по позиции). Проверил эмпирически на примере из issue:
desc.getRange()= строки 0–5 (блок описания над методом);method.getRange()= строки 6–8 (только объявление, без ведущего описания);getSymbolAtPosition(1:20)иgetSymbolAtPosition(4:10)— позиции на именах типов внутри описания — возвращают Module, а не метод;getSymbolAtPosition(6:10)— внутри объявления — возвращает Method.
То есть описание лежит вне getRange() символа (у SourceDefinedSymbol есть только getRange()/getSelectionRange(), оба про объявление), и getSymbolAtPosition для позиции в комментарии-описании отдаёт модуль. Поэтому напрямую заменить обход описаний на «символ по позиции → его описание» нельзя — символ под курсором не находится.
Варианты, как это причесать:
- оставить обход
getMethods()/getVariables(), но гейтить поdescription.getRange().contains(position)и сразу выходить — дёшево, минимальное изменение, поведение то же; - включить ведущее описание в
getRange()метода/переменной — тогдаgetSymbolAtPositionначнёт попадать в описание (но это меняет семантику range символа, затронет другие потребители getRange); - добавить в
SymbolTreeметод поиска символа по позиции его описания (getSymbolByDescriptionPosition) и в финдере брать описание только найденного символа.
Я бы выбрал (1) как локальный, или (3) если хочется переиспользуемого API «символ по позиции описания» (его же могли бы использовать оба semantic token supplier'а). Что предпочитаешь? Я не угадал твой посыл — поправь, если имел в виду другое.
There was a problem hiding this comment.
Согласен — финдер в текущем виде не масштабируется и это блокер.
Проблема. DescriptionTypeReferenceFinder стоит @Order(300) (последний), поэтому ReferenceResolver вызывает его на каждую позицию, которую не разрешил ни один предыдущий финдер — а это не только ховер, но и горячий путь инференсера/диагностик. Каждый вызов делает полный линейный обход getMethods() + getVariables() (а getVariables() — это все переменные плоско: модульные + все локальные во всех методах; на УправлениеДоступом — тысячи) с разбором описаний, плюс трогает getContentList()/getSymbolTree() под computeLock на no-lock-пути. Это O(символов) на вызов.
Что нужно. Перебор описаний — один раз на документ, с индексом по позиции; ховер = поиск по позиции, без обхода дерева.
Развилка (проверил модель ReferenceIndex): getReference(uri, pos) фильтрует SymbolOccurrence по позиции, а buildReference ресолвит лёгкий указатель (mdoRef/moduleType/symbolName/symbolKind) обратно в SourceDefinedSymbol и понимает только Variable/Module/Method. Синтетический ref на платформенный TypeRef в эту модель без расширения не ложится.
Варианты:
- A (локально, рекомендую): добавить в
DocumentContextлениво-вычисляемый и кэшируемый (Lazy, сбрасывается вместе с symbol tree вclearSecondaryData) индекс TYPE_NAME-элементов описаний (отсортированный по позиции). Финдер делает по нему поиск по позиции (бинарный/интервальный) и резолвит имя черезtypeService.resolve. Перебор описаний — один раз на документ; ховер — O(log n). Изменение точечное,ReferenceIndexне трогаем. - B (унифицировано): расширить
SymbolOccurrence/buildReferenceподдержкой type-таргетов и наполнять type-name-ссылки вReferenceIndexFiller.fill(). Концептуально «как все ссылки», но лезет в общую модель (затронетgetReferencesTo/accessibility и пр.).
Как промежуточная дешёвая отсечка в любом случае: если getSymbolAtPosition(pos) вернул не-Module символ, курсор внутри объявления (не в комментарии-описании) — сразу выходим.
Я за A. Дай добро на направление — переделаю и прогоню тесты. Если хочешь B (единый индекс) — тоже сделаю, но это уже про общую модель ссылок.
Подтверждает, что имя USER-типа в описании метода резолвится тем же DescriptionTypeReferenceFinder, что и платформенные типы — никакой отдельной обработки по виду типа нет (как и в semantic token supplier'е, который подсвечивает все TYPE_NAME-элементы единообразно). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|



Что сделано
Реализован hover по имени типа в описании метода (BSLDoc — секции
// Параметры:и// Возвращаемое значение:). При наведении на имя типа показывается всплывающая документация этого типа.closes #3990
Результат проверки текущего поведения
Как и предполагалось в issue — hover по имени типа в описании метода ранее не был реализован вовсе. Проверено эмпирически: ни один из
ReferenceFinderне обрабатывал позиции внутри описаний; элементы описания (DescriptionElementс типомTYPE_NAME) использовались только вBslDocSemanticTokensSupplierдля подсветки. Hover возвращал пустой результат и для инстанцируемых, и для неинстанцируемых типов.Ключевой факт для реализации: позиции
DescriptionElement— абсолютные координаты в файле (0-based), поэтому сопоставление с курсором идёт напрямую.Инстанцируемые vs неинстанцируемые
Догадка из issue про «по-разному распознаются» в части резолва для BSL не подтвердилась на уровне реестра типов: и
ТаблицаЗначений, иСтрокаТаблицыЗначений/КлючИЗначениеприсутствуют вbuiltin-platform-types.jsonи одинаково резолвятся черезTypeService.resolve(...). Поэтому hover работает для обоих видов единообразно (см. тесты).Важно: hover специально не показывает синтаксис
Новый Тип(...)(это hover конструктора,ConstructorHoverBuilder) — для неинстанцируемых типов он был бы бессмысленным. Вместо этого рендерится имя типа + метка «тип» + описание типа.Реализация
По образцу существующих finder'ов (
NewExpressionReferenceFinder→ symbol → builder):TypeReferenceSymbol— synthetic-символ с разрешённымTypeRef;DescriptionTypeReferenceFinder(@Order(300), последний) — перебирает описания методов и переменных (включая trailing-описание переменной) — тем же набором, чтоBslDocSemanticTokensSupplier/CommentSemanticTokensSupplier— находитTYPE_NAME-элемент под курсором и резолвит его вTypeRef. Стоит последним, поэтому на обычном коде не вмешивается;TypeReferenceMarkupContentBuilder— рендерит имя типа, метку «тип»/«type» (ru/en) и описание типа; для коллекций добавляетCollectionHoverHints.Тесты
HoverOnDescriptionTypeTest(структура given/when/then):ТаблицаЗначений) в// Параметры:;СтрокаТаблицыЗначений) в// Возвращаемое значение:;HoverProviderOScriptLibraryTest.hoverOnUserTypeNameInMethodDescriptionResolves— hover по имени пользовательского (OneScript) типа в описании метода резолвится тем же finder'ом.Открытые вопросы / заметки
Новый). Альтернатива — показывать ещё и сигнатуру конструктора. Выбрал «документацию типа», т.к. это соответствует формулировке issue. Готов поменять, если хочется иначе.КолонкаТаблицыЗначений,HTTPОтвет,Поток) сейчас присутствует только вbuiltin-oscript-platform-types.json, поэтому в.bsl-файлах они не резолвятся и hover по ним не покажется. Это отдельный пробел в данных платформенных типов BSL (не в логике hover) — стоит ли заводить отдельную задачу?Специальной обработки нет— резолвятся тем жеtypeService.resolve(name, fileType), что и платформенные: финдер перебирает те жеTYPE_NAME-элементы, что подсвечивает semantic token supplier, без различения вида типа. Подтверждено тестом на OneScript USER-тип (PublicEntity). Никакой отдельной обработки не требуется. КонфигурационныеСправочникСсылка.Xрезолвятся при загруженной конфигурации тем же путём.🤖 Generated with Claude Code
Summary by CodeRabbit