From 08b539411805c6436609e470320745e2c9a4ab5d Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sun, 2 Aug 2026 11:18:33 +0200 Subject: [PATCH 1/5] =?UTF-8?q?feat(description):=20=D1=82=D0=B8=D0=BF=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B6=D0=B5=D1=82=20=D0=B1=D1=8B=D1=82=D1=8C=20?= =?UTF-8?q?=D1=83=D1=82=D0=BE=D1=87=D0=BD=D1=91=D0=BD=20=D1=81=D1=81=D1=8B?= =?UTF-8?q?=D0=BB=D0=BA=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Запись «СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения» ломала разбор: после типа с двоеточием грамматика ссылку не допускала, правило параметра не срабатывало целиком, и строка разбиралась по запасным альтернативам — имя параметра подменялось ссылкой, а сам параметр терялся. За простым типом разрешена ссылка, и она доезжает до описания отдельным свойством: голова говорит, чем значение является, ссылка — откуда взять его состав. Потребитель решает сам, как их сложить. Closes #405 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y --- src/main/antlr/BSLDescriptionParser.g4 | 2 +- .../description/SimpleTypeDescription.java | 28 +++++- .../parser/description/TypeDescription.java | 13 +++ .../reader/MethodDescriptionReader.java | 23 ++++- .../reader/TypeWithReferenceTest.java | 88 +++++++++++++++++++ 5 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java diff --git a/src/main/antlr/BSLDescriptionParser.g4 b/src/main/antlr/BSLDescriptionParser.g4 index 17d6d318..c3d1ccba 100644 --- a/src/main/antlr/BSLDescriptionParser.g4 +++ b/src/main/antlr/BSLDescriptionParser.g4 @@ -145,7 +145,7 @@ typesBlock: splitter type ; type: listTypes | collectionType | hyperlinkType | simpleType; -simpleType: typeName=(WORD | DOTSWORD) colon=COLON?; +simpleType: typeName=(WORD | DOTSWORD) colon=COLON? (SPACE? reference=hyperlink)?; collectionType: collection=(WORD | DOTSWORD) SPACE OF_KEYWORD SPACE value=type; hyperlinkType: hyperlink; listTypes: listType (COMMA SPACE? listType?)+; diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java index ec24fd4e..86a75439 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java @@ -22,7 +22,9 @@ package com.github._1c_syntax.bsl.parser.description; import com.github._1c_syntax.bsl.parser.description.support.DescriptionElement; +import com.github._1c_syntax.bsl.parser.description.support.Hyperlink; import com.github._1c_syntax.bsl.parser.description.support.SimpleRange; +import edu.umd.cs.findbugs.annotations.Nullable; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Value; @@ -30,6 +32,7 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; /** * Описание простого типа @@ -41,7 +44,8 @@ public class SimpleTypeDescription implements TypeDescription { "", "", Collections.emptyList(), - new DescriptionElement(SimpleRange.EMPTY, DescriptionElement.Type.UNKNOWN) + new DescriptionElement(SimpleRange.EMPTY, DescriptionElement.Type.UNKNOWN), + null ); @Accessors(fluent = true) @@ -56,10 +60,24 @@ public class SimpleTypeDescription implements TypeDescription { @Accessors(fluent = true) DescriptionElement element; + /** + * Ссылка, уточняющая тип; {@code null}, если тип ссылкой не уточнён + */ + @Nullable + Hyperlink typeReference; + public static TypeDescription create(String name, DescriptionElement element, String description, List fieldList) { + return create(name, element, description, fieldList, null); + } + + public static TypeDescription create(String name, + DescriptionElement element, + String description, + List fieldList, + @Nullable Hyperlink typeReference) { if (name.isBlank() && description.isBlank()) { return EMPTY; } @@ -67,7 +85,8 @@ public static TypeDescription create(String name, name.strip().intern(), description.strip(), fieldList, - element + element, + typeReference ); } @@ -75,4 +94,9 @@ public static TypeDescription create(String name, public Variant variant() { return Variant.SIMPLE; } + + @Override + public Optional reference() { + return Optional.ofNullable(typeReference); + } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java index c20a1dff..c1efed9b 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java @@ -23,9 +23,12 @@ import com.github._1c_syntax.bsl.parser.description.support.DescriptionElement; +import com.github._1c_syntax.bsl.parser.description.support.Hyperlink; + import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; /** * Описание типа параметра, прочитанного из описания метода @@ -66,6 +69,16 @@ public interface TypeDescription { */ DescriptionElement element(); + /** + * Ссылка, уточняющая тип: запись вида {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}, + * где имя типа говорит, чем значение является, а ссылка — откуда взять его состав. + * + * @return Ссылка; пусто, если тип ссылкой не уточнён + */ + default Optional reference() { + return Optional.empty(); + } + /** * Список элементов описания включая все дочерние описания (поля, типы...) * diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java index 7ce52892..87a23102 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java @@ -465,6 +465,9 @@ private void addType(BSLDescriptionParser.SimpleTypeContext typeContext, BSLDescriptionParser.@Nullable TypeDescriptionContext description) { if (typeContext.typeName != null) { var lastType = new TempParameterTypeData(typeContext.typeName, TypeDescription.Variant.SIMPLE, level); + if (typeContext.reference != null) { + lastType.setReference(typeContext.reference); + } if (description != null) { lastType.addTypeDescription(description); } @@ -525,6 +528,8 @@ private static final class TempParameterTypeData { private final TypeDescription.Variant variant; private final List valueTypes; private @Nullable Token linkParamsToken; + private @Nullable Token referenceToken; + private @Nullable Token referenceParamsToken; private final SimpleRange range; @@ -552,6 +557,18 @@ private TempParameterTypeData(Token typeName, TypeDescription.Variant variant, i this.name = typeName.getText(); } + /** + * Запомнить ссылку, уточняющую тип: {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}. + * + * @param reference Узел ссылки + */ + private void setReference(BSLDescriptionParser.HyperlinkContext reference) { + if (reference.link != null) { + this.referenceToken = reference.link; + this.referenceParamsToken = reference.linkParams; + } + } + private void addTypeDescription(BSLDescriptionParser.TypeDescriptionContext typeDescription) { var lastField = lastField(); if (lastField.isPresent()) { @@ -620,7 +637,11 @@ private TypeDescription build(int lineShift, int[] charShifts) { var element = new DescriptionElement(newRange, DescriptionElement.Type.TYPE_NAME); return switch (variant) { - case SIMPLE -> SimpleTypeDescription.create(name, element, description.toString(), fieldList); + case SIMPLE -> SimpleTypeDescription.create(name, element, description.toString(), fieldList, + referenceToken == null ? null : Hyperlink.create( + referenceToken.getText(), + referenceParamsToken == null ? "" : referenceParamsToken.getText(), + SimpleRange.create(referenceToken, lineShift, charShifts))); case COLLECTION -> CollectionTypeDescription.create( name, element, description.toString(), valueTypes.stream() diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java new file mode 100644 index 00000000..490ce8e0 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java @@ -0,0 +1,88 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright (c) 2018-2026 + * Alexey Sosnoviy , Nikita Fedkin , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser.description.reader; + +import com.github._1c_syntax.bsl.parser.BSLParser; +import com.github._1c_syntax.bsl.parser.BSLTokenizer; +import com.github._1c_syntax.bsl.parser.description.MethodDescription; +import com.github._1c_syntax.bsl.parser.description.TypeDescription; +import org.antlr.v4.runtime.Token; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Тип, уточнённый ссылкой: {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}. + * Голова говорит, чем значение является, ссылка — откуда взять его состав. + */ +class TypeWithReferenceTest { + + private List getTokens(String example) { + var tokenizer = new BSLTokenizer(example); + return tokenizer.getTokens().stream() + .filter(token -> token.getType() == BSLParser.LINE_COMMENT) + .collect(Collectors.toList()); + } + + @Test + void typeRefinedByReferenceKeepsBothParts() { + // given + var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения\n"; + + // when + var description = MethodDescription.create(getTokens(src)); + + // then + assertThat(description.getParameters()) + .singleElement() + .satisfies(parameter -> { + assertThat(parameter.name()).isEqualTo("Объект"); + assertThat(parameter.types()).singleElement().satisfies(type -> { + assertThat(type.variant()).isEqualTo(TypeDescription.Variant.SIMPLE); + assertThat(type.name()).isEqualTo("СтрокаТабличнойЧасти"); + assertThat(type.reference()) + .isPresent() + .hasValueSatisfying(reference -> + assertThat(reference.link()).isEqualTo("Справочник.Товары.ЕдиницыИзмерения")); + }); + }); + } + + @Test + void plainTypeHasNoReference() { + // given + var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти\n"; + + // when + var description = MethodDescription.create(getTokens(src)); + + // then + assertThat(description.getParameters()) + .singleElement() + .satisfies(parameter -> assertThat(parameter.types()) + .singleElement() + .satisfies(type -> assertThat(type.reference()).isEmpty())); + } +} From e751f8c2c4fcec408a76664888bb59454e63f53b Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sun, 2 Aug 2026 12:03:47 +0200 Subject: [PATCH 2/5] =?UTF-8?q?fix(description):=20=D1=83=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D1=8F=D1=8E=D1=89=D0=B0=D1=8F=20=D1=81=D1=81=D1=8B?= =?UTF-8?q?=D0=BB=D0=BA=D0=B0=20=D1=82=D1=80=D0=B5=D0=B1=D1=83=D0=B5=D1=82?= =?UTF-8?q?=20=D0=B4=D0=B2=D0=BE=D0=B5=D1=82=D0=BE=D1=87=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Без разделителя запись «Тип См. Ссылка» рекомендацией не предусмотрена, а грамматика принимала её наравне с «Тип: См. Ссылка». Заодно аннотация обнуляемости приведена к той, что используется в проекте. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y --- src/main/antlr/BSLDescriptionParser.g4 | 2 +- .../parser/description/SimpleTypeDescription.java | 2 +- .../description/reader/TypeWithReferenceTest.java | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/antlr/BSLDescriptionParser.g4 b/src/main/antlr/BSLDescriptionParser.g4 index c3d1ccba..55e1a35a 100644 --- a/src/main/antlr/BSLDescriptionParser.g4 +++ b/src/main/antlr/BSLDescriptionParser.g4 @@ -145,7 +145,7 @@ typesBlock: splitter type ; type: listTypes | collectionType | hyperlinkType | simpleType; -simpleType: typeName=(WORD | DOTSWORD) colon=COLON? (SPACE? reference=hyperlink)?; +simpleType: typeName=(WORD | DOTSWORD) (colon=COLON (SPACE? reference=hyperlink)?)?; collectionType: collection=(WORD | DOTSWORD) SPACE OF_KEYWORD SPACE value=type; hyperlinkType: hyperlink; listTypes: listType (COMMA SPACE? listType?)+; diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java index 86a75439..e73070db 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java @@ -24,11 +24,11 @@ import com.github._1c_syntax.bsl.parser.description.support.DescriptionElement; import com.github._1c_syntax.bsl.parser.description.support.Hyperlink; import com.github._1c_syntax.bsl.parser.description.support.SimpleRange; -import edu.umd.cs.findbugs.annotations.Nullable; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Value; import lombok.experimental.Accessors; +import org.jspecify.annotations.Nullable; import java.util.Collections; import java.util.List; diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java index 490ce8e0..fb60ba86 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java @@ -70,6 +70,20 @@ void typeRefinedByReferenceKeepsBothParts() { }); } + @Test + void referenceWithoutColonIsNotATypeReference() { + // given: разделителем в этой записи служит двоеточие, без него это не уточнение типа. + var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти См. Справочник.Товары.ЕдиницыИзмерения\n"; + + // when + var description = MethodDescription.create(getTokens(src)); + + // then + assertThat(description.getParameters()) + .allSatisfy(parameter -> assertThat(parameter.types()) + .allSatisfy(type -> assertThat(type.reference()).isEmpty())); + } + @Test void plainTypeHasNoReference() { // given From eb4863a261b01c2a451d2bf26a6da86a642c420c Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sun, 2 Aug 2026 12:15:23 +0200 Subject: [PATCH 3/5] =?UTF-8?q?refactor(description):=20=D1=81=D1=81=D1=8B?= =?UTF-8?q?=D0=BB=D0=BA=D0=B0=20=D1=82=D0=B8=D0=BF=D0=B0=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B7=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20hyperlink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Отдельного имени для уточняющей ссылки заводить незачем: это та же ссылка, что и у гиперссылочного типа, поэтому у описания типа один аксессор на оба случая. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y --- src/main/antlr/BSLDescriptionParser.g4 | 2 +- .../description/SimpleTypeDescription.java | 14 ++++------ .../parser/description/TypeDescription.java | 14 +++++----- .../reader/MethodDescriptionReader.java | 26 +++++++++---------- ...ceTest.java => TypeWithHyperlinkTest.java} | 21 +++++++-------- 5 files changed, 37 insertions(+), 40 deletions(-) rename src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/{TypeWithReferenceTest.java => TypeWithHyperlinkTest.java} (83%) diff --git a/src/main/antlr/BSLDescriptionParser.g4 b/src/main/antlr/BSLDescriptionParser.g4 index 55e1a35a..fa9bf862 100644 --- a/src/main/antlr/BSLDescriptionParser.g4 +++ b/src/main/antlr/BSLDescriptionParser.g4 @@ -145,7 +145,7 @@ typesBlock: splitter type ; type: listTypes | collectionType | hyperlinkType | simpleType; -simpleType: typeName=(WORD | DOTSWORD) (colon=COLON (SPACE? reference=hyperlink)?)?; +simpleType: typeName=(WORD | DOTSWORD) (colon=COLON (SPACE? hyperlink)?)?; collectionType: collection=(WORD | DOTSWORD) SPACE OF_KEYWORD SPACE value=type; hyperlinkType: hyperlink; listTypes: listType (COMMA SPACE? listType?)+; diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java index e73070db..768271e9 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/SimpleTypeDescription.java @@ -32,7 +32,7 @@ import java.util.Collections; import java.util.List; -import java.util.Optional; + /** * Описание простого типа @@ -64,7 +64,8 @@ public class SimpleTypeDescription implements TypeDescription { * Ссылка, уточняющая тип; {@code null}, если тип ссылкой не уточнён */ @Nullable - Hyperlink typeReference; + @Accessors(fluent = true) + Hyperlink hyperlink; public static TypeDescription create(String name, DescriptionElement element, @@ -77,7 +78,7 @@ public static TypeDescription create(String name, DescriptionElement element, String description, List fieldList, - @Nullable Hyperlink typeReference) { + @Nullable Hyperlink hyperlink) { if (name.isBlank() && description.isBlank()) { return EMPTY; } @@ -86,7 +87,7 @@ public static TypeDescription create(String name, description.strip(), fieldList, element, - typeReference + hyperlink ); } @@ -94,9 +95,4 @@ public static TypeDescription create(String name, public Variant variant() { return Variant.SIMPLE; } - - @Override - public Optional reference() { - return Optional.ofNullable(typeReference); - } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java index c1efed9b..959ce9f7 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/TypeDescription.java @@ -24,11 +24,11 @@ import com.github._1c_syntax.bsl.parser.description.support.DescriptionElement; import com.github._1c_syntax.bsl.parser.description.support.Hyperlink; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; /** * Описание типа параметра, прочитанного из описания метода @@ -70,13 +70,15 @@ public interface TypeDescription { DescriptionElement element(); /** - * Ссылка, уточняющая тип: запись вида {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}, - * где имя типа говорит, чем значение является, а ссылка — откуда взять его состав. + * Ссылка типа: у гиперссылочного типа это он сам, у простого — уточнение записью вида + * {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}, где имя типа + * говорит, чем значение является, а ссылка — откуда взять его состав. * - * @return Ссылка; пусто, если тип ссылкой не уточнён + * @return Ссылка; {@code null}, если у типа ссылки нет */ - default Optional reference() { - return Optional.empty(); + @Nullable + default Hyperlink hyperlink() { + return null; } /** diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java index 87a23102..c0a22724 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java @@ -465,8 +465,8 @@ private void addType(BSLDescriptionParser.SimpleTypeContext typeContext, BSLDescriptionParser.@Nullable TypeDescriptionContext description) { if (typeContext.typeName != null) { var lastType = new TempParameterTypeData(typeContext.typeName, TypeDescription.Variant.SIMPLE, level); - if (typeContext.reference != null) { - lastType.setReference(typeContext.reference); + if (typeContext.hyperlink() != null) { + lastType.setHyperlink(typeContext.hyperlink()); } if (description != null) { lastType.addTypeDescription(description); @@ -528,8 +528,8 @@ private static final class TempParameterTypeData { private final TypeDescription.Variant variant; private final List valueTypes; private @Nullable Token linkParamsToken; - private @Nullable Token referenceToken; - private @Nullable Token referenceParamsToken; + private @Nullable Token hyperlinkToken; + private @Nullable Token hyperlinkParamsToken; private final SimpleRange range; @@ -560,12 +560,12 @@ private TempParameterTypeData(Token typeName, TypeDescription.Variant variant, i /** * Запомнить ссылку, уточняющую тип: {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}. * - * @param reference Узел ссылки + * @param hyperlink Узел ссылки */ - private void setReference(BSLDescriptionParser.HyperlinkContext reference) { - if (reference.link != null) { - this.referenceToken = reference.link; - this.referenceParamsToken = reference.linkParams; + private void setHyperlink(BSLDescriptionParser.HyperlinkContext hyperlink) { + if (hyperlink.link != null) { + this.hyperlinkToken = hyperlink.link; + this.hyperlinkParamsToken = hyperlink.linkParams; } } @@ -638,10 +638,10 @@ private TypeDescription build(int lineShift, int[] charShifts) { return switch (variant) { case SIMPLE -> SimpleTypeDescription.create(name, element, description.toString(), fieldList, - referenceToken == null ? null : Hyperlink.create( - referenceToken.getText(), - referenceParamsToken == null ? "" : referenceParamsToken.getText(), - SimpleRange.create(referenceToken, lineShift, charShifts))); + hyperlinkToken == null ? null : Hyperlink.create( + hyperlinkToken.getText(), + hyperlinkParamsToken == null ? "" : hyperlinkParamsToken.getText(), + SimpleRange.create(hyperlinkToken, lineShift, charShifts))); case COLLECTION -> CollectionTypeDescription.create( name, element, description.toString(), valueTypes.stream() diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java similarity index 83% rename from src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java rename to src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java index fb60ba86..9e89e2f3 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithReferenceTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java @@ -37,7 +37,7 @@ * Тип, уточнённый ссылкой: {@code СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения}. * Голова говорит, чем значение является, ссылка — откуда взять его состав. */ -class TypeWithReferenceTest { +class TypeWithHyperlinkTest { private List getTokens(String example) { var tokenizer = new BSLTokenizer(example); @@ -47,7 +47,7 @@ private List getTokens(String example) { } @Test - void typeRefinedByReferenceKeepsBothParts() { + void typeRefinedByHyperlinkKeepsBothParts() { // given var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения\n"; @@ -62,30 +62,29 @@ void typeRefinedByReferenceKeepsBothParts() { assertThat(parameter.types()).singleElement().satisfies(type -> { assertThat(type.variant()).isEqualTo(TypeDescription.Variant.SIMPLE); assertThat(type.name()).isEqualTo("СтрокаТабличнойЧасти"); - assertThat(type.reference()) - .isPresent() - .hasValueSatisfying(reference -> - assertThat(reference.link()).isEqualTo("Справочник.Товары.ЕдиницыИзмерения")); + assertThat(type.hyperlink()).isNotNull(); + assertThat(type.hyperlink().link()).isEqualTo("Справочник.Товары.ЕдиницыИзмерения"); }); }); } @Test - void referenceWithoutColonIsNotATypeReference() { + void hyperlinkWithoutColonDoesNotRefineType() { // given: разделителем в этой записи служит двоеточие, без него это не уточнение типа. var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти См. Справочник.Товары.ЕдиницыИзмерения\n"; // when var description = MethodDescription.create(getTokens(src)); - // then + // then: простого типа, уточнённого ссылкой, здесь нет — ссылка сама по себе. assertThat(description.getParameters()) .allSatisfy(parameter -> assertThat(parameter.types()) - .allSatisfy(type -> assertThat(type.reference()).isEmpty())); + .filteredOn(type -> type.variant() == TypeDescription.Variant.SIMPLE) + .allSatisfy(type -> assertThat(type.hyperlink()).isNull())); } @Test - void plainTypeHasNoReference() { + void plainTypeHasNoHyperlink() { // given var src = "// Параметры:\n// Объект - СтрокаТабличнойЧасти\n"; @@ -97,6 +96,6 @@ void plainTypeHasNoReference() { .singleElement() .satisfies(parameter -> assertThat(parameter.types()) .singleElement() - .satisfies(type -> assertThat(type.reference()).isEmpty())); + .satisfies(type -> assertThat(type.hyperlink()).isNull())); } } From f63e48d7daf9720a3ae0c412b0a143b8d158b612 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sun, 2 Aug 2026 12:27:02 +0200 Subject: [PATCH 4/5] =?UTF-8?q?style(description):=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=B0=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=20=D0=BF=D0=BE=20=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D1=8F=D1=8E=D1=89=D0=B5=D0=B9=20=D1=81=D1=81?= =?UTF-8?q?=D1=8B=D0=BB=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Параметр помечен обнуляемым, сборка ссылки вынесена из вложенного тернарника, в тесте нет ни устаревшего сборщика списка, ни утверждения, истинного на пустом списке. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y --- .../reader/MethodDescriptionReader.java | 30 +++++++++++++------ .../reader/TypeWithHyperlinkTest.java | 16 +++++----- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java index c0a22724..fb919df3 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/description/reader/MethodDescriptionReader.java @@ -465,9 +465,7 @@ private void addType(BSLDescriptionParser.SimpleTypeContext typeContext, BSLDescriptionParser.@Nullable TypeDescriptionContext description) { if (typeContext.typeName != null) { var lastType = new TempParameterTypeData(typeContext.typeName, TypeDescription.Variant.SIMPLE, level); - if (typeContext.hyperlink() != null) { - lastType.setHyperlink(typeContext.hyperlink()); - } + lastType.setHyperlink(typeContext.hyperlink()); if (description != null) { lastType.addTypeDescription(description); } @@ -562,8 +560,25 @@ private TempParameterTypeData(Token typeName, TypeDescription.Variant variant, i * * @param hyperlink Узел ссылки */ - private void setHyperlink(BSLDescriptionParser.HyperlinkContext hyperlink) { - if (hyperlink.link != null) { + /** + * Собрать ссылку, уточняющую тип. + * + * @param lineShift Сдвиг номера строки + * @param charShifts Сдвиг символов по строкам + * + * @return Ссылка; {@code null}, если тип ссылкой не уточнён + */ + private @Nullable Hyperlink buildHyperlink(int lineShift, int[] charShifts) { + if (hyperlinkToken == null) { + return null; + } + var params = hyperlinkParamsToken == null ? "" : hyperlinkParamsToken.getText(); + return Hyperlink.create(hyperlinkToken.getText(), params, + SimpleRange.create(hyperlinkToken, lineShift, charShifts)); + } + + private void setHyperlink(BSLDescriptionParser.@Nullable HyperlinkContext hyperlink) { + if (hyperlink != null && hyperlink.link != null) { this.hyperlinkToken = hyperlink.link; this.hyperlinkParamsToken = hyperlink.linkParams; } @@ -638,10 +653,7 @@ private TypeDescription build(int lineShift, int[] charShifts) { return switch (variant) { case SIMPLE -> SimpleTypeDescription.create(name, element, description.toString(), fieldList, - hyperlinkToken == null ? null : Hyperlink.create( - hyperlinkToken.getText(), - hyperlinkParamsToken == null ? "" : hyperlinkParamsToken.getText(), - SimpleRange.create(hyperlinkToken, lineShift, charShifts))); + buildHyperlink(lineShift, charShifts)); case COLLECTION -> CollectionTypeDescription.create( name, element, description.toString(), valueTypes.stream() diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java index 9e89e2f3..ae9229d4 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java @@ -29,7 +29,6 @@ import org.junit.jupiter.api.Test; import java.util.List; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -43,7 +42,7 @@ private List getTokens(String example) { var tokenizer = new BSLTokenizer(example); return tokenizer.getTokens().stream() .filter(token -> token.getType() == BSLParser.LINE_COMMENT) - .collect(Collectors.toList()); + .toList(); } @Test @@ -76,11 +75,14 @@ void hyperlinkWithoutColonDoesNotRefineType() { // when var description = MethodDescription.create(getTokens(src)); - // then: простого типа, уточнённого ссылкой, здесь нет — ссылка сама по себе. - assertThat(description.getParameters()) - .allSatisfy(parameter -> assertThat(parameter.types()) - .filteredOn(type -> type.variant() == TypeDescription.Variant.SIMPLE) - .allSatisfy(type -> assertThat(type.hyperlink()).isNull())); + // then: ссылка осталась сама по себе — уточнённого ею простого типа не появилось. + var types = description.getParameters().stream() + .flatMap(parameter -> parameter.types().stream()) + .toList(); + assertThat(types).isNotEmpty(); + assertThat(types) + .filteredOn(type -> type.hyperlink() != null) + .allSatisfy(type -> assertThat(type.variant()).isEqualTo(TypeDescription.Variant.HYPERLINK)); } @Test From 10e2f333cb1e6078ceea08ab86a3b7dab028a880 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sun, 2 Aug 2026 12:33:05 +0200 Subject: [PATCH 5/5] =?UTF-8?q?test(description):=20=D0=B1=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=B4=D0=B2=D0=BE=D0=B5=D1=82=D0=BE=D1=87=D0=B8=D1=8F=20=D1=81?= =?UTF-8?q?=D1=81=D1=8B=D0=BB=D0=BA=D0=B0=20=D0=BE=D1=81=D1=82=D0=B0=D1=91?= =?UTF-8?q?=D1=82=D1=81=D1=8F=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=BC=20=D1=82=D0=B8=D0=BF=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Прежняя проверка проходила и на пустом наборе — то есть и в случае, если бы ссылка потерялась вовсе. Теперь ожидается ровно один гиперссылочный тип со своей ссылкой, а простые типы остаются без неё. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y --- .../description/reader/TypeWithHyperlinkTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java index ae9229d4..19083f8b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/description/reader/TypeWithHyperlinkTest.java @@ -79,10 +79,14 @@ void hyperlinkWithoutColonDoesNotRefineType() { var types = description.getParameters().stream() .flatMap(parameter -> parameter.types().stream()) .toList(); - assertThat(types).isNotEmpty(); assertThat(types) - .filteredOn(type -> type.hyperlink() != null) - .allSatisfy(type -> assertThat(type.variant()).isEqualTo(TypeDescription.Variant.HYPERLINK)); + .filteredOn(type -> type.variant() == TypeDescription.Variant.HYPERLINK) + .singleElement() + .satisfies(type -> + assertThat(type.hyperlink().link()).isEqualTo("Справочник.Товары.ЕдиницыИзмерения")); + assertThat(types) + .filteredOn(type -> type.variant() == TypeDescription.Variant.SIMPLE) + .allSatisfy(type -> assertThat(type.hyperlink()).isNull()); } @Test