diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java index 4ddf257847b..48d4c5d1434 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/codelenses/RunAllTestsCodeLensSupplier.java @@ -80,8 +80,12 @@ public List getCodeLenses(DocumentContext documentContext) { return Collections.emptyList(); } - var symbolTree = documentContext.getSymbolTree(); - var firstMethod = symbolTree.getMethods().get(0); + var methods = documentContext.getSymbolTree().getMethods(); + if (methods.isEmpty()) { + return Collections.emptyList(); + } + + var firstMethod = methods.get(0); return List.of(toCodeLens(firstMethod, documentContext)); } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentContext.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentContext.java index abb0373d639..26a112d8448 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentContext.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentContext.java @@ -48,6 +48,7 @@ import jakarta.annotation.PostConstruct; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.Locked; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -111,9 +112,9 @@ public class DocumentContext implements Comparable { @Getter private FileType fileType; - @Getter + @Getter(onMethod = @__({@Locked("computeLock")})) private BSLTokenizer tokenizer; - @Getter + @Getter(onMethod = @__({@Locked("computeLock")})) private SymbolTree symbolTree; @Getter @@ -144,20 +145,24 @@ public ServerContext getServerContext() { return context; } + @Locked("computeLock") public String getContent() { requireNonNull(content); return content; } + @Locked("computeLock") public String[] getContentList() { return contentList.getOrCompute(); } + @Locked("computeLock") public BSLParser.FileContext getAst() { requireNonNull(content); return tokenizer.getAst(); } + @Locked("computeLock") public List getTokens() { requireNonNull(content); return tokenizer.getTokens(); @@ -173,6 +178,7 @@ public List getComments() { .toList(); } + @Locked("computeLock") public String getText(Range range) { Position start = range.getStart(); Position end = range.getEnd(); diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/VariableSymbolComputer.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/VariableSymbolComputer.java index 81a9103b0d0..96c84b6640f 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/VariableSymbolComputer.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/VariableSymbolComputer.java @@ -184,6 +184,10 @@ public ParseTree visitForStatement(BSLParser.ForStatementContext ctx) { @Override public ParseTree visitForEachStatement(BSLParser.ForEachStatementContext ctx) { + if (ctx.IDENTIFIER() == null) { + return super.visitForEachStatement(ctx); + } + if ( currentMethodVariables.containsKey(ctx.IDENTIFIER().getText()) || moduleVariables.containsKey(ctx.IDENTIFIER().getText()) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationParamSymbol.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationParamSymbol.java new file mode 100644 index 00000000000..d7e84011ac9 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationParamSymbol.java @@ -0,0 +1,86 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server 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 Language Server 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 Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.context.symbol; + +import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; +import com.github._1c_syntax.bsl.languageserver.context.symbol.description.MethodDescription; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Setter; +import lombok.ToString; +import lombok.Value; +import lombok.experimental.NonFinal; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolKind; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@Value +@Builder +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@ToString(exclude = {"parent"}) +public class AnnotationParamSymbol implements SourceDefinedSymbol, Describable { + + String name; + + @EqualsAndHashCode.Include + DocumentContext owner; + + Range range; + + @EqualsAndHashCode.Include + Range selectionRange; + + @Setter + @NonFinal + @Builder.Default + Optional parent = Optional.empty(); + + Optional description; + + @Override + public List getChildren() { + return Collections.emptyList(); + } + + public SymbolKind getSymbolKind() { + return SymbolKind.TypeParameter; + } + + @Override + public void accept(SymbolTreeVisitor visitor) { + // no-op + } + + public static AnnotationParamSymbol from(String name, MethodSymbol methodSymbol) { + return AnnotationParamSymbol.builder() + .name(name) + .owner(methodSymbol.getOwner()) + .range(methodSymbol.getRange()) + .selectionRange(methodSymbol.getSelectionRange()) + .description(methodSymbol.getDescription()) + .parent(Optional.of(methodSymbol)) + .build(); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationSymbol.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationSymbol.java index 857f1f05d9b..a1a002e818c 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationSymbol.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/AnnotationSymbol.java @@ -44,8 +44,6 @@ public class AnnotationSymbol implements SourceDefinedSymbol, Describable { String name; - SymbolKind symbolKind; - @EqualsAndHashCode.Include DocumentContext owner; @@ -66,6 +64,10 @@ public List getChildren() { return Collections.emptyList(); } + public SymbolKind getSymbolKind() { + return SymbolKind.Interface; + } + @Override public void accept(SymbolTreeVisitor visitor) { // no-op @@ -74,7 +76,6 @@ public void accept(SymbolTreeVisitor visitor) { public static AnnotationSymbol from(String name, MethodSymbol methodSymbol) { return AnnotationSymbol.builder() .name(name) - .symbolKind(SymbolKind.TypeParameter) .owner(methodSymbol.getOwner()) .range(methodSymbol.getRange()) .selectionRange(methodSymbol.getSelectionRange()) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java index 384cf2a2ce3..17c8ca7b277 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java @@ -58,7 +58,7 @@ public class MissingCommonModuleMethodDiagnostic extends AbstractDiagnostic { private final LocationRepository locationRepository; private static String getMethodNameByLocation(BSLParserRuleContext node, Range range) { - return Trees.findTerminalNodeContainsPosition(node, range.getEnd()) + return Trees.findTerminalNodeContainsPosition(node, range.getStart()) .map(ParseTree::getText) .orElseThrow(); } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationParamSymbolMarkupContentBuilder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationParamSymbolMarkupContentBuilder.java new file mode 100644 index 00000000000..793dcf8e2f3 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationParamSymbolMarkupContentBuilder.java @@ -0,0 +1,79 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2025 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server 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 Language Server 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 Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.hover; + +import com.github._1c_syntax.bsl.languageserver.context.symbol.AnnotationParamSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.AnnotationSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.ParameterDefinition; +import lombok.RequiredArgsConstructor; +import org.eclipse.lsp4j.MarkupContent; +import org.eclipse.lsp4j.MarkupKind; +import org.eclipse.lsp4j.SymbolKind; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.StringJoiner; + +/** + * Построитель контента для всплывающего окна для {@link AnnotationSymbol}. + */ +@Component +@RequiredArgsConstructor +public class AnnotationParamSymbolMarkupContentBuilder implements MarkupContentBuilder { + + private final DescriptionFormatter descriptionFormatter; + + @Override + public MarkupContent getContent(AnnotationParamSymbol symbol) { + var maybeMethodSymbol = symbol.getParent(); + if (maybeMethodSymbol.filter(MethodSymbol.class::isInstance).isEmpty()) { + return new MarkupContent(MarkupKind.MARKDOWN, ""); + } + + var markupBuilder = new StringJoiner("\n"); + var methodSymbol = (MethodSymbol) maybeMethodSymbol.get(); + var maybeParameterDefinition = methodSymbol.getParameters().stream() + .filter(parameter -> parameter.getName().equalsIgnoreCase(symbol.getName())) + .findFirst(); + + if (maybeParameterDefinition.isEmpty()) { + return new MarkupContent(MarkupKind.MARKDOWN, ""); + } + + var parameterDefinition = maybeParameterDefinition.get(); + + // описание параметра аннотации + String parameter = descriptionFormatter.parameterToString(parameterDefinition); + descriptionFormatter.addSectionIfNotEmpty(markupBuilder, parameter); + + String content = markupBuilder.toString(); + + return new MarkupContent(MarkupKind.MARKDOWN, content); + } + + @Override + public SymbolKind getSymbolKind() { + return SymbolKind.TypeParameter; + } + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationSymbolMarkupContentBuilder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationSymbolMarkupContentBuilder.java index 12ce665772c..757b29dc119 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationSymbolMarkupContentBuilder.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/AnnotationSymbolMarkupContentBuilder.java @@ -88,7 +88,7 @@ public MarkupContent getContent(AnnotationSymbol symbol) { @Override public SymbolKind getSymbolKind() { - return SymbolKind.TypeParameter; + return SymbolKind.Interface; } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinder.java index f838fbadc22..982eabe14be 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinder.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinder.java @@ -26,6 +26,7 @@ import com.github._1c_syntax.bsl.languageserver.context.ServerContext; import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent; import com.github._1c_syntax.bsl.languageserver.context.events.ServerContextPopulatedEvent; +import com.github._1c_syntax.bsl.languageserver.context.symbol.AnnotationParamSymbol; import com.github._1c_syntax.bsl.languageserver.context.symbol.AnnotationSymbol; import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; import com.github._1c_syntax.bsl.languageserver.context.symbol.annotations.Annotation; @@ -34,8 +35,8 @@ import com.github._1c_syntax.bsl.languageserver.utils.Ranges; import com.github._1c_syntax.bsl.languageserver.utils.Trees; import com.github._1c_syntax.bsl.parser.BSLParser; +import com.github._1c_syntax.bsl.parser.BSLParserRuleContext; import lombok.RequiredArgsConstructor; -import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; @@ -93,25 +94,119 @@ private void findAndRegisterAnnotation(DocumentContext documentContext) { @Override public Optional findReference(URI uri, Position position) { - DocumentContext document = serverContext.getDocument(uri); - if (document == null || document.getFileType() != FileType.OS) { + DocumentContext documentContext = serverContext.getDocument(uri); + if (documentContext == null || documentContext.getFileType() != FileType.OS) { return Optional.empty(); } - return Trees.findTerminalNodeContainsPosition(document.getAst(), position) - .filter(node -> node.getParent().getRuleContext().getRuleIndex() == BSLParser.RULE_annotationName) - .flatMap((TerminalNode annotationNode) -> { - var annotationName = annotationNode.getText(); - var annotationSymbol = registeredAnnotations.get(annotationName); - if (annotationSymbol == null) { - return Optional.empty(); - } - return Optional.of(Reference.of( - document.getSymbolTree().getModule(), - annotationSymbol, - new Location(uri.toString(), Ranges.create(annotationNode.getParent().getParent())) - )); - }); + var maybeTerminalNode = Trees.findTerminalNodeContainsPosition(documentContext.getAst(), position); + if (maybeTerminalNode.isEmpty()) { + return Optional.empty(); + } + + var terminalNode = maybeTerminalNode.get(); + var parent = terminalNode.getParent(); + if (!(parent instanceof BSLParserRuleContext parentContext)) { + return Optional.empty(); + } + + return Optional.of(parentContext) + .filter(BSLParser.AnnotationNameContext.class::isInstance) + .map(BSLParser.AnnotationNameContext.class::cast) + .flatMap(annotationName -> getReferenceToAnnotationSymbol(uri, annotationName, documentContext)) + + .or(() -> Optional.of(parentContext) + .filter(BSLParser.AnnotationParamNameContext.class::isInstance) + .map(BSLParser.AnnotationParamNameContext.class::cast) + .flatMap(annotationParamName -> getReferenceToAnnotationParamSymbol(annotationParamName, documentContext)) + ) + + .or(() -> Optional.of(parentContext) + .filter(BSLParser.ConstValueContext.class::isInstance) + .map(BSLParser.ConstValueContext.class::cast) + .flatMap(constValue -> getReferenceToAnnotationParamSymbol(constValue, documentContext)) + ) + + .or(() -> Optional.of(parentContext) + .map(BSLParserRuleContext::getParent) + .filter(BSLParser.ConstValueContext.class::isInstance) + .map(BSLParser.ConstValueContext.class::cast) + .flatMap(constValue -> getReferenceToAnnotationParamSymbol(constValue, documentContext)) + ) + + .or(() -> Optional.of(parentContext) + .map(BSLParserRuleContext::getParent) + .map(BSLParserRuleContext::getParent) + .filter(BSLParser.ConstValueContext.class::isInstance) + .map(BSLParser.ConstValueContext.class::cast) + .flatMap(constValue -> getReferenceToAnnotationParamSymbol(constValue, documentContext)) + ); + + } + + private Optional getReferenceToAnnotationSymbol(URI uri, BSLParser.AnnotationNameContext annotationName, DocumentContext documentContext) { + var annotationNode = (BSLParser.AnnotationContext) annotationName.getParent(); + + return getAnnotationSymbol(annotationName) + .map(annotationSymbol -> Reference.of( + documentContext.getSymbolTree().getModule(), + annotationSymbol, + new Location(uri.toString(), Ranges.create(annotationNode)) + )); + } + + private Optional getReferenceToAnnotationParamSymbol(BSLParser.AnnotationParamNameContext annotationParamName, DocumentContext documentContext) { + return Optional.of(annotationParamName) + .map(BSLParserRuleContext::getParent) // BSLParser.AnnotationParamContext + .map(BSLParser.AnnotationParamContext.class::cast) + .flatMap(annotationParamContext -> getReferenceToAnnotationParam(documentContext, Optional.of(annotationParamContext))); + } + + private Optional getReferenceToAnnotationParamSymbol(BSLParser.ConstValueContext constValue, DocumentContext documentContext) { + return Optional.of(constValue) + .map(BSLParserRuleContext::getParent) // BSLParser.AnnotationParamContext + .filter(BSLParser.AnnotationParamContext.class::isInstance) + .map(BSLParser.AnnotationParamContext.class::cast) + .flatMap(annotationParamContext -> getReferenceToAnnotationParam(documentContext, Optional.of(annotationParamContext))); + } + + private Optional getAnnotationSymbol(BSLParser.AnnotationNameContext annotationNode) { + var annotationName = annotationNode.getText(); + return Optional.ofNullable(registeredAnnotations.get(annotationName)); + } + + private Optional getReferenceToAnnotationParam( + DocumentContext documentContext, + Optional annotationParamContext + ) { + + var annotationParamName = annotationParamContext + .map(BSLParser.AnnotationParamContext::annotationParamName) + .map(BSLParserRuleContext::getText) + .orElse("Значение"); + + BSLParserRuleContext annotationParamLocation = annotationParamContext + .map(BSLParser.AnnotationParamContext::annotationParamName) + .map(BSLParserRuleContext.class::cast) + .or(() -> annotationParamContext.map(BSLParser.AnnotationParamContext::constValue)) + .orElseThrow(); + + return annotationParamContext + .map(BSLParserRuleContext::getParent) // BSLParser.AnnotationParamsContext + .map(BSLParserRuleContext::getParent) // BSLParser.AnnotationContext + .map(BSLParser.AnnotationContext.class::cast) + + .map(BSLParser.AnnotationContext::annotationName) + .flatMap(this::getAnnotationSymbol) + .flatMap(AnnotationSymbol::getParent) + .filter(MethodSymbol.class::isInstance) + .map(MethodSymbol.class::cast) + .map(annotationDefinitionMethodSymbol -> AnnotationParamSymbol.from(annotationParamName, annotationDefinitionMethodSymbol)) + .map(annotationParamSymbol -> Reference.of( + documentContext.getSymbolTree().getModule(), + annotationParamSymbol, + new Location(documentContext.getUri().toString(), Ranges.create(annotationParamLocation)) + )); } private static Optional> findAnnotation(MethodSymbol methodSymbol) { diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Ranges.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Ranges.java index 362e0644713..82be119230e 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Ranges.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Ranges.java @@ -30,6 +30,8 @@ import org.antlr.v4.runtime.tree.TerminalNode; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.util.Preconditions; +import org.eclipse.lsp4j.util.Positions; import java.util.Collection; import java.util.List; @@ -144,7 +146,11 @@ public boolean containsRange(Range bigger, Range smaller) { } public boolean containsPosition(Range range, Position position) { - return org.eclipse.lsp4j.util.Ranges.containsPosition(range, position); + Preconditions.checkNotNull(range, "range"); + Preconditions.checkNotNull(position, "position"); + return range.getStart().equals(position) + || (Positions.isBefore(range.getStart(), position) + && Positions.isBefore(position, range.getEnd())); } /** diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/DefinitionProviderTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/DefinitionProviderTest.java index f891777e927..d7c3dd7da16 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/DefinitionProviderTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/DefinitionProviderTest.java @@ -76,7 +76,7 @@ void testDefinitionOfLocalMethod() { var methodSymbol = documentContext.getSymbolTree().getMethodSymbol("ИмяФункции").orElseThrow(); var params = new DefinitionParams(); - params.setPosition(new Position(4, 10)); + params.setPosition(new Position(4, 9)); // when var definitions = definitionProvider.getDefinition(documentContext, params); diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SelectionRangeProviderTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SelectionRangeProviderTest.java index 5cccd092e93..3c99350ad84 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SelectionRangeProviderTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SelectionRangeProviderTest.java @@ -71,7 +71,7 @@ void testGlobalMethodCallCapturesSemicolon() { @Test void testStatementCapturesStatementBlockAfterStatement() { // given - var params = selection(4, 20); + var params = selection(4, 19); // when var selectionRanges = provider.getSelectionRange(documentContext, params); @@ -89,7 +89,7 @@ void testStatementCapturesStatementBlockAfterStatement() { @Test void testStatementCapturesStatementBlockBeforeStatement() { // given - var params = selection(6, 20); + var params = selection(6, 19); // when var selectionRanges = provider.getSelectionRange(documentContext, params); @@ -107,7 +107,7 @@ void testStatementCapturesStatementBlockBeforeStatement() { @Test void testStatementCapturesStatementBlockAroundStatement() { // given - var params = selection(5, 20); + var params = selection(5, 19); // when var selectionRanges = provider.getSelectionRange(documentContext, params); diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinderTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinderTest.java index 7f618f2d351..ec60725616b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinderTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/AnnotationReferenceFinderTest.java @@ -28,6 +28,8 @@ import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.SymbolKind; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -41,7 +43,7 @@ class AnnotationReferenceFinderTest extends AbstractServerContextAwareTest { private AnnotationReferenceFinder referenceFinder; @Test - void findReference() { + void findReferenceOfAnnotation() { // given initServerContext("./src/test/resources/references/annotations"); var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/references/AnnotationReferenceFinder.os"); @@ -56,9 +58,96 @@ void findReference() { .isPresent() .hasValueSatisfying(reference -> assertThat(reference.getFrom()).isEqualTo(module)) .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getName()).isEqualTo("ТестоваяАннотация")) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getSymbolKind()).isEqualTo(SymbolKind.Interface)) + .hasValueSatisfying(reference -> assertThat(reference.getSelectionRange()).isEqualTo(Ranges.create(0, 0, 76))) + .hasValueSatisfying(reference -> assertThat(reference.getSourceDefinedSymbol().orElseThrow().getSelectionRange()).isEqualTo(Ranges.create(7, 10, 28))) + ; + } + + @Test + void findReferenceOfAnnotationParameterName() { + // given + initServerContext("./src/test/resources/references/annotations"); + var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/references/AnnotationReferenceFinder.os"); + + var module = documentContext.getSymbolTree().getModule(); + + // when + var optionalReference = referenceFinder.findReference(documentContext.getUri(), new Position(0, 25)); + + // then + assertThat(optionalReference) + .isPresent() + .hasValueSatisfying(reference -> assertThat(reference.getFrom()).isEqualTo(module)) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getName()).isEqualTo("ВторойПараметр")) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getSymbolKind()).isEqualTo(SymbolKind.TypeParameter)) + .hasValueSatisfying(reference -> assertThat(reference.getSelectionRange()).isEqualTo(Ranges.create(0, 19, 33))) + .hasValueSatisfying(reference -> assertThat(reference.getSourceDefinedSymbol().orElseThrow().getSelectionRange()).isEqualTo(Ranges.create(7, 10, 28))) + ; + } + + @Test + void findReferenceOfAnnotationParameterValue() { + // given + initServerContext("./src/test/resources/references/annotations"); + var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/references/AnnotationReferenceFinder.os"); + + var module = documentContext.getSymbolTree().getModule(); + + // when + var optionalReference = referenceFinder.findReference(documentContext.getUri(), new Position(0, 60)); + + // then + assertThat(optionalReference) + .isPresent() + .hasValueSatisfying(reference -> assertThat(reference.getFrom()).isEqualTo(module)) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getName()).isEqualTo("Значение")) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getSymbolKind()).isEqualTo(SymbolKind.TypeParameter)) + .hasValueSatisfying(reference -> assertThat(reference.getSelectionRange()).isEqualTo(Ranges.create(0, 56, 75))) + .hasValueSatisfying(reference -> assertThat(reference.getSourceDefinedSymbol().orElseThrow().getSelectionRange()).isEqualTo(Ranges.create(7, 10, 28))) + ; + } + + @ParameterizedTest + @CsvSource(textBlock = """ + 6, 24, 6, 19, 6, 27 + 8, 10, 8, 4, 9, 12 + 9, 10, 8, 4, 9, 12 + 11, 19, 11, 19, 11, 21 + 12, 19, 12, 19, 12, 22 + 13, 19, 13, 19, 13, 25 + 14, 19, 14, 19, 14, 29 + 15, 19, 15, 19, 15, 31 + 16, 19, 16, 19, 16, 23 + 18, 4, 18, 4, 18, 6 + 19, 4, 19, 4, 19, 7 + 20, 4, 20, 4, 20, 12 + 21, 4, 21, 4, 22, 12 + 22, 4, 21, 4, 22, 12 + 23, 4, 23, 4, 23, 10 + 24, 4, 24, 4, 24, 14 + 25, 4, 25, 4, 25, 16 + 26, 4, 26, 4, 26, 8 + """ + ) + void findReferenceOfAnnotationParameterValue_allLiterals(int positionLine, int positionCharacter, int selectionRangeStartLine, int selectionRangeStartCharacter, int selectionRangeEndLine, int selectionRangeEndCharacter) { + // given + initServerContext("./src/test/resources/references/annotations"); + var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/references/AnnotationReferenceFinder.os"); + + var module = documentContext.getSymbolTree().getModule(); + + // when + var optionalReference = referenceFinder.findReference(documentContext.getUri(), new Position(positionLine, positionCharacter)); + + // then + assertThat(optionalReference) + .isPresent() + .hasValueSatisfying(reference -> assertThat(reference.getFrom()).isEqualTo(module)) + .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getName()).isEqualTo("Значение")) .hasValueSatisfying(reference -> assertThat(reference.getSymbol().getSymbolKind()).isEqualTo(SymbolKind.TypeParameter)) - .hasValueSatisfying(reference -> assertThat(reference.getSelectionRange()).isEqualTo(Ranges.create(0, 0, 18))) - .hasValueSatisfying(reference -> assertThat(reference.getSourceDefinedSymbol().orElseThrow().getSelectionRange()).isEqualTo(Ranges.create(1, 10, 28))) + .hasValueSatisfying(reference -> assertThat(reference.getSelectionRange()).isEqualTo(Ranges.create(selectionRangeStartLine, selectionRangeStartCharacter, selectionRangeEndLine, selectionRangeEndCharacter))) + .hasValueSatisfying(reference -> assertThat(reference.getSourceDefinedSymbol().orElseThrow().getSelectionRange()).isEqualTo(Ranges.create(7, 10, 28))) ; } } \ No newline at end of file diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFillerTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFillerTest.java index 9464bad8945..cda194d139b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFillerTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFillerTest.java @@ -203,7 +203,7 @@ void testFindVariablesRangesCallStatement() { var referencedSymbol = referenceIndex.getReference( documentContext.getUri(), - new Position(33, 10) + new Position(33, 9) ); assertThat(referencedSymbol).isPresent(); assertThat(referencedSymbol).get() diff --git a/src/test/resources/references/AnnotationReferenceFinder.os b/src/test/resources/references/AnnotationReferenceFinder.os index a6bb131844e..42d905391dc 100644 --- a/src/test/resources/references/AnnotationReferenceFinder.os +++ b/src/test/resources/references/AnnotationReferenceFinder.os @@ -1,8 +1,32 @@ -&ТестоваяАннотация +&ТестоваяАннотация(ВторойПараметр = "ТестовоеЗначение", "ТестовоеЗначение2") Перем ТестоваяПеременная; &ТестоваяАннотация2 Перем ТестоваяПеременная2; +&ТестоваяАннотация("Строка") +&ТестоваяАннотация( + "Многострочная + |Строка" +) +&ТестоваяАннотация(32) +&ТестоваяАннотация(-32) +&ТестоваяАннотация(Истина) +&ТестоваяАннотация('20200101') +&ТестоваяАннотация(Неопределено) +&ТестоваяАннотация(NULL) +&ТестоваяАннотация( + 42, + -42, + "Строка", + "Многострочная + |Строка", + Истина, + '20200101', + Неопределено, + NULL +) +Перем ТестоваяПеременная3; + Процедура ПриСозданииОбъекта() КонецПроцедуры \ No newline at end of file diff --git "a/src/test/resources/references/annotations/\320\242\320\265\321\201\321\202\320\276\320\262\320\260\321\217\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" "b/src/test/resources/references/annotations/\320\242\320\265\321\201\321\202\320\276\320\262\320\260\321\217\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" index e3b4f507ffa..698dc1aa070 100644 --- "a/src/test/resources/references/annotations/\320\242\320\265\321\201\321\202\320\276\320\262\320\260\321\217\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" +++ "b/src/test/resources/references/annotations/\320\242\320\265\321\201\321\202\320\276\320\262\320\260\321\217\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" @@ -1,3 +1,9 @@ +// Описание +// +// Параметры: +// Значение - Строка - Значение аннотации +// ВторойПараметр - Строка - Второе значение аннотации +// &Аннотация("ТестоваяАннотация") -Процедура ПриСозданииОбъекта() +Процедура ПриСозданииОбъекта(Значение, ВторойПараметр) КонецПроцедуры \ No newline at end of file