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 1a960a9471..c3e2781058 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 @@ -24,27 +24,69 @@ import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; import com.github._1c_syntax.bsl.languageserver.context.FileType; 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.AnnotationSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.SymbolTree; +import com.github._1c_syntax.bsl.languageserver.context.symbol.annotations.Annotation; import com.github._1c_syntax.bsl.languageserver.references.model.Reference; +import com.github._1c_syntax.bsl.languageserver.utils.Methods; 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 lombok.RequiredArgsConstructor; import org.antlr.v4.runtime.tree.TerminalNode; +import org.apache.commons.collections4.map.CaseInsensitiveMap; import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.net.URI; +import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; @Component @RequiredArgsConstructor public class AnnotationReferenceFinder implements ReferenceFinder { private final ServerContext serverContext; + private final Map registeredAnnotations = new CaseInsensitiveMap<>(); + + @EventListener + public void handleContextRefresh(ServerContextPopulatedEvent event) { + registeredAnnotations.clear(); + serverContext.getDocuments() + .values() + .forEach(this::findAndRegisterAnnotation); + } + + @EventListener + public void handleDocumentContextChange(DocumentContextContentChangedEvent event) { + DocumentContext documentContext = event.getSource(); + var uri = documentContext.getUri(); + + registeredAnnotations.values().stream() + .filter(annotationSymbol -> annotationSymbol.getOwner().getUri().equals(uri)) + .forEach(annotationSymbol -> registeredAnnotations.remove(annotationSymbol.getName())); + + findAndRegisterAnnotation(documentContext); + } + + private void findAndRegisterAnnotation(DocumentContext documentContext) { + if (documentContext.getFileType() != FileType.OS) { + return; + } + + var symbolTree = documentContext.getSymbolTree(); + + Methods.getOscriptClassConstructor(symbolTree) + .flatMap(AnnotationReferenceFinder::findAnnotation) + .map(methodSymbolAnnotationPair -> AnnotationSymbol.from(getAnnotationName(methodSymbolAnnotationPair.getRight()), methodSymbolAnnotationPair.getLeft())) + .ifPresent(annotationSymbol -> registeredAnnotations.put(annotationSymbol.getName(), annotationSymbol)); + } @Override public Optional findReference(URI uri, Position position) { @@ -53,27 +95,30 @@ public Optional findReference(URI uri, Position position) { return Optional.empty(); } - var registeredAnnotations = serverContext.getDocuments().values().stream() - .filter(documentContext -> documentContext.getFileType() == FileType.OS) - .map(DocumentContext::getSymbolTree) - .flatMap(symbolTree -> symbolTree.getMethodSymbol("ПриСозданииОбъекта").stream()) - .filter(methodSymbol -> methodSymbol.getAnnotations().stream().anyMatch(annotation -> annotation.getName().equals("Аннотация"))) - .map(methodSymbol -> Pair.of(methodSymbol, methodSymbol.getAnnotations().stream().filter(annotation -> annotation.getName().equals("Аннотация")).findFirst().get())) - .collect(Collectors.toMap(methodSymbolAnnotationPair -> methodSymbolAnnotationPair.getRight().getParameters().get(0).getValue(), Pair::getLeft)); - return Trees.findTerminalNodeContainsPosition(document.getAst(), position) .filter(node -> node.getParent().getRuleContext().getRuleIndex() == BSLParser.RULE_annotationName) .flatMap((TerminalNode annotationNode) -> { var annotationName = annotationNode.getText(); - var foundAnnotationDeclaration = registeredAnnotations.get(annotationName); - if (foundAnnotationDeclaration == null) { + var annotationSymbol = registeredAnnotations.get(annotationName); + if (annotationSymbol == null) { return Optional.empty(); } return Optional.of(Reference.of( document.getSymbolTree().getModule(), - AnnotationSymbol.from(annotationName, foundAnnotationDeclaration), + annotationSymbol, new Location(uri.toString(), Ranges.create(annotationNode.getParent().getParent())) )); }); } + + private static Optional> findAnnotation(MethodSymbol methodSymbol) { + return methodSymbol.getAnnotations().stream() + .filter(annotation -> annotation.getName().equalsIgnoreCase("Аннотация")) + .findFirst() + .map(annotation -> Pair.of(methodSymbol, annotation)); + } + + private static String getAnnotationName(Annotation annotation) { + return annotation.getParameters().get(0).getValue(); + } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Methods.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Methods.java index f223a5aa39..3a55bf8e80 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Methods.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Methods.java @@ -21,6 +21,8 @@ */ package com.github._1c_syntax.bsl.languageserver.utils; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.SymbolTree; import com.github._1c_syntax.bsl.parser.BSLParser; import lombok.experimental.UtilityClass; import org.antlr.v4.runtime.Token; @@ -82,4 +84,9 @@ public static Optional getMethodName(BSLParser.LValueContext lValueContex .flatMap(Methods::getMethodName); } + public static Optional getOscriptClassConstructor(SymbolTree symbolTree) { + return symbolTree.getMethodSymbol("ПриСозданииОбъекта") + .or(() -> symbolTree.getMethodSymbol("OnObjectCreate")); + } + } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/AbstractServerContextAwareTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/AbstractServerContextAwareTest.java new file mode 100644 index 0000000000..b483d6b638 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/context/AbstractServerContextAwareTest.java @@ -0,0 +1,29 @@ +package com.github._1c_syntax.bsl.languageserver.context; + +import com.github._1c_syntax.utils.Absolute; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.annotation.PostConstruct; +import java.nio.file.Path; + +@SpringBootTest +public abstract class AbstractServerContextAwareTest { + @Autowired + protected ServerContext context; + + @PostConstruct + public void init() { + context.clear(); + } + + protected void initServerContext(String path) { + var configurationRoot = Absolute.path(path); + initServerContext(configurationRoot); + } + + protected void initServerContext(Path configurationRoot) { + context.setConfigurationRoot(configurationRoot); + context.populateContext(); + } +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractDiagnosticTest.java index 8d845642a6..68e3cea83d 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractDiagnosticTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractDiagnosticTest.java @@ -22,6 +22,7 @@ package com.github._1c_syntax.bsl.languageserver.diagnostics; import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration; +import com.github._1c_syntax.bsl.languageserver.context.AbstractServerContextAwareTest; import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; import com.github._1c_syntax.bsl.languageserver.context.ServerContext; import com.github._1c_syntax.bsl.languageserver.diagnostics.infrastructure.DiagnosticObjectProvider; @@ -46,13 +47,11 @@ import java.util.List; @SpringBootTest -abstract class AbstractDiagnosticTest { +abstract class AbstractDiagnosticTest extends AbstractServerContextAwareTest { @Autowired private DiagnosticObjectProvider diagnosticObjectProvider; @Autowired - protected ServerContext context; - @Autowired protected LanguageServerConfiguration configuration; private final Class diagnosticClass; @@ -65,20 +64,9 @@ abstract class AbstractDiagnosticTest { @PostConstruct public void init() { diagnosticInstance = diagnosticObjectProvider.get(diagnosticClass); - context.clear(); configuration.reset(); } - protected void initServerContext(String path) { - var configurationRoot = Absolute.path(path); - initServerContext(configurationRoot); - } - - protected void initServerContext(Path configurationRoot) { - context.setConfigurationRoot(configurationRoot); - context.populateContext(); - } - protected List getDiagnostics(DocumentContext documentContext) { return diagnosticInstance.getDiagnostics(documentContext); } 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 e38d1ac9b5..82b9a0af98 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 @@ -21,6 +21,8 @@ */ package com.github._1c_syntax.bsl.languageserver.references; +import com.github._1c_syntax.bsl.languageserver.context.AbstractServerContextAwareTest; +import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterClass; import com.github._1c_syntax.bsl.languageserver.util.TestUtils; import com.github._1c_syntax.bsl.languageserver.utils.Ranges; import org.eclipse.lsp4j.Position; @@ -32,7 +34,8 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest -class AnnotationReferenceFinderTest { +@CleanupContextBeforeClassAndAfterClass +class AnnotationReferenceFinderTest extends AbstractServerContextAwareTest { @Autowired private AnnotationReferenceFinder referenceFinder; @@ -40,7 +43,9 @@ class AnnotationReferenceFinderTest { @Test void findReference() { // given + initServerContext(TestUtils.PATH_TO_METADATA); var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/references/AnnotationReferenceFinder.os"); + var module = documentContext.getSymbolTree().getModule(); var method = documentContext.getSymbolTree().getMethods().get(0);